docs(12-01): complete cloud-resource-foundation plan 01 summary

This commit is contained in:
curo1305
2026-06-18 22:38:46 +02:00
parent 718fb2c2b5
commit 3127853c1e
@@ -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)