190 lines
14 KiB
Markdown
190 lines
14 KiB
Markdown
---
|
|
phase: "12"
|
|
plan: "03"
|
|
type: execute
|
|
wave: 3
|
|
depends_on:
|
|
- "12-02"
|
|
files_modified:
|
|
- frontend/src/api/cloud.js
|
|
- frontend/src/stores/cloudConnections.js
|
|
- frontend/src/router/index.js
|
|
- frontend/src/views/CloudStorageView.vue
|
|
- frontend/src/views/CloudFolderView.vue
|
|
- frontend/src/components/settings/SettingsCloudTab.vue
|
|
- frontend/src/components/storage/StorageBrowser.vue
|
|
- frontend/src/components/ui/BreadcrumbBar.vue
|
|
- frontend/src/components/ui/AppIcon.vue
|
|
- frontend/src/utils/formatters.js
|
|
- frontend/src/stores/__tests__/cloudConnections.test.js
|
|
- frontend/src/views/__tests__/CloudStorageView.test.js
|
|
- frontend/src/views/__tests__/CloudFolderView.test.js
|
|
- frontend/src/components/settings/__tests__/SettingsCloudTab.test.js
|
|
- frontend/src/components/storage/__tests__/StorageBrowser.capabilities.test.js
|
|
- frontend/src/components/ui/__tests__/BreadcrumbBar.test.js
|
|
- backend/main.py
|
|
- frontend/package.json
|
|
- frontend/package-lock.json
|
|
- AGENTS.md
|
|
- README.md
|
|
autonomous: true
|
|
requirements:
|
|
- CONN-04
|
|
- CLOUD-01
|
|
- CLOUD-08
|
|
- CACHE-01
|
|
- CACHE-02
|
|
- SYNC-01
|
|
|
|
must_haves:
|
|
truths:
|
|
- "D-01/D-03: Cloud Storage shows every connection as a customizable, disambiguated top-level root"
|
|
- "D-04/D-05: breadcrumbs begin with connection name and folder memory lasts only for the browser session"
|
|
- "D-06/D-07: unsupported actions remain in stable positions and explain themselves on hover, focus, and touch tap"
|
|
- "D-09/D-10: permanent limitations are gray; temporary/remediable blocks are amber with exact guidance"
|
|
- "D-12/D-14/D-16: folder refresh state is subtle, cached rows remain usable, and warning copy includes last refresh/retry"
|
|
- "D-15/D-17: cloud rows match local rows, repeat source only contextually, and show a compact marker only for cached bytes"
|
|
artifacts:
|
|
- path: "frontend/src/components/storage/StorageBrowser.vue"
|
|
provides: "Single capability-aware local/cloud row and action renderer"
|
|
contains: "aria-disabled"
|
|
- path: "frontend/src/components/ui/BreadcrumbBar.vue"
|
|
provides: "Connection-root breadcrumb and refreshing/fresh/warning sync indicator"
|
|
contains: "folderFreshness"
|
|
- path: "frontend/src/views/CloudFolderView.vue"
|
|
provides: "Thin connection-ID route data provider"
|
|
contains: "connectionId"
|
|
- path: "frontend/src/components/storage/__tests__/StorageBrowser.capabilities.test.js"
|
|
provides: "Pointer, keyboard, touch, action suppression, and local regression coverage"
|
|
contains: "temporarily_unavailable"
|
|
key_links:
|
|
- from: "frontend/src/router/index.js"
|
|
to: "frontend/src/views/CloudFolderView.vue"
|
|
via: "/cloud/:connectionId/:folderId route"
|
|
pattern: ":connectionId"
|
|
- from: "frontend/src/views/CloudFolderView.vue"
|
|
to: "frontend/src/components/storage/StorageBrowser.vue"
|
|
via: "capabilities, connectionRoot, folderFreshness, lastRefreshedAt, byteAvailability props"
|
|
pattern: ":capabilities"
|
|
---
|
|
|
|
<objective>
|
|
Make connected accounts and durable cloud metadata feel like the local file manager by extending the one shared `StorageBrowser` and breadcrumb system. Render provider capabilities and freshness without duplicating a cloud grid or implementing Phase 13 actions.
|
|
</objective>
|
|
|
|
<execution_context>
|
|
@$HOME/.claude/get-shit-done/workflows/execute-plan.md
|
|
@$HOME/.claude/get-shit-done/templates/summary.md
|
|
</execution_context>
|
|
|
|
<context>
|
|
@.planning/phases/12-cloud-resource-foundation/12-02-SUMMARY.md
|
|
@.planning/phases/12-cloud-resource-foundation/12-CONTEXT.md
|
|
@.planning/phases/12-cloud-resource-foundation/12-UI-SPEC.md
|
|
@.planning/phases/12-cloud-resource-foundation/12-PATTERNS.md
|
|
@.planning/phases/12-cloud-resource-foundation/12-VALIDATION.md
|
|
</context>
|
|
|
|
<tasks>
|
|
|
|
<task type="auto">
|
|
<name>Task 1: Route and store cloud navigation by connection identity</name>
|
|
<files>frontend/src/api/cloud.js, frontend/src/stores/cloudConnections.js, frontend/src/router/index.js, frontend/src/views/CloudStorageView.vue, frontend/src/views/CloudFolderView.vue, frontend/src/components/settings/SettingsCloudTab.vue, frontend/src/stores/__tests__/cloudConnections.test.js, frontend/src/views/__tests__/CloudStorageView.test.js, frontend/src/views/__tests__/CloudFolderView.test.js, frontend/src/components/settings/__tests__/SettingsCloudTab.test.js</files>
|
|
<read_first>
|
|
- frontend/src/api/cloud.js — current provider-keyed folder URL
|
|
- frontend/src/stores/cloudConnections.js — existing Pinia fetch/disconnect pattern
|
|
- frontend/src/router/index.js — current `/cloud/:provider/:folderId` route
|
|
- frontend/src/views/CloudStorageView.vue — connection list currently bypasses StorageBrowser
|
|
- frontend/src/views/CloudFolderView.vue — thin-view boundary and provider-keyed browse
|
|
- frontend/src/components/settings/SettingsCloudTab.vue — existing connection maintenance UI where display-name editing belongs
|
|
- .planning/phases/12-cloud-resource-foundation/12-UI-SPEC.md — root list and session navigation contracts
|
|
</read_first>
|
|
<action>
|
|
Change the canonical cloud route to `/cloud/:connectionId/:folderId(.*)` and call the connection-ID browse endpoint through `api/cloud.js`. Add `renameCloudConnection(id, displayName)` and a Pinia `rename` action that updates only the returned connection after the API succeeds. Add accessible display-name editing to the existing SettingsCloudTab connection maintenance surface; default names remain plain provider labels and duplicate defaults show a four-character connection UUID suffix in the cloud root until customized. Extend the Pinia store with selected connection, browse result, capabilities, freshness, last refresh, and safe error state while keeping API calls out of smart components. Render all connection roots through `StorageBrowser` data rather than a parallel row grid. `CloudFolderView` passes normalized props/events only. Save the last folder in `sessionStorage` under a namespaced key containing connection UUID; entering from the root may resume during the same session, while absent session state starts at root. Do not store JWTs, credentials, cloud item content, or persistent navigation in browser storage. Add tests for rename validation/success/failure, two same-provider roots, connection UUID URLs/API calls, root reset/fresh session, same-session resume, and thin-view behavior.
|
|
</action>
|
|
<acceptance_criteria>
|
|
- no frontend browse request or route selects a connection by provider slug alone
|
|
- duplicate provider connections render as separate roots, default-name suffixes are deterministic, and Settings can customize each name independently
|
|
- only folder navigation state is stored in sessionStorage; no token or credential data is stored
|
|
- CloudStorageView and CloudFolderView contain no file-grid markup
|
|
- focused store/view tests pass
|
|
</acceptance_criteria>
|
|
<verify><automated>cd frontend && npm test -- --run cloudConnections CloudStorageView CloudFolderView SettingsCloudTab</automated></verify>
|
|
<done>Connection roots, custom names, connection-ID routing, and session-only folder memory pass store/view/settings tests.</done>
|
|
</task>
|
|
|
|
<task type="auto">
|
|
<name>Task 2: Replace mode-gated actions with accessible capability rendering</name>
|
|
<files>frontend/src/components/storage/StorageBrowser.vue, frontend/src/components/ui/AppIcon.vue, frontend/src/components/storage/__tests__/StorageBrowser.capabilities.test.js</files>
|
|
<read_first>
|
|
- frontend/src/components/storage/StorageBrowser.vue — all current `mode === 'local'` gates, emits, and touch targets
|
|
- frontend/src/components/ui/AppIcon.vue — centralized icon registry
|
|
- frontend/src/components/storage/__tests__/StorageBrowser.dragmove.test.js — shared browser mount/event pattern
|
|
- .planning/phases/12-cloud-resource-foundation/12-UI-SPEC.md — exact capability colors, props, interaction, copy, accessibility
|
|
</read_first>
|
|
<action>
|
|
Add typed/defaulted props for capabilities, connectionRoot, folderFreshness, lastRefreshedAt, and byteAvailability. Supply a full-supported local capability set so local and cloud use one action-rendering path. Keep action controls in stable locations. Supported controls emit existing generic events; unsupported controls render gray with `aria-disabled="true"`; temporarily unavailable controls render amber; neither uses native `disabled`; Enter, Space, pointer click, and touch tap suppress the action and surface the capability message through a shared explanation event/state. Use tooltips for hover/focus and an inline dismissible notice for touch per UI-SPEC. Do not expose Phase 13 mutation handlers: controls may be present but capability-disabled. Add a compact cached-byte icon and exact quota tooltip only when availability is `cached`; cloud_only renders no marker. Add AppIcon entries only if existing names are insufficient. Tests cover all three states, exact message/remedy, click/keyboard/touch action suppression, active event emission, touch target classes, cached marker, no cloud-only marker, and unchanged local drag/move/share/delete behavior.
|
|
</action>
|
|
<acceptance_criteria>
|
|
- no action visibility block depends solely on `mode === 'local'`
|
|
- `aria-disabled` controls remain focusable and never emit the underlying action
|
|
- unsupported and temporary states use gray and amber UI-SPEC tokens respectively
|
|
- local capability defaults preserve all pre-Phase-12 local actions
|
|
- all StorageBrowser tests pass
|
|
</acceptance_criteria>
|
|
<verify><automated>cd frontend && npm test -- --run StorageBrowser</automated></verify>
|
|
<done>One shared browser renders and safely explains every capability state while preserving local behavior.</done>
|
|
</task>
|
|
|
|
<task type="auto">
|
|
<name>Task 3: Add connection breadcrumb freshness and finish UI plan protocol</name>
|
|
<files>frontend/src/components/ui/BreadcrumbBar.vue, frontend/src/components/ui/__tests__/BreadcrumbBar.test.js, frontend/src/utils/formatters.js, frontend/src/views/CloudFolderView.vue, backend/main.py, frontend/package.json, frontend/package-lock.json, AGENTS.md, README.md</files>
|
|
<read_first>
|
|
- frontend/src/components/ui/BreadcrumbBar.vue — existing generic segment/root API
|
|
- frontend/src/components/ui/__tests__/BreadcrumbBar.test.js — current test conventions
|
|
- frontend/src/utils/formatters.js — single source for date/size/provider formatting
|
|
- .planning/phases/12-cloud-resource-foundation/12-UI-SPEC.md — sync indicator states, copy, typography, spacing
|
|
- AGENTS.md — documentation and version protocol
|
|
</read_first>
|
|
<action>
|
|
Extend BreadcrumbBar generically with optional connection root icon/name and freshness metadata. Render refreshing spinner with accessible label, fresh checkmark that fades after three seconds without layout shift, and persistent warning indicator/banner with exact UI-SPEC copy and relative last-update time. Keep cached rows interactive. Use shared formatter utilities; add one reusable relative-time formatter there only if absent. Ensure no repeated provider badge appears on rows within a connection. Add fake-timer tests for fade behavior plus labels/tooltips/warning copy. Apply plan protocol: bump versions from 0.1.5 to 0.1.6 in backend main, package.json, and package-lock root/package entry; update AGENTS current state/shared component map if contracts changed; update README feature text for unified connection-root browsing and capability explanations.
|
|
</action>
|
|
<acceptance_criteria>
|
|
- breadcrumb root is connection display name, with no redundant `Cloud` segment
|
|
- freshness exposes exact accessible labels for refreshing, up to date, and failed
|
|
- fresh indicator fades without resizing the breadcrumb; warning persists until state changes
|
|
- versions are exactly 0.1.6 across backend/frontend lock sources
|
|
- focused frontend suite and production build pass
|
|
</acceptance_criteria>
|
|
<verify><automated>cd frontend && npm test -- --run BreadcrumbBar StorageBrowser CloudFolderView && npm run build</automated></verify>
|
|
<done>Breadcrumb freshness, cached-byte status, documentation, version 0.1.6, focused tests, and production build are complete.</done>
|
|
</task>
|
|
|
|
</tasks>
|
|
|
|
<threat_model>
|
|
| Threat ID | Threat | Mitigation and evidence |
|
|
|-----------|--------|-------------------------|
|
|
| T-12-01 | Client route IDOR | Backend remains authority; frontend uses opaque connection UUID and tests never infer owner/provider access |
|
|
| T-12-08 | Stored XSS via filename/provider message | Vue text interpolation only; no `v-html`; controlled capability message schema |
|
|
| T-12-10 | Disabled action executes on keyboard/touch | `aria-disabled` guard suppresses click/Enter/Space and tests assert no action emit |
|
|
| T-12-11 | Secret/token persistence | sessionStorage contains folder references only; negative source/test assertions |
|
|
| T-12-12 | UI source confusion | connection root breadcrumb and contextual provider icon; no misleading per-row duplication |
|
|
</threat_model>
|
|
|
|
<verification>
|
|
1. `cd frontend && npm test`
|
|
2. `cd frontend && npm run build`
|
|
3. Playwright desktop/mobile inspection: tooltip focus/hover/tap, no overlap, cached rows usable during refresh.
|
|
4. Source scan confirms StorageBrowser remains the only file grid and views stay thin.
|
|
</verification>
|
|
|
|
<success_criteria>
|
|
- Multiple provider accounts appear as distinct connection roots and navigate by connection UUID.
|
|
- Local and cloud files share one row/action implementation.
|
|
- Unsupported/temporary actions explain themselves accessibly without executing.
|
|
- Breadcrumb freshness and cached-byte status match the approved UI contract.
|
|
</success_criteria>
|
|
|
|
<output>Create `.planning/phases/12-cloud-resource-foundation/12-03-SUMMARY.md` when complete.</output>
|