8.5 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 | 06 | cloud-operations | complete |
|
|
|
|
|
|
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 correctprovider_item_idandname. This was the core reconcile-before-return behavioral test.test_upload_success_marks_folder_freshness_stale— verifies that aCloudFolderStaterow 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 07–11.
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.pywithupsert_cloud_item,update_folder_state,write_audit_logcalls in MUT_KIND_UPLOADED branch - FOUND:
backend/tests/test_cloud_mutations.pywith 3 new reconciliation tests - FOUND:
backend/tests/test_cloud_audit.pywithtest_upload_success_writes_metadata_only_audit_rowpromoted 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