diff --git a/.planning/phases/12-cloud-resource-foundation/12-01-SUMMARY.md b/.planning/phases/12-cloud-resource-foundation/12-01-SUMMARY.md new file mode 100644 index 0000000..9caf35c --- /dev/null +++ b/.planning/phases/12-cloud-resource-foundation/12-01-SUMMARY.md @@ -0,0 +1,121 @@ +--- +phase: "12" +plan: "01" +subsystem: cloud-resource-foundation +tags: [cloud, metadata, capability, schema, migration, service] +dependency_graph: + requires: [] + provides: + - backend/storage/cloud_base.py — CloudCapability, CloudResource, CloudListing, CloudResourceAdapter + - backend/migrations/versions/0006_cloud_resource_foundation.py — cloud_items, cloud_item_topics, cloud_folder_states tables + - backend/db/models.py — CloudItem, CloudItemTopic, CloudFolderState ORM models + - backend/services/cloud_items.py — reconcile_cloud_listing, upsert_cloud_item, resolve_owned_connection + affects: + - backend/db/models.py — extended with 3 new ORM classes +tech_stack: + added: + - SQLAlchemy 2.0 async ORM models for cloud metadata + - Alembic migration 0006 with cloud_items, cloud_item_topics, cloud_folder_states + patterns: + - Frozen dataclass value types for immutable capability/resource contracts + - Composite (connection_id, provider_item_id) uniqueness for stable item identity + - Soft-deletion reconciliation gated on CloudListing.complete=True + - Domain exceptions (ConnectionNotFound, CloudItemNotFound) — no HTTPException in service +key_files: + created: + - backend/storage/cloud_base.py + - backend/migrations/versions/0006_cloud_resource_foundation.py + - backend/services/cloud_items.py + - backend/tests/test_cloud_capabilities.py + - backend/tests/test_cloud_items.py + modified: + - backend/db/models.py +decisions: + - Frozen dataclasses for CloudCapability/CloudResource/CloudListing enforce immutability at Python level + - parent_ref='' (empty string) represents connection root in cloud_folder_states to allow unique constraint + - provider_size never referenced in quota service — D-18 compliance by design + - No object_key field on CloudItem — provider bytes are not mirrored in Phase 12 + - display_name_override column added to cloud_connections for user-customized names without uniqueness changes +metrics: + duration: "~15 minutes" + completed: "2026-06-18" + tasks_completed: 3 + tasks_total: 3 + files_created: 5 + files_modified: 1 + tests_added: 46 +--- + +# 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 `CloudResourceAdapter` with `list_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 field +- `cloud_item_topics`: association between CloudItem and Topic without requiring a Document row +- `cloud_folder_states`: per-connection/parent-ref freshness row; parent_ref='' for root enables unique constraint +- `cloud_connections`: `display_name_override` column 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)` — raises `ConnectionNotFound` for cross-owner access +- `list_cloud_children(session, user_id, connection_id, parent_ref)` — composite owner+connection scope +- `upsert_cloud_item(session, user_id, resource)` — preserves CloudItem UUID across rename/move +- `reconcile_cloud_listing(session, ...)` — soft-deletes missing items only when `CloudListing.complete=True` +- `get_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 passed +- `rg "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=True` in `reconcile_cloud_listing` +- T-12-09: `provider_size` never touches quota service — confirmed by `test_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: +- 0a7273b — feat(12-01): normalized cloud resource capability contract and unit tests +- 718fb2c — feat(12-01): durable owner-scoped cloud metadata schema (migration 0006 + models)