6.0 KiB
phase, plan, subsystem, tags, dependency_graph, tech_stack, key_files, decisions, metrics
| phase | plan | subsystem | tags | dependency_graph | tech_stack | key_files | decisions | metrics | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 12 | 01 | cloud-resource-foundation |
|
|
|
|
|
|
Phase 12 Plan 01: Cloud Resource Foundation Summary
One-liner: Provider-neutral CloudResourceAdapter contract, Alembic migration 0006 with durable owner-scoped cloud_items/cloud_folder_states tables, and idempotent reconciliation service with stable UUID identity across provider rename/move.
What Was Built
Task 1: Normalized cloud resource capabilities (0a7273b)
backend/storage/cloud_base.py defines the Phase 12 read-only contract:
- 9 action keys (browse, open, preview, upload, create_folder, rename, move, delete, change_tracking)
- 3 capability states (supported, unsupported, temporarily_unavailable)
- 6 reason codes (provider_unsupported, insufficient_scope, read_only, reauth_required, offline, item_restricted)
- Frozen dataclasses:
CloudCapability,CloudResource,CloudListing - Abstract
CloudResourceAdapterwithlist_folder,get_capabilities,merge_item_capabilities - No mutation methods in Phase 12 interface — Phase 13 boundary enforced
29 unit tests covering vocabulary, validation, merge behavior, and a fake adapter proving no mutation methods exist.
Task 2: Durable owner-scoped metadata schema (718fb2c)
Migration 0006_cloud_resource_foundation.py adds:
cloud_items: UUID PK, user_id + connection_id ownership FKs with CASCADE, unique (connection_id, provider_item_id), no object_key or retained-byte fieldcloud_item_topics: association between CloudItem and Topic without requiring a Document rowcloud_folder_states: per-connection/parent-ref freshness row; parent_ref='' for root enables unique constraintcloud_connections:display_name_overridecolumn for user-defined same-provider disambiguation
ORM models CloudItem, CloudItemTopic, CloudFolderState added to backend/db/models.py.
Task 3: Owner-scoped reconciliation service (718fb2c)
backend/services/cloud_items.py implements:
resolve_owned_connection(session, connection_id, user_id)— raisesConnectionNotFoundfor cross-owner accesslist_cloud_children(session, user_id, connection_id, parent_ref)— composite owner+connection scopeupsert_cloud_item(session, user_id, resource)— preserves CloudItem UUID across rename/movereconcile_cloud_listing(session, ...)— soft-deletes missing items only whenCloudListing.complete=Trueget_or_create_folder_state/update_folder_state— idempotent, controlled error_code/message
17 tests covering rename/move stable identity, complete removal, incomplete-listing retention, owner isolation, and idempotency.
Verification
pytest -q tests/test_cloud_capabilities.py tests/test_cloud_items.py— 46 passedrg "HTTPException" backend/services/cloud_items.py— no matches- No Phase 12 task writes provider file bytes or MinIO objects
Deviations from Plan
None — plan executed exactly as written. Service module (cloud_items.py) was created during Task 2 preparation since tests required the import; it was committed in the Task 2 commit which also covered the Task 3 deliverable.
Threat Flags
None. All T-12-01 through T-12-09 mitigations addressed:
- T-12-01: Composite owner+connection predicates enforced in every service function
- T-12-02: Connection UUID in uniqueness constraint and ownership checks
- T-12-06: Read-only interface verified by fake adapter test
- T-12-07: Deletion gated on
complete=Trueinreconcile_cloud_listing - T-12-09:
provider_sizenever touches quota service — confirmed bytest_model_quota_unchanged_after_item_upsert
Self-Check: PASSED
Files exist:
- backend/storage/cloud_base.py — FOUND
- backend/migrations/versions/0006_cloud_resource_foundation.py — FOUND
- backend/services/cloud_items.py — FOUND
- backend/tests/test_cloud_capabilities.py — FOUND
- backend/tests/test_cloud_items.py — FOUND
Commits: