--- phase: "13" plan: "10" subsystem: cloud-frontend status: complete tags: - cloud - health - reconnect - no-probe - google-drive-consent - shared-browser - rendered-flow - tdd dependency_graph: requires: - "13-02 (store/browse state, capabilities, freshness)" - "13-04 (operations route layer)" - "13-07 (cloud queue, preview, shared browser)" - "13-09 (move/delete mutations, reconciliation, audit)" provides: - "D-12: connectionHealth map as single source for browser and Settings (T-13-30)" - "D-13: testConnection explicit action; no-probe-on-navigation invariant enforced" - "D-14: markReconnecting preserves cached browseItems; markReconnectRefreshPending flag" - "D-15: degraded vs requires_reauth health states distinguished in store" - "D-17: Google Drive broader scope notice in SettingsCloudTab (T-13-32)" - "StorageBrowser cloud-health-banner + requires-reauth prompt with Reconnect action" - "Reactive mock store in rendered-flow tests — health banner and no-probe assertions" - "70 tests passing across health, store, view, and rendered-flow suites" affects: - "13-11 (final plan in phase)" tech_stack: added: - "connectionHealth ref map in cloudConnections.js store (D-12)" - "pendingHealthRetest Set ref (D-13)" - "reconnectRefreshPending boolean ref (D-14)" - "VALID_HEALTH_STATES const for server-to-UI vocabulary translation" - "setConnectionHealth() — writes to connectionHealth map, validates state" - "getConnectionHealth() — read accessor for browser and Settings consumers" - "handleHealthFailure() — records failure + schedules retest (D-13)" - "markReconnecting() — preserves browseItems, downgrades freshness to stale (D-14)" - "markReconnectRefreshPending() — signals CloudFolderView to refresh after reconnect (D-14)" - "testConnection() — explicit health probe (never called by navigation)" - "reconnect() — marks reconnecting state then fetches updated connections" - "connectionsNeedingHealthCheck computed — initial test candidates (D-12)" - "StorageBrowser cloud-health-banner (warning/stale) with data-test='reconnect-action'" - "StorageBrowser requires-reauth prompt with data-test='requires-reauth'" - "'reconnect' event emitted from StorageBrowser (D-12)" - "Google Drive scope notice in SettingsCloudTab (D-17, T-13-32)" - "Per-connection health badge and error detail in SettingsCloudTab (D-12)" - "handleTestConnection() in SettingsCloudTab — explicit Test action (D-13)" - "Reactive mock store with Vue refs in CloudFolderRenderedFlow.test.js" patterns: - "Single-source health state: store map → browser compact status AND Settings diagnostics" - "No-probe-on-navigation: testCloudConnection never called during browse/navigate" - "Cached metadata visible during warning/stale: browseItems preserved on reconnect" - "Reactive mock store: ref-backed folderFreshness propagates through CloudFolderView to StorageBrowser" key_files: modified: - path: "frontend/src/stores/cloudConnections.js" change: "Added connectionHealth map, health helpers (setConnectionHealth, getConnectionHealth, handleHealthFailure, markReconnecting, markReconnectRefreshPending, testConnection, reconnect), pendingHealthRetest Set, reconnectRefreshPending flag, VALID_HEALTH_STATES const" - path: "frontend/src/components/settings/SettingsCloudTab.vue" change: "Per-connection health badge (healthBadgeClasses/Label), error detail, health-warning indicator, Test/Reconnect/Disconnect controls with correct IDs, Google Drive scope notice (D-17)" - path: "frontend/src/components/storage/StorageBrowser.vue" change: "cloud-health-banner (warning/stale freshness) with Reconnect button; requires-reauth prompt; 'reconnect' added to emits" - path: "frontend/src/views/CloudFolderView.vue" change: "Thin data-provider: passes folderFreshness/lastRefreshedAt/byteAvailability/capabilities from store to StorageBrowser; upload queue; no health probe on navigation (D-13)" - path: "frontend/src/stores/__tests__/cloudConnections.test.js" change: "59 tests covering D-12 health mapping, D-13 no-probe/retest, D-14 reconnect metadata preservation, auto-test-after-connect" - path: "frontend/src/views/__tests__/CloudFolderRenderedFlow.test.js" change: "Reactive mock store (Vue refs + live setBrowseState) so folderFreshness propagates to StorageBrowser; added testCloudConnection to API mock; reset refs in beforeEach; 11 tests all passing" decisions: - "Reactive mock store pattern: vi.mock factory returns object with Vue ref-backed getters and live setBrowseState — ensures health banner appears in rendered-flow tests without stubbing StorageBrowser" - "Store-level health map is the canonical translation layer — neither browser nor Settings interpret server states independently" - "testConnection is an explicit store action, never a navigation side effect (D-13 invariant)" - "markReconnecting does NOT call selectConnection (which clears browseItems) — preserves cached metadata as stale (D-14)" metrics: duration: "~25 minutes" completed: "2026-06-22" tasks_completed: 2 tasks_planned: 2 files_changed: 6 files_created: 0 tests_added: 11 tests_passing: 429 --- # Phase 13 Plan 10: Frontend Health UX, Reconnect, and Shared Browser Mutation Surface Summary **One-liner:** Store-backed single-source health map (D-12), no-probe-on-navigation invariant (D-13), cached-metadata reconnect (D-14), Google Drive consent copy (D-17), and reactive rendered-flow test mock that confirms the health banner and Reconnect action surface through the shared StorageBrowser. ## Tasks Completed | Task | Name | Commit | Key Files | |------|------|--------|-----------| | 1 (GREEN) | Store-backed health, reconnect UX, no-probe, Google Drive consent | 60afb02 | cloudConnections.js, SettingsCloudTab.vue, StorageBrowser.vue, CloudFolderView.vue, cloudConnections.test.js | | 2 (GREEN) | Reactive rendered-flow test mock — health banner and no-probe assertions | a7e55d1 | CloudFolderRenderedFlow.test.js | ## What Was Built ### Task 1: Store-Backed Health and Reconnect UX **`cloudConnections.js` store extensions:** - `connectionHealth` ref — centralized map of `{ state, error_code, error_message, checked_at }` keyed by connection UUID (D-12) - `VALID_HEALTH_STATES` — translates server health vocabulary to UI-actionable states (`healthy`, `requires_reauth`, `degraded`, `unknown`) - `setConnectionHealth(id, data)` — writes to map with validation; consumers (browser and Settings) never translate independently - `getConnectionHealth(id)` — read accessor, returns `{ state: 'unknown', ... }` if absent - `handleHealthFailure(id, data)` — records failure state + schedules retest via `pendingHealthRetest` Set (D-13 auto-test after failure) - `markReconnecting(id)` — downgrades freshness to stale but does NOT clear `browseItems` (D-14 cached metadata preserved) - `markReconnectRefreshPending(id)` — sets `reconnectRefreshPending` flag for CloudFolderView to detect - `testConnection(id)` — explicit health probe; only callable from user action or post-failure retest, never from navigation - `reconnect(id)` — `markReconnecting` + `fetchConnections` + `markReconnectRefreshPending` sequence - `connectionsNeedingHealthCheck` computed — identifies connections with null/unknown health for initial test scheduling **`StorageBrowser.vue` health banners:** ```html