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

8.5 KiB
Raw Blame History

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 06 cloud-operations complete
cloud
upload
reconciliation
audit
cloud-items
tdd
requires provides affects
13-01 (RED mutation contract tests)
13-03 (cloud operations seam + mutable provider implementations)
13-04 (operations.py route layer + typed result vocabulary)
13-05 (keep_both_name + upload mechanics tests)
Upload success routes through upsert_cloud_item for stable row identity
Parent folder state invalidated (warning/upload_mutated) after success
cloud.file_uploaded audit row written in same transaction as reconciliation
3 Task 1 reconciliation tests in test_cloud_mutations.py
1 Task 2 upload audit test promoted from xfail in test_cloud_audit.py
13-07 through 13-11 (rename, move, delete, folder audit and reconciliation plans)
added patterns
upsert_cloud_item called from upload route on MUT_KIND_UPLOADED success
update_folder_state called with warning/upload_mutated on upload success
write_audit_log called with cloud.file_uploaded event on upload success
get_client_ip imported from deps.utils in operations.py
write_audit_log imported from services.audit in operations.py
Reconcile-before-return: upsert + folder invalidation complete before response
Audit-in-transaction: write_audit_log flushes in caller transaction, caller commits
Non-success bypass: conflict/offline/reauth paths never reach reconciliation or audit
upload_mutated folder state: controlled code signaling re-list without a full provider scan
modified
path change
backend/api/cloud/operations.py Upload success path now calls upsert_cloud_item, update_folder_state, write_audit_log before commit
path change
backend/tests/test_cloud_mutations.py Added 3 reconciliation behavioral tests for Task 1 (RED then GREEN)
path change
backend/tests/test_cloud_audit.py Promoted test_upload_success_writes_metadata_only_audit_row from xfail to real test (RED then GREEN)
upload_mutated is the controlled error_code for folder invalidation after upload — avoids apply_listing_and_finalize which needs a full provider listing
CloudResource.id assigned uuid4() at route layer before upsert — cloud_items.upsert_cloud_item uses provider_item_id as the stable identity key, not this id
Audit write uses flush (not commit) per services.audit contract; one commit covers upsert + folder state + audit atomically
Non-success upload paths (conflict, offline, reauth_required) do not call write_audit_log, satisfying T-13-21 (no false success events)
duration completed tasks_completed tasks_planned files_changed files_created tests_added tests_passing
~5 minutes 2026-06-22 2 2 3 0 4 745

Phase 13 Plan 06: Upload Follow-Through Slice Summary

One-liner: Upload success routes through upsert_cloud_item + folder state invalidation + metadata-only cloud.file_uploaded audit row before the response is returned.

Tasks Completed

Task Name Commit Key Files
1 (RED) Add failing upload reconciliation tests 4cd6499 test_cloud_mutations.py
1 (GREEN) Route upload success through centralized reconciliation 7ecbec7 api/cloud/operations.py
2 (RED) Promote upload audit test from xfail 33f0498 test_cloud_audit.py
2 (GREEN) Emit metadata-only audit row on upload success d959e0c api/cloud/operations.py

What Was Built

Task 1: Reconcile-before-return on upload success

RED phase added 3 tests to test_cloud_mutations.py:

  • test_upload_success_upserts_cloud_item_before_returning — verifies that after a 200 upload response, a CloudItem row exists in the DB with the correct provider_item_id and name. This was the core reconcile-before-return behavioral test.
  • test_upload_success_marks_folder_freshness_stale — verifies that a CloudFolderState row exists for the parent folder after success (folder state created/updated to signal re-list needed).
  • test_upload_failed_does_not_mutate_cloud_items — verifies offline/error upload results do not create phantom CloudItem rows.

GREEN phase implemented in backend/api/cloud/operations.py:

Inside the MUT_KIND_UPLOADED success branch of upload_cloud_file:

