Plans 01–04, CONTEXT, PATTERNS, RESEARCH, UAT, and .gitkeep for
the fix-nextcloud-root-listing-and-sync-visibility sub-phase.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Align the cloud frontend with the normalized API. Use `kind` for classification, `provider_item_id` for provider navigation, stable `id` for DocuVault row identity, and backend freshness as the only source of synchronization truth.
@.planning/phases/12.1-fix-nextcloud-root-listing-and-sync-visibility/12.1-CONTEXT.md
@.planning/phases/12.1-fix-nextcloud-root-listing-and-sync-visibility/12.1-PATTERNS.md
@.planning/phases/12-cloud-resource-foundation/12-03-PLAN.md
@frontend/src/views/CloudFolderView.vue
@frontend/src/stores/cloudConnections.js
@frontend/src/components/storage/StorageBrowser.vue
Task 1: Add red normalized-item and navigation regressions
frontend/src/views/__tests__/CloudFolderView.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, frontend/src/components/cloud/__tests__/CloudBreadcrumbNavigation.test.js
- frontend/src/views/CloudFolderView.vue — current is_dir filtering, item.id navigation, and fabricated freshness
- frontend/src/components/cloud/CloudProviderTreeItem.vue — root folder filtering
- frontend/src/components/cloud/CloudFolderTreeItem.vue — nested filtering and navigation
- frontend/src/components/storage/StorageBrowser.vue — shared row keys/events and freshness props
- frontend/src/components/cloud/__tests__/CloudBreadcrumbNavigation.test.js — explicit lineage and opaque-reference breadcrumb regressions
- backend/api/cloud/schemas.py — canonical response shape
Replace legacy-shaped fixtures with realistic CloudItemOut objects containing distinct `id` and `provider_item_id`, `kind`, nullable metadata, and no `is_dir`. Add tests named `renders_kind_folder_and_file_in_shared_browser`, `folder_click_uses_provider_item_id_not_id`, `tree_expansion_filters_kind_folder`, `nested_tree_uses_provider_item_id`, and `stable_docuvault_id_remains_row_key`. Include duplicate display names with distinct IDs/references so tests cannot pass accidentally by name.
Add opaque-reference fixtures containing `/`, `?`, `#`, `%`, spaces, and Unicode. Assert CloudFolderView only passes props/emits events through StorageBrowser and does not add another grid/tree implementation. Verify root sentinel remains `parent_ref=null` at the API boundary and nested folders pass their opaque provider reference unchanged through named Vue Router navigation and the API client's query serializer—never manual route-string interpolation.
Add breadcrumb tests proving labels and navigation refs come from an explicit session-only lineage of visited `{name, provider_item_id}` folder nodes (or equivalent API-provided ancestors), not `split('/')` or any other parsing of provider references. Breadcrumb clicks must restore the exact stored opaque reference.
- tests fail against current is_dir and item.id behavior
- fixtures omit is_dir and distinguish id from provider_item_id
- root and nested provider navigation are both covered
- StorageBrowser remains the rendered browser component
- no test relies on provider-specific path reconstruction in Vue
- reserved-character provider references survive route/query transport and breadcrumb navigation unchanged
cd frontend && npm test -- --run src/views/__tests__/CloudFolderView.test.js src/components/cloud/__tests__/CloudProviderTreeItem.test.js src/components/cloud/__tests__/CloudFolderTreeItem.test.js src/components/storage/__tests__/StorageBrowser.skeleton.test.js src/components/cloud/__tests__/CloudBreadcrumbNavigation.test.js
Red frontend tests reproduce invisible folders and wrong-reference navigation using the actual API shape.
Task 2: Make backend freshness authoritative in Pinia and the view
frontend/src/stores/cloudConnections.js, frontend/src/views/CloudFolderView.vue, frontend/src/stores/__tests__/cloudConnections.test.js, frontend/src/views/__tests__/CloudFolderView.test.js
- frontend/src/stores/cloudConnections.js — folderFreshness and lastRefreshedAt state API
- frontend/src/views/CloudFolderView.vue — unconditional fresh/browser-clock assignment
- frontend/src/components/ui/BreadcrumbBar.vue — expected refreshing/fresh/warning values and timestamp rendering
- frontend/src/views/__tests__/CloudFolderView.test.js — existing fetch/error behavior
- frontend/src/stores/__tests__/cloudConnections.test.js — store reset/persistence conventions
Add tests `maps_server_refreshing_freshness`, `maps_server_warning_and_last_success`, `http_200_does_not_imply_fresh`, `does_not_use_browser_clock_as_refresh_evidence`, and `cached_items_remain_visible_during_warning`. Map `response.freshness.refresh_state`, `last_refreshed_at`, `error_code`, and controlled `error_message` into Pinia. Keep a request-in-flight UI state only if it cannot overwrite the last authoritative server state; after the response, render the server object verbatim. A successful cached browse response is not proof of provider synchronization.
Remove `new Date().toISOString()` and any unconditional `fresh` assignment from CloudFolderView. On request failure without a server freshness payload, preserve cached items and prior last successful timestamp while showing a controlled client transport warning; never invent a provider refresh time.
- HTTP 200 plus warning freshness renders warning, not fresh
- server last_refreshed_at is preserved exactly and browser Date is not called to create evidence
- error code/message remain controlled display data and do not affect item ownership/navigation
- cached items remain usable during refreshing/warning states
- store reset clears cloud browse state without persisting secrets
cd frontend && npm test -- --run src/stores/__tests__/cloudConnections.test.js src/views/__tests__/CloudFolderView.test.js
The UI reports backend freshness truthfully and preserves cached navigation through warnings.
Task 3: Implement normalized item use across cloud view and trees
frontend/src/views/CloudFolderView.vue, frontend/src/components/cloud/CloudProviderTreeItem.vue, frontend/src/components/cloud/CloudFolderTreeItem.vue, frontend/src/components/storage/StorageBrowser.vue, frontend/src/views/__tests__/CloudFolderView.test.js, frontend/src/components/cloud/__tests__/CloudProviderTreeItem.test.js, frontend/src/components/cloud/__tests__/CloudFolderTreeItem.test.js, frontend/src/components/cloud/__tests__/CloudBreadcrumbNavigation.test.js
- frontend/src/components/ui/TreeItem.vue — shared expand/collapse state contract
- frontend/src/components/storage/StorageBrowser.vue — single browser row/event contract
- frontend/src/views/CloudFolderView.vue — thin view boundary
- frontend/src/components/cloud/CloudProviderTreeItem.vue — root folder source
- frontend/src/components/cloud/CloudFolderTreeItem.vue — nested folder source
Replace all cloud `is_dir` checks with `item.kind === "folder"` and file checks with `item.kind === "file"`. Route and browse nested folders with `provider_item_id`; retain `id` for Vue keys, selection, and DocuVault metadata identity. Do not add an alias field, provider-specific mapper, alternate browser, or duplicate expand/collapse state. Keep CloudProviderTreeItem and CloudFolderTreeItem wrapped by TreeItem.vue.
Use named-route objects for connection routing and keep `parent_ref` in serialized query/API state; do not interpolate it into a route path. Ensure folder references are encoded only by Vue Router/API query serializers, not manually concatenated or decoded. Maintain explicit session-only breadcrumb lineage from root to visited folders; never reconstruct hierarchy by splitting `provider_item_id`, because Drive/OneDrive IDs and DAV references are opaque. Preserve connection UUID routing from Phase 12. Remove dead compatibility logic in the same change.
- no active cloud component references item.is_dir
- every provider browse/navigation call uses provider_item_id for nested parent_ref
- stable id remains the rendering/metadata identity and is never sent to a provider adapter
- CloudFolderView contains no grid/layout duplication
- focused and full cloud frontend suites pass
- opaque IDs containing reserved characters and breadcrumb back-navigation round-trip exactly
cd frontend && npm test -- --run src/views/__tests__/CloudFolderView.test.js src/components/cloud/__tests__/CloudProviderTreeItem.test.js src/components/cloud/__tests__/CloudFolderTreeItem.test.js src/components/storage/__tests__/StorageBrowser.skeleton.test.js src/stores/__tests__/cloudConnections.test.js src/components/cloud/__tests__/CloudBreadcrumbNavigation.test.js && ! rg '\.is_dir' src/views/CloudFolderView.vue src/components/cloud
Cloud folders render and navigate with the canonical API fields while shared component boundaries remain intact.
Task 4: Document, security-review, commit, and push Plan 03
AGENTS.md, README.md, RUNBOOK.md, SECURITY.md, .planning/phases/12.1-fix-nextcloud-root-listing-and-sync-visibility/12.1-03-SUMMARY.md
Update required docs for normalized cloud UI routing, breadcrumb lineage, and server freshness. Bump the backend/frontend patch version once and synchronize package-lock metadata because the plan changes user-visible behavior. Run a dedicated security agent against this plan's diff, resolving or escalating findings. Run focused/full frontend tests, production build, npm audit, Compose validation, and diff checks. Stage only Plan 03 files, exclude `.env` and unrelated work, commit atomically as `fix(12.1): align cloud browser contract`, and push immediately.
docker compose config --quiet && cd frontend && npm test && npm run build && npm audit --audit-level=high && cd .. && git diff --check
Plan 03 has independent documentation, security approval, green gates, commit, and push.
<threat_model>
Threat ID
Threat
Mitigation and evidence
T-12.1-11
DocuVault UUID sent as provider path
Distinct-ID fixtures and provider_item_id navigation assertions
T-12.1-12
UI falsely reports provider synchronization
Server freshness mapping; no browser-clock evidence
T-12.1-13
Provider reference injected into provider-specific URL
API query serialization; adapters own provider translation/validation
T-12.1-14
Secrets persisted in frontend state
Existing memory/session rules; tests store folder reference only
T-12.1-15
Parallel browser/tree logic drifts
StorageBrowser and TreeItem remain canonical shared components
</threat_model>
1. Demonstrate actual API-shaped fixtures fail current is_dir/item.id code.
2. Verify kind-based root and nested classification.
3. Verify provider_item_id navigation while id remains row identity.
4. Verify warning/refreshing/fresh server payloads and exact timestamps.
5. Search for legacy is_dir usage in active cloud components and run the focused frontend suite.
<success_criteria>
Users can see folders/files returned by every normalized provider adapter.
Folder clicks and tree expansion use valid provider references.
Synchronization wording reflects backend state rather than HTTP status or client time.