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

8.0 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 08 cloud-operations complete
cloud
create-folder
rename
collision-retry
stale-guard
reconciliation
tdd
requires provides affects
13-03 (MutableCloudResourceAdapter + mutable provider implementations)
13-04 (operations.py route layer + typed result vocabulary)
13-06 (upload reconcile-before-return pattern)
Bounded collision retry for create-folder (D-05, D-06)
Stale guard with folder state refresh for create-folder and rename (D-07)
Reconcile-before-return for create-folder and rename success (T-13-26)
10 new behavioral tests in test_cloud_mutations.py
13-09 through 13-11 (move, delete, audit plans)
added patterns
Bounded collision retry loop in create_cloud_folder route (D-06)
_CREATE_FOLDER_MAX_RETRIES = 5 constant
upsert_cloud_item called from create_cloud_folder success path
upsert_cloud_item called from rename_cloud_item success path
update_folder_state called with warning/create_folder_mutated on create-folder success
update_folder_state called with warning/rename_mutated on rename success
update_folder_state called with warning/stale_listing on stale create-folder or rename
keep_both_name imported from services.cloud_operations for counter suffix generation
Reconcile-before-return: upsert + folder invalidation before success response
Bounded retry: up to 5 collision suffix attempts before returning conflict
Stale-guard: folder state invalidation on stale result before returning typed stale body
Non-success bypass: conflict/offline/reauth/stale never reach reconciliation upsert
modified
path change
backend/api/cloud/operations.py create_cloud_folder: bounded retry + stale guard + reconcile-before-return; rename_cloud_item: stale guard + reconcile-before-return; new imports
path change
backend/tests/test_cloud_mutations.py Added 10 behavioral tests (RED then GREEN) for Plan 08
Rename collision surfaced to user (they chose the name explicitly) — no auto-retry for rename
Create-folder retry uses keep_both_name counter suffix: 'Projects (1)', 'Projects (2)', etc.
Stale guard for both operations uses update_folder_state with warning/stale_listing error_code
Reconciliation pattern follows Plan 06 upload pattern: upsert + folder state + session.commit in one atomic transaction
rename reconcile resolves parent_ref from existing CloudItem (if present) to invalidate correct folder
duration completed tasks_completed tasks_planned files_changed files_created tests_added tests_passing
~20 minutes 2026-06-22 2 2 2 0 10 755

Phase 13 Plan 08: Create-Folder and Rename Collision, Stale Guard, and Reconciliation Summary

One-liner: Bounded collision retry (D-05/D-06), stale-guard with folder refresh (D-07), and reconcile-before-return reconciliation (T-13-26) for create-folder and rename mutations.

Tasks Completed

Task Name Commit Key Files
1 (RED) Add failing collision retry, stale guard, and provider normalization tests 9ad9946 test_cloud_mutations.py
1/2 (GREEN) Implement bounded retry, stale guard, and reconcile-before-return aaa63c1 api/cloud/operations.py

What Was Built

Task 1: Collision Retry, Stale Guard, Provider Normalization (GREEN)

create_cloud_folder in backend/api/cloud/operations.py:

Added a bounded retry loop (up to _CREATE_FOLDER_MAX_RETRIES = 5 attempts) around the adapter call:

for attempt in range(_CREATE_FOLDER_MAX_RETRIES + 1):
    result = await adapter.create_folder(parent_ref=..., name=candidate_name, ...)
    if kind == MUT_KIND_FOLDER:
        # reconcile and return
    if kind == MUT_KIND_CONFLICT and attempt < _CREATE_FOLDER_MAX_RETRIES:
        candidate_name = keep_both_name(body.name, attempt + 1)  # 'Projects (1)', etc.
        continue
    if kind == MUT_KIND_STALE:
        # update_folder_state with warning/stale_listing and return stale body
    break
  • D-05/D-06: Collision auto-suffixes Projects (1), Projects (2), etc. via keep_both_name; retries up to 5 times before surfacing conflict to client
  • D-07: Stale precondition calls update_folder_state(warning, stale_listing) before returning typed {kind: 'stale'}

rename_cloud_item in backend/api/cloud/operations.py:

  • D-07: Stale result triggers update_folder_state(warning, stale_listing) using the existing CloudItem's parent_ref before returning typed {kind: 'stale'}
  • Rename collision is surfaced directly (user chose the name — no auto-retry)

Task 2: Reconcile Create-Folder and Rename Success (GREEN)

Both create_cloud_folder and rename_cloud_item now follow the reconcile-before-return pattern established by Plan 06 (upload):

create_cloud_folder success path:

resource = CloudResource(provider_item_id=..., kind="folder", ...)
await upsert_cloud_item(session, user_id=..., resource=resource)
await update_folder_state(session, ..., refresh_state="warning", error_code="create_folder_mutated")
await session.commit()

rename_cloud_item success path:

# Resolve existing item for parent_ref, kind, content_type, size
existing_item = await session.execute(select(CloudItem).where(...))
resource = CloudResource(provider_item_id=..., name=resolved_name, ...)
await upsert_cloud_item(session, user_id=..., resource=resource)
await update_folder_state(session, ..., refresh_state="warning", error_code="rename_mutated")
await session.commit()

Key design decisions:

  • upsert_cloud_item uses provider_item_id as stable identity key — existing rows are updated in place, preserving the DocuVault UUID
  • update_folder_state invalidates the parent folder (not the item itself) so the next browse triggers a provider re-list
  • Non-success paths (conflict, offline, reauth, stale) never reach the upsert/folder-state calls — preserving T-13-26 (no phantom metadata mutations on failure)

Deviations from Plan

Auto-fixed Issues

None. The plan was executed exactly as written.

Scope note: The plan listed backend/services/cloud_operations.py and backend/services/cloud_items.py as files modified. In practice, neither was modified — the existing upsert_cloud_item, update_folder_state, and keep_both_name helpers were sufficient. The route layer in operations.py consumed them directly via imports, consistent with the CLAUDE.md shared module map.

Test Results

755 passed, 17 skipped, 4 deselected, 12 xfailed
  • test_cloud_mutations.py: 10 new tests, all pass
  • test_cloud_backends.py: unchanged, all pass
  • test_cloud_provider_contract.py: unchanged, all pass
  • Full backend suite: no regressions

Known Stubs

None. Create-folder and rename reconciliation are fully functional. The reconcile-before-return pattern is complete for create-folder and rename (analogous to Plan 06 for upload).

Threat Flags

No new security surfaces introduced. T-13-24, T-13-25, T-13-26 are mitigated:

  • T-13-24 (collision handling): Bounded retry loop enforces D-06; tests verify retry behavior and collision exhaustion.
  • T-13-25 (stale mutation safety): Stale guards stop mutations, refresh affected folder state, require retry — never forcing a stale create-folder or rename.
  • T-13-26 (identity reconciliation): Successful create-folder and rename route through upsert_cloud_item before success is returned.

Self-Check: PASSED

  • FOUND: backend/api/cloud/operations.py with bounded retry, stale guards, and reconcile-before-return for both create_cloud_folder and rename_cloud_item
  • FOUND: backend/tests/test_cloud_mutations.py with 10 new behavioral tests
  • FOUND: .planning/phases/13-virtual-local-cloud-operations/13-08-SUMMARY.md
  • FOUND commit: 9ad9946 (Task 1 RED)
  • FOUND commit: aaa63c1 (Task 1/2 GREEN)
  • Full suite: 755 passed, 17 skipped, 4 deselected, 12 xfailed