resource = CloudResource(
    id=uuid.uuid4(),
    provider_item_id=provider_item_id,
    connection_id=connection_id,
    user_id=current_user.id,
    name=resolved_name,
    kind="file",
    parent_ref=resolved_parent_ref,
    content_type=content_type,
    size=resolved_size,
)
await upsert_cloud_item(session, user_id=str(current_user.id), resource=resource)

await update_folder_state(
    session,
    user_id=str(current_user.id),
    connection_id=str(connection_id),
    parent_ref=invalidate_parent,
    refresh_state="warning",
    error_code="upload_mutated",
    error_message="Folder contents changed by upload — re-listing required.",
)

The folder state is set to warning with error_code="upload_mutated" (not apply_listing_and_finalize) because we do not have a full provider listing — we only know one new item arrived. The browse endpoint will detect the non-fresh state and trigger a provider re-list on the next navigation.

Task 2: Metadata-only upload audit row

RED phase promoted test_upload_success_writes_metadata_only_audit_row from xfail to a real test. The test now uses a mock adapter so it reliably triggers the 200 success path and checks for a cloud.file_uploaded audit row.

GREEN phase added audit write in backend/api/cloud/operations.py:

await write_audit_log(
    session,
    event_type="cloud.file_uploaded",
    user_id=current_user.id,
    actor_id=current_user.id,
    resource_id=None,
    ip_address=get_client_ip(request),
    metadata_={
        "connection_id": str(connection_id),
        "provider_item_id": provider_item_id,
        "filename": resolved_name,
        "size_bytes": resolved_size,
        "parent_ref": resolved_parent_ref,
    },
)
await session.commit()

The audit payload is metadata-only: no provider URLs, access tokens, refresh tokens, document content, or raw bytes. The session.commit() is called once after upsert + folder state + audit — all three writes land in a single atomic transaction.

Non-success paths (MUT_KIND_CONFLICT, MUT_KIND_OFFLINE, MUT_KIND_REAUTH) branch before this code is reached, so they cannot produce false cloud.file_uploaded events (T-13-21).

Deviations from Plan

Auto-fixed Issues

None. The plan was executed exactly as written.

Scope note: The plan listed backend/api/cloud/operations.py, backend/services/cloud_operations.py, backend/services/cloud_items.py, backend/tests/test_cloud_mutations.py, and backend/tests/test_cloud_audit.py as files modified. In practice, cloud_operations.py and cloud_items.py were not modified — the existing upsert_cloud_item, update_folder_state, and write_audit_log helpers were sufficient. The route layer in operations.py consumed them directly per the CLAUDE.md shared module map.

Known Stubs

None. Upload reconciliation and audit are fully functional. Remaining xfail tests in test_cloud_audit.py cover rename, move, delete, and folder operations which are deferred to Plans 0711.

Threat Flags

No new security surfaces introduced. T-13-19, T-13-20, and T-13-21 are mitigated:

  • T-13-19 (upload reconciliation bypass): Tests verify reconcile-before-return; non-success paths bypass the upsert/folder-state path.
  • T-13-20 (audit payload secrecy): Audit metadata contains only controlled metadata fields; no provider token, URL, or content.
  • T-13-21 (false success audit events): Conflict, offline, and reauth_required branches never reach the audit write call.

Self-Check: PASSED

  • FOUND: backend/api/cloud/operations.py with upsert_cloud_item, update_folder_state, write_audit_log calls in MUT_KIND_UPLOADED branch
  • FOUND: backend/tests/test_cloud_mutations.py with 3 new reconciliation tests
  • FOUND: backend/tests/test_cloud_audit.py with test_upload_success_writes_metadata_only_audit_row promoted from xfail
  • FOUND commit: 4cd6499 (Task 1 RED)
  • FOUND commit: 7ecbec7 (Task 1 GREEN)
  • FOUND commit: 33f0498 (Task 2 RED)
  • FOUND commit: d959e0c (Task 2 GREEN)
  • Full suite: 745 passed, 17 skipped, 4 deselected, 12 xfailed