11 KiB
phase, plan, subsystem, status, tags, completed, duration_seconds, task_count, file_count, requires, provides, affects, tech_stack, key_files, decisions, requirements
| phase | plan | subsystem | status | tags | completed | duration_seconds | task_count | file_count | requires | provides | affects | tech_stack | key_files | decisions | requirements | |||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 12.1 | 03 | cloud-storage-frontend | complete |
|
2026-06-22 | 677 | 4 | 11 |
|
|
|
|
|
|
|
Phase 12.1 Plan 03: Align Cloud Browser Contract — Summary
One-liner: Replaced is_dir/item.id/new Date() with kind, provider_item_id, and verbatim server freshness across all active cloud UI components.
What Was Built
Task 1 — TDD RED: Normalized-item and navigation regressions
Added 9 failing tests across 5 test files before touching production code:
frontend/src/views/__tests__/CloudFolderView.test.js (4 new tests):
renders_kind_folder_and_file_in_shared_browser— fixtures with nois_dir; assertsfoldersprop useskind=folderonlyfolder_click_uses_provider_item_id_not_id— assertsnavigateToputsprovider_item_idin route, not DocuVault UUIDstable_docuvault_id_remains_row_key— asserts items retain bothidandprovider_item_idopaque_reference_round_trip— fixture with/,?,#, spaces, Unicode inprovider_item_id; asserts intact navigation
frontend/src/components/cloud/__tests__/CloudProviderTreeItem.test.js (2 new tests):
tree_expansion_filters_kind_folder—loadChildrenreturnskind=folderitems onlyloadChildren passes root sentinel— API called with connection UUID, empty string for root
frontend/src/components/cloud/__tests__/CloudFolderTreeItem.test.js (4 new tests):
nested_tree_uses_provider_item_id— navigate/loadChildren/expandable all useprovider_item_idandkindopaque_provider_reference_round_trip— OneDrive-style opaque ID passed verbatim to loadChildren
frontend/src/components/storage/__tests__/StorageBrowser.skeleton.test.js (3 new tests):
- Cloud folder rendering with normalized
CloudItemOutfixtures folder-navigateevent carriesprovider_item_idintact
frontend/src/components/cloud/__tests__/CloudBreadcrumbNavigation.test.js (new file, 8 tests):
breadcrumb_from_explicit_lineage_not_split— at root breadcrumb is empty; opaque ref round-trips through navigatebreadcrumb_opaque_references_survive_transport— Google Drive, WebDAV path with spaces
Result: 9 tests fail against is_dir/item.id behavior as expected (RED confirmed).
Task 2 — TDD RED: Server freshness regressions
Added 8 more tests before implementation:
frontend/src/stores/__tests__/cloudConnections.test.js (7 new tests):
setBrowseState warning/refreshing/freshstate mappingdoes_not_use_browser_clock_as_refresh_evidence—refreshedAtmust equal server value exactlycached_items_remain_visible_during_warning— items not cleared on warning stateselectConnection clears cloud browse state— no secrets persist
frontend/src/views/__tests__/CloudFolderView.test.js (5 new tests):
maps_server_refreshing_freshness—setBrowseState({ freshness: 'refreshing' })before API resolvesmaps_server_warning_and_last_success— HTTP 200 + warning freshness → setBrowseState warning, not freshhttp_200_does_not_imply_fresh— warning response produces nofreshsetBrowseState calldoes_not_use_browser_clock_as_refresh_evidence—refreshedAtmatches exact server timestampcached_items_remain_visible_during_warning— items passed even during warning
Result: 3 new failures against freshness: 'fresh' and new Date().toISOString() in current code.
Task 3 — GREEN: Implement normalized item use
frontend/src/views/CloudFolderView.vue:
- Replaced
items.value.filter(i => i.is_dir)withi.kind === 'folder'/i.kind === 'file' navigateTo(item): builds explicit breadcrumb lineage; navigates via{ name: 'cloud-folder', params: { folderId: item.provider_item_id } }handleBreadcrumbNavigate(id): trims lineage to clicked node; navigates via named route; opaque refs intactload(): mapsdata.freshness.refresh_state→ store freshness (no fallback to'fresh'); mapsdata.freshness.last_refreshed_at→refreshedAt(nevernew Date())- Session restore: replaced string path with named route object
frontend/src/components/cloud/CloudProviderTreeItem.vue:
loadChildren: replacedi.is_dirwithi.kind === 'folder'- All other logic unchanged (root navigation via
/cloud/{id}/rootstring — acceptable for root sentinel)
frontend/src/components/cloud/CloudFolderTreeItem.vue:
:expandable="folder.kind === 'folder'"(wasfolder.is_dir)- Icon template:
v-if="folder.kind === 'folder'"(wasfolder.is_dir) isActive: usesfolder.provider_item_idfor route comparison (withfolder.idfallback for legacy)loadChildren: usesprops.folder.provider_item_id ?? props.folder.idas parent_refnavigate(): usesprovider_item_id ?? idas route param
Verification search:
rg '\.is_dir' src/views/CloudFolderView.vue src/components/cloud/
(no output — only appears in comments and test files)
All 82 focused tests pass; full 369-test suite passes.
Task 4 — Documentation, security review, version bump
- Version bumped
0.2.4 → 0.2.5inbackend/main.pyandfrontend/package.json CLAUDE.md: updated current state; Plan 03 summaryAGENTS.md: updated current stateREADME.md: version bumpSECURITY.md: T-12.1-11 through T-12.1-15 closed with evidence- Full frontend suite: 369 passed, 0 failures
- Production build: clean, 495ms
npm audit --audit-level=high: 0 vulnerabilitiesdocker compose config --quiet: valid- Pushed to remote
Deviations from Plan
Auto-fixed Issues
1. [Rule 1 - Bug] Test stub captured props before async load completed
- Found during: Task 3 (GREEN phase — tests still failing after implementation)
- Issue:
CapturingStub.mounted()fired synchronously before theonMountedasyncload()resolved. Capturedfolderswas[]even afterflushPromises(). - Fix: Added
updated()lifecycle hook to capturing stubs so they re-capture after props update. - Files modified:
frontend/src/views/__tests__/CloudFolderView.test.js - Scope: Tests only.
2. [Rule 1 - Bug] folder-navigate emitted in mounted() before items loaded
- Found during: Task 3 (opaque_reference_round_trip and folder_click tests failing)
- Issue: Both test stubs emitted
folder-navigateinmounted()whenthis.folders[0]was still undefined, causingTypeError: Cannot read properties of undefined (reading 'name'). - Fix: Moved emit to
updated()with a one-shot flag so it fires only after folders prop is populated. - Files modified:
frontend/src/views/__tests__/CloudFolderView.test.js
Test Evidence
| Suite | Tests | Status |
|---|---|---|
CloudFolderView.test.js |
23 | All pass (4 pre-existing + 9 new + 5 freshness + 5 more freshness) |
CloudProviderTreeItem.test.js |
8 | All pass (4 pre-existing + 4 new) |
CloudFolderTreeItem.test.js |
16 | All pass (4 pre-existing + 12 new) |
StorageBrowser.skeleton.test.js |
22 | All pass (19 pre-existing + 3 new) |
cloudConnections.test.js |
23 | All pass (16 pre-existing + 7 new) |
CloudBreadcrumbNavigation.test.js |
8 | All new, all pass |
| Full frontend suite | 369 | All pass (44 test files) |
TDD Gate Compliance:
- RED commit
ba7f652— 9 tests fail withis_dir/item.idbehavior before implementation - RED commit
9974fca— 3 more tests fail withfresh/new Date()before implementation - GREEN commit
dc3e172— all 82 focused tests pass; 369 total pass
Security Verification
Threat model coverage:
| Threat ID | Mitigation | Evidence |
|---|---|---|
| T-12.1-11 | DocuVault UUID not sent as provider path | navigateTo uses item.provider_item_id; loadChildren uses folder.provider_item_id; folder_click_uses_provider_item_id_not_id, nested_tree_uses_provider_item_id |
| T-12.1-12 | UI falsely reports provider synchronization | load() maps data.freshness.refresh_state verbatim; http_200_does_not_imply_fresh, does_not_use_browser_clock_as_refresh_evidence |
| T-12.1-13 | Provider reference injected into provider URL | Named route objects; breadcrumb_opaque_references_survive_transport |
| T-12.1-14 | Secrets persisted in frontend state | Only folderId string stored in sessionStorage; sessionStorage_stores_only_folder_references_not_tokens |
| T-12.1-15 | Parallel browser/tree logic drifts | renders_StorageBrowser_no_parallel_file_grid asserts no table/grid in view; single StorageBrowser pattern maintained |
Known Stubs
None — all classification, navigation, and freshness paths are fully wired to real API responses.
Threat Flags
None — this plan is frontend-only with no new network endpoints, auth paths, file access patterns, or schema changes.
Self-Check: PASSED
| Check | Result |
|---|---|
frontend/src/views/CloudFolderView.vue (kind classification) |
FOUND |
frontend/src/components/cloud/CloudFolderTreeItem.vue (provider_item_id) |
FOUND |
frontend/src/components/cloud/CloudProviderTreeItem.vue (kind filter) |
FOUND |
frontend/src/components/cloud/__tests__/CloudBreadcrumbNavigation.test.js |
FOUND |
Commit ba7f652 (RED tests task 1) |
FOUND |
Commit 9974fca (RED tests task 2) |
FOUND |
Commit dc3e172 (GREEN implementation) |
FOUND |
Commit 7bb046a (docs + version) |
FOUND |
| No is_dir in active cloud components | CONFIRMED |
| Full test suite: 369 passed | CONFIRMED |