Files
kite/.planning/phases/12.1-fix-nextcloud-root-listing-and-sync-visibility/12.1-03-SUMMARY.md
T

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
cloud
frontend
kind
provider_item_id
freshness
breadcrumb
tdd
security
2026-06-22 677 4 11
12.1-02
kind-based item classification in CloudFolderView (folders/files computed)
provider_item_id for all folder navigation and tree expansion
server freshness mapped verbatim — no browser-clock evidence
explicit breadcrumb lineage from visited nodes
normalized test regressions covering kind/provider_item_id/freshness
cloud-browse-ui
cloud-sidebar-tree
breadcrumb-navigation
added patterns
TDD RED/GREEN — 9+7 failing tests written first; implementation makes them all pass
Named route object navigation (never string interpolation for opaque refs)
Server freshness mapped verbatim — freshness.refresh_state|last_refreshed_at
Breadcrumb lineage as explicit visited-node list (never split provider_item_id)
created modified
frontend/src/components/cloud/__tests__/CloudBreadcrumbNavigation.test.js
frontend/src/views/CloudFolderView.vue
frontend/src/components/cloud/CloudProviderTreeItem.vue
frontend/src/components/cloud/CloudFolderTreeItem.vue
frontend/src/views/__tests__/CloudFolderView.test.js
frontend/src/stores/__tests__/cloudConnections.test.js
frontend/src/components/cloud/__tests__/CloudProviderTreeItem.test.js
frontend/src/components/cloud/__tests__/CloudFolderTreeItem.test.js
frontend/src/components/storage/__tests__/StorageBrowser.skeleton.test.js
backend/main.py
frontend/package.json
Named route objects used for all cloud navigation — Vue Router handles encoding of opaque refs
Breadcrumb lineage maintained as explicit {name, providerItemId} array in view state — never reconstructed from provider_item_id
provider_item_id is the canonical navigation reference; id remains row identity for Vue keys and DocuVault metadata
Test stubs use updated() lifecycle hook (not mounted()) to capture props after async load resolves
CLOUD-01
CONN-04
SYNC-01

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 no is_dir; asserts folders prop uses kind=folder only
  • folder_click_uses_provider_item_id_not_id — asserts navigateTo puts provider_item_id in route, not DocuVault UUID
  • stable_docuvault_id_remains_row_key — asserts items retain both id and provider_item_id
  • opaque_reference_round_trip — fixture with /, ?, #, spaces, Unicode in provider_item_id; asserts intact navigation

frontend/src/components/cloud/__tests__/CloudProviderTreeItem.test.js (2 new tests):

  • tree_expansion_filters_kind_folderloadChildren returns kind=folder items only
  • loadChildren 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 use provider_item_id and kind
  • opaque_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 CloudItemOut fixtures
  • folder-navigate event carries provider_item_id intact

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 navigate
  • breadcrumb_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/fresh state mapping
  • does_not_use_browser_clock_as_refresh_evidencerefreshedAt must equal server value exactly
  • cached_items_remain_visible_during_warning — items not cleared on warning state
  • selectConnection clears cloud browse state — no secrets persist

frontend/src/views/__tests__/CloudFolderView.test.js (5 new tests):

  • maps_server_refreshing_freshnesssetBrowseState({ freshness: 'refreshing' }) before API resolves
  • maps_server_warning_and_last_success — HTTP 200 + warning freshness → setBrowseState warning, not fresh
  • http_200_does_not_imply_fresh — warning response produces no fresh setBrowseState call
  • does_not_use_browser_clock_as_refresh_evidencerefreshedAt matches exact server timestamp
  • cached_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) with i.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 intact
  • load(): maps data.freshness.refresh_state → store freshness (no fallback to 'fresh'); maps data.freshness.last_refreshed_atrefreshedAt (never new Date())
  • Session restore: replaced string path with named route object

frontend/src/components/cloud/CloudProviderTreeItem.vue:

  • loadChildren: replaced i.is_dir with i.kind === 'folder'
  • All other logic unchanged (root navigation via /cloud/{id}/root string — acceptable for root sentinel)

frontend/src/components/cloud/CloudFolderTreeItem.vue:

  • :expandable="folder.kind === 'folder'" (was folder.is_dir)
  • Icon template: v-if="folder.kind === 'folder'" (was folder.is_dir)
  • isActive: uses folder.provider_item_id for route comparison (with folder.id fallback for legacy)
  • loadChildren: uses props.folder.provider_item_id ?? props.folder.id as parent_ref
  • navigate(): uses provider_item_id ?? id as 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.5 in backend/main.py and frontend/package.json
  • CLAUDE.md: updated current state; Plan 03 summary
  • AGENTS.md: updated current state
  • README.md: version bump
  • SECURITY.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 vulnerabilities
  • docker 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 the onMounted async load() resolved. Captured folders was [] even after flushPromises().
  • 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-navigate in mounted() when this.folders[0] was still undefined, causing TypeError: 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 with is_dir/item.id behavior before implementation
  • RED commit 9974fca — 3 more tests fail with fresh/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