Files
kite/.planning/phases/12-cloud-resource-foundation/12-01-SUMMARY.md
T

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
cloud
metadata
capability
schema
migration
service
requires provides affects
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
backend/db/models.py — extended with 3 new ORM classes
added patterns
SQLAlchemy 2.0 async ORM models for cloud metadata
Alembic migration 0006 with cloud_items, cloud_item_topics, cloud_folder_states
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
created modified
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
backend/db/models.py
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
duration completed tasks_completed tasks_total files_created files_modified tests_added
~15 minutes 2026-06-18 3 3 5 1 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)