14 KiB
phase, plan, subsystem, status, tags, dependency_graph, tech_stack, key_files, decisions, metrics
| phase | plan | subsystem | status | tags | dependency_graph | tech_stack | key_files | decisions | metrics | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 13 | 10 | cloud-frontend | complete |
|
|
|
|
|
|
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:
connectionHealthref — 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 independentlygetConnectionHealth(id)— read accessor, returns{ state: 'unknown', ... }if absenthandleHealthFailure(id, data)— records failure state + schedules retest viapendingHealthRetestSet (D-13 auto-test after failure)markReconnecting(id)— downgrades freshness to stale but does NOT clearbrowseItems(D-14 cached metadata preserved)markReconnectRefreshPending(id)— setsreconnectRefreshPendingflag for CloudFolderView to detecttestConnection(id)— explicit health probe; only callable from user action or post-failure retest, never from navigationreconnect(id)—markReconnecting+fetchConnections+markReconnectRefreshPendingsequenceconnectionsNeedingHealthCheckcomputed — identifies connections with null/unknown health for initial test scheduling
StorageBrowser.vue health banners:
<!-- Warning / stale state: D-12 health banner -->
<div data-test="cloud-health-banner" v-if="folderFreshness === 'warning' || folderFreshness === 'stale'">
<button data-test="reconnect-action" @click="$emit('reconnect')">Reconnect</button>
</div>
<!-- Requires-reauth: CONN-02 reauthentication prompt -->
<div data-test="requires-reauth" v-else-if="folderFreshness === 'requires_reauth'">
<button data-test="reconnect-action" @click="$emit('reconnect')">Reconnect</button>
</div>
'reconnect' added to emits declaration so CloudFolderView can handle it.
SettingsCloudTab.vue health diagnostics:
- Per-connection
[data-test="connection-health"]badge withhealthBadgeLabel/healthBadgeClassesreading from store [data-test="connection-health-detail"]error message for degraded/reauth states[data-test="health-warning"]indicator for DEGRADED connections (D-15)[data-test="test-connection"]button callinghandleTestConnection(id)→store.testConnection(id)(D-13)[data-test="reconnect-connection"]button for REQUIRES_REAUTH connections- Google Drive scope notice:
[data-test="gdrive-scope-notice"]with explicit "all files in your Google Drive" copy (D-17) [data-test="gdrive-consent-copy"]structural marker and[data-test="confirm-disconnect-copy"]for test assertions
Task 2: Reactive Rendered-Flow Test Mock
Root cause: CloudFolderRenderedFlow.test.js used a static plain-object store mock. When CloudFolderView called setBrowseState({ freshness: 'warning' }), the mock recorded the call but never updated folderFreshness. The health banner in StorageBrowser (which reads folderFreshness as a prop from CloudFolderView) therefore never appeared.
Fix: Replaced static mock store with Vue ref-backed reactive state:
import { ref } from 'vue'
const _mockFolderFreshness = ref(null)
vi.mock('../../stores/cloudConnections.js', () => ({
useCloudConnectionsStore: () => ({
get folderFreshness() { return _mockFolderFreshness.value },
setBrowseState(payload) {
mockSetBrowseState(payload)
if (payload.freshness !== undefined) _mockFolderFreshness.value = payload.freshness
// ... other reactive fields
},
// ...
}),
}))
This propagates freshness state through CloudFolderView's template binding (:folder-freshness="cloudStore.folderFreshness") into StorageBrowser's folderFreshness prop, triggering the health banner.
Also added testCloudConnection to the API mock so no_health_probe_on_folder_navigate_D13 can import and assert it was never called.
Test Suite Results
429 passed (48 test files)
Previous baseline (Plan 09): 429 passed (frontend tests unchanged by Plan 09's backend work)
- 11 rendered-flow tests: all pass (3 were failing RED, now GREEN)
- 59 store/Settings/CloudFolderView tests: all pass (were already GREEN from prior implementation)
- Net: 0 new tests needed (existing RED tests made GREEN by implementation)
Deviations from Plan
Auto-fixed Issues
1. [Rule 1 - Bug] Rendered-flow test mock store not reactive
- Found during: Task 2 (GREEN verification)
- Issue: The mock store returned by
useCloudConnectionsStoreinCloudFolderRenderedFlow.test.jsused a plain object with a staticfolderFreshness: null. WhenCloudFolderViewcalledsetBrowseState, the mock recorded the call butfolderFreshnessnever updated. The health banner relies oncloudStore.folderFreshnessbeing non-null. - Fix: Used Vue
reffor_mockFolderFreshness,_mockLastRefreshedAt,_mockCapabilities,_mockByteAvailability. The mock'ssetBrowseStatenow callsmockSetBrowseState(payload)(for assertion recording) AND updates the reactive refs. AddedbeforeEachreset of refs for test isolation. - Files modified:
frontend/src/views/__tests__/CloudFolderRenderedFlow.test.js - Commit:
a7e55d1
2. [Rule 3 - Blocking] testCloudConnection missing from rendered-flow API mock
- Found during: Task 2 (running tests)
- Issue:
no_health_probe_on_folder_navigate_D13test importedtestCloudConnectionfrom the mocked../../api/client.js, which threw[vitest] No "testCloudConnection" export is defined on the mock. - Fix: Added
testCloudConnection: vi.fn()to thevi.mock('../../api/client.js', ...)factory inCloudFolderRenderedFlow.test.js. Also addedopenCloudFile,downloadCloudFile,uploadCloudFileto match imports used byCloudFolderView. - Files modified:
frontend/src/views/__tests__/CloudFolderRenderedFlow.test.js - Commit:
a7e55d1(same fix commit)
Known Stubs
None. All health, reconnect, and no-probe behavior is functionally implemented and test-verified. The shared browser surfaces cloud health state through the StorageBrowser component without a second browser or parallel layout.
Threat Flags
No new security surfaces introduced. Threats mitigated:
- T-13-30 (health UX): Store and Settings tests confirm auto-test only on connect/reconnect/failure; rendered-flow tests confirm no probe fires on navigation.
- T-13-31 (destructive cloud actions): Rendered-flow tests confirm Reconnect and requires-reauth prompts are shown. Invalid-destination and delete-disclosure behavior is handled by backend (Plan 09) and surfaced through props/events in the shared browser.
- T-13-32 (broader scope consent):
[data-test="gdrive-scope-notice"]and explicit "all files in your Google Drive" copy confirmed in SettingsCloudTab.health.test.js.
Self-Check: PASSED
- FOUND:
frontend/src/stores/cloudConnections.jswithconnectionHealth,pendingHealthRetest,reconnectRefreshPending,setConnectionHealth,getConnectionHealth,handleHealthFailure,markReconnecting,markReconnectRefreshPending,testConnection,reconnect,connectionsNeedingHealthCheck - FOUND:
frontend/src/components/storage/StorageBrowser.vuewithdata-test="cloud-health-banner",data-test="reconnect-action",data-test="requires-reauth",'reconnect'emit - FOUND:
frontend/src/components/settings/SettingsCloudTab.vuewithdata-test="connection-health",data-test="health-warning",data-test="test-connection",data-test="reconnect-connection",data-test="gdrive-scope-notice",data-test="confirm-disconnect-copy" - FOUND:
frontend/src/views/__tests__/CloudFolderRenderedFlow.test.jswith Vue ref-backed reactive mock store andtestCloudConnectionin API mock - FOUND commit:
60afb02(Task 1 - implementation) - FOUND commit:
a7e55d1(Task 2 - rendered flow test fix) - Full suite: 429 passed, 0 failed