docs(12-01): complete cloud-resource-foundation plan 01 summary
This commit is contained in:
@@ -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)
|
||||
Reference in New Issue
Block a user