Files
kite/.planning/phases/13-virtual-local-cloud-operations/13-05-SUMMARY.md
T

7.9 KiB

phase, plan, subsystem, status, tags, dependency_graph, tech_stack, key_files, decisions, metrics
phase plan subsystem status tags dependency_graph tech_stack key_files decisions metrics
13 05 cloud-operations complete
cloud
upload
conflict
keep-both
typed-results
tdd
requires provides affects
13-01 (RED mutable adapter contract tests)
13-03 (cloud operations seam + mutable provider implementations)
13-04 (operations.py route layer + typed result vocabulary)
keep_both_name() helper in services/cloud_operations.py for D-03/D-05 collision naming
TestUploadConflictSemantics: 16 provider-level upload behavioral tests
TestKeepBothNaming: 7 keep-both naming format tests
7 typed upload route mechanics tests in test_cloud_mutations.py
All 4 providers verified to return typed upload results (never raw provider errors)
13-06 through 13-11 (reconcile, audit, frontend upload queue plans)
added patterns
keep_both_name(filename, counter) in services/cloud_operations.py
Counter-before-extension naming: Report.pdf → Report (1).pdf
Typed upload result vocabulary: MUT_KIND_UPLOADED / MUT_KIND_CONFLICT / MUT_KIND_OFFLINE / MUT_KIND_REAUTH
Provider-neutral upload mechanics: all 4 providers normalize errors into shared kinds
Queue-control semantics from backend-authored typed bodies (not Vue-side HTTP status inference)
modified
path change
backend/services/cloud_operations.py Added keep_both_name(filename, counter) helper for D-03/D-05 collision naming
path change
backend/tests/test_cloud_backends.py Added TestUploadConflictSemantics (16 tests) and TestKeepBothNaming (7 tests)
path change
backend/tests/test_cloud_mutations.py Added 7 typed upload route mechanics tests (Task 2 behaviors)
keep_both_name lives in services/cloud_operations (not a provider) — naming is queue/service concern, not provider concern
Counter inserted before first dot to preserve compound extensions (archive.tar.gz → archive (1).tar.gz)
Providers already had upload_file implementations from Plan 03 — Plan 05 adds behavioral tests and the naming utility only
Upload route already had typed result handling from Plan 04 — Plan 05 confirms test coverage of all outcome paths
Reconcile and audit follow-through deferred to Plans 06+ per PLAN.md bounded scope
duration completed tasks_completed tasks_planned files_changed files_created tests_added tests_passing
~45 minutes 2026-06-22 2 2 3 0 30 741

Phase 13 Plan 05: Upload Mechanics Slice Summary

One-liner: Provider-normalized upload conflict/retry/reauth typed mechanics with keep_both_name() helper for D-03/D-05 queue-level collision resolution.

Tasks Completed

Task Name Commit Key Files
1 (RED) Add failing upload conflict and keep-both naming tests f47e36d test_cloud_backends.py
1 (GREEN) Implement keep_both_name() helper e1ce4cb services/cloud_operations.py
2 Add typed upload route mechanics tests 3ddf4da test_cloud_mutations.py

What Was Built

Task 1: Provider-normalized upload contract tests + keep_both_name() helper

RED phase: Added 23 tests (16 provider behavioral + 7 keep-both naming) to test_cloud_backends.py:

TestUploadConflictSemantics:

  • test_google_drive_upload_success_returns_typed_result — kind='uploaded' with provider_item_id, name, size; no credentials in result
  • test_google_drive_upload_transient_error_returns_offline_kind — HTTP 503 → kind='offline' (retryable)
  • test_google_drive_upload_auth_error_returns_reauth_kind — HTTP 401 → kind='reauth_required'
  • test_onedrive_upload_success_returns_typed_result — resumable session success → kind='uploaded'
  • test_onedrive_upload_conflict_returns_typed_conflict_kind — conflictBehavior=fail 409 → kind='conflict', reason='name_collision'
  • test_onedrive_upload_never_writes_cloud_items — source scan confirms no cloud_items import
  • test_webdav_upload_success_returns_typed_result — PUT success → kind='uploaded' with correct path
  • test_webdav_upload_provider_error_returns_error_kind — connection timeout → kind='offline'
  • test_upload_result_never_contains_credentials — parametrized over 4 providers; no access_token/refresh_token in result
  • test_upload_result_kind_in_known_kinds — parametrized over 4 providers; kind must be in MUT_KINDS constant set

TestKeepBothNaming:

  • test_keep_both_naming_module_exists — services.cloud_operations exposes keep_both_name
  • test_keep_both_basic_pdf — Report.pdf + 1 → Report (1).pdf
  • test_keep_both_counter_increments — Report.pdf + 2 → Report (2).pdf
  • test_keep_both_no_extension — README + 1 → README (1)
  • test_keep_both_compound_extension — archive.tar.gz + 1 → archive (1).tar.gz
  • test_keep_both_preserves_existing_counter — Report (1).pdf + 2 → Report (1) (2).pdf
  • test_keep_both_spaces_in_name — My Document.docx + 1 → My Document (1).docx

GREEN phase: Implemented keep_both_name(filename, counter) in services/cloud_operations.py:

def keep_both_name(filename: str, counter: int) -> str:
    """Insert collision counter before first file extension.
    
    Report.pdf + 1  →  Report (1).pdf
    archive.tar.gz + 1  →  archive (1).tar.gz
    README + 1  →  README (1)
    """

The counter is inserted before the first dot (not last), so compound extensions are preserved intact.

Task 2: Typed upload route mechanics tests

Added 7 tests to test_cloud_mutations.py verifying Task 2 behavioral contract:

  1. test_upload_success_returns_typed_uploaded_body — success body has kind/provider_item_id/name/size
  2. test_upload_provider_retryable_error_returns_offline_kind — offline kind from provider error, no bare 500
  3. test_upload_provider_conflict_returns_conflict_kind — provider-detected conflict when cache misses
  4. test_upload_reauth_result_is_typed_and_not_500 — CONN-02 token expiry surfaces as typed body
  5. test_upload_queue_control_semantics_are_backend_authored — all 4 required queue fields present
  6. test_upload_keep_both_name_format — service utility accessible to route layer
  7. test_upload_foreign_user_blocked — IDOR protection on upload endpoint

All 7 tests pass because the upload route in operations.py (Plan 04) already implements the typed result dispatch correctly. Plan 05 adds the test coverage that was missing.

Deviations from Plan

Auto-fixed Issues

None. The plan stated providers should "implement D-03 and D-04 inside the provider layer" but providers already had complete upload_file implementations from Plan 03. Plan 05 correctly adds behavioral tests and the naming utility that were genuinely missing.

Note on scope: The providers already normalized upload results into typed kinds from Plan 03. Plan 05's contribution is:

  1. Behavioral tests that prove the normalization is correct
  2. The keep_both_name() service helper that the upload queue needs for conflict resolution

Known Stubs

None. The upload mechanics are fully functional. Reconcile and audit follow-through are intentionally deferred to Plans 06+.

Threat Flags

No new security surfaces introduced. Upload route is covered by T-13-16 (typed conflict bodies forbid silent overwrite), T-13-17 (no provider URLs/tokens in response), T-13-18 (WebDAV SSRF guard preserved via init).

Self-Check: PASSED

  • FOUND: backend/services/cloud_operations.py with keep_both_name() function
  • FOUND: backend/tests/test_cloud_backends.py (modified — 23 new tests)
  • FOUND: backend/tests/test_cloud_mutations.py (modified — 7 new tests)
  • FOUND commit: f47e36d (Task 1 RED)
  • FOUND commit: e1ce4cb (Task 1 GREEN — keep_both_name)
  • FOUND commit: 3ddf4da (Task 2 — route mechanics tests)
  • Full suite: 741 passed, 17 skipped, 4 deselected, 13 xfailed