Files
kite/.planning/phases/14.1-cloud-local-file-parity-hardening/14.1-03-PLAN.md
T

17 KiB

phase, plan, type, wave, depends_on, files_modified, autonomous, requirements, must_haves
phase plan type wave depends_on files_modified autonomous requirements must_haves
14.1-cloud-local-file-parity-hardening 03 execute 3
14.1-01
14.1-02
frontend/src/components/storage/DocumentDetailSurface.vue
frontend/src/views/DocumentView.vue
frontend/src/views/CloudDetailView.vue
frontend/src/router/index.js
frontend/src/api/cloud.js
frontend/src/stores/cloudConnections.js
true
CLOUD-02
ANALYZE-01
ANALYZE-05
ANALYZE-06
CACHE-03
truths artifacts key_links
A shared DocumentDetailSurface renders header, source metadata, status, topics, and extracted text for both local and cloud detail
Local DocumentView and cloud CloudDetailView are thin data providers that feed the shared surface and handle its events
A named cloud-file-detail route opens a cloud detail view from a connection id and opaque provider item id
The cloud detail view shows Analyze before analysis and Re-analyze after, with confirmation for forcing a current file
Preview unavailable keeps Download active and never auto-downloads
Visible copy says Re-analyze everywhere; no rendered Re-classify remains
path provides min_lines
frontend/src/components/storage/DocumentDetailSurface.vue Shared detail surface (header, source metadata, status/stale, topics, extracted text, action slot) 80
path provides min_lines
frontend/src/views/CloudDetailView.vue Cloud detail data-provider view backed by CloudItem detail endpoint 60
path provides contains
frontend/src/router/index.js Named cloud-file-detail route under /cloud cloud-file-detail
path provides contains
frontend/src/api/cloud.js getCloudItemDetail API client method getCloudItemDetail
path provides contains
frontend/src/stores/cloudConnections.js reanalyze/force enqueue + detail fetch wiring; single translateAnalysisStatus source translateAnalysisStatus
from to via pattern
frontend/src/views/CloudDetailView.vue frontend/src/api/cloud.js calls getCloudItemDetail(connectionId, itemId) getCloudItemDetail
from to via pattern
frontend/src/views/DocumentView.vue frontend/src/components/storage/DocumentDetailSurface.vue renders the shared surface with local data + handlers DocumentDetailSurface
from to via pattern
frontend/src/router/index.js frontend/src/views/CloudDetailView.vue cloud-file-detail route component CloudDetailView
Extract a shared document detail surface from DocumentView.vue and build a cloud detail route/view backed by the CloudItem detail endpoint, so local and cloud files render the same detail layout, status, topics, extracted text, and action slot without forking. Rename visible Re-classify to Re-analyze, add force re-analyze with confirmation, and ensure unsupported preview never auto-downloads.

Purpose: Implements D-01..D-11, D-13, D-14, D-19 frontend contracts and the UI-SPEC Detail Layout / Detail State / Preview-Unsupported / Re-Analyze contracts. Satisfies CLOUD-02 (authorized open/preview/view parity), ANALYZE-01/05/06 (analyze/re-analyze/retry/force), CACHE-03 (bytes only via authorized handlers). Output: DocumentDetailSurface.vue shared component; DocumentView.vue refactored to use it; CloudDetailView.vue + cloud-file-detail route; getCloudItemDetail API method; store wiring for force re-analyze and detail fetch.

<execution_context> @$HOME/.claude/gsd-core/workflows/execute-plan.md @$HOME/.claude/gsd-core/templates/summary.md </execution_context>

@.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md @.planning/phases/14.1-cloud-local-file-parity-hardening/14.1-CONTEXT.md @.planning/phases/14.1-cloud-local-file-parity-hardening/14.1-RESEARCH.md @.planning/phases/14.1-cloud-local-file-parity-hardening/14.1-UI-SPEC.md @CLAUDE.md @frontend/src/views/DocumentView.vue @frontend/src/views/CloudFolderView.vue @frontend/src/router/index.js @frontend/src/api/cloud.js @frontend/src/stores/cloudConnections.js @frontend/src/utils/formatters.js @frontend/src/views/__tests__/CloudDetailParity.test.js Task 1: Extract DocumentDetailSurface.vue and refactor DocumentView.vue to use it (Re-analyze copy) - frontend/src/views/DocumentView.vue (full file — header block, Topics card with Re-classify button, Extracted Text card, reclassify()/suggestTopics() methods, preview modal) - frontend/src/components/topics/TopicBadge.vue (topic badge presentational component reused by the surface) - frontend/src/utils/formatters.js (formatDate, formatSize, providerColor, providerBg, providerLabel — use these, do not redefine) - .planning/phases/14.1-cloud-local-file-parity-hardening/14.1-UI-SPEC.md (Detail Layout Contract section order; Copywriting Contract; Re-Analyze And Retry Contract) - .planning/phases/14.1-cloud-local-file-parity-hardening/14.1-CONTEXT.md (D-04, D-05, D-08, D-09, D-10) - frontend/src/views/__tests__/CloudDetailParity.test.js (paired detail section-order assertions the surface must satisfy) Create frontend/src/components/storage/DocumentDetailSurface.vue as a presentational smart component owning the detail layout for BOTH local and cloud detail. Per the UI-SPEC Detail Layout Contract, the section order is: (1) Back navigation slot, (2) Header block — title (24px semibold, break-all wrap), metadata line (date/size/type, 12-14px muted), subtle source metadata line/chips for cloud (provider chip via providerColor/providerBg + location; absent or minimal for local), and a primary action cluster, (3) Status and source notice area (current/stale/working badges using green/amber/blue per UI-SPEC), (4) Topics section (TopicBadge list + empty copy "No topics assigned yet."), (5) Extracted text section (pre block), (6) secondary controls / inline error+help copy. Define props: title, metadataLine, source (provider/location/isStale/statusLabel), topics (array), analysisStatus, extractedText, previewState ({ supported, reason }), downloadState ({ supported }), analysisAction ({ kind: 'analyze'|'reanalyze'|'retry', busy }), and optional slots for delete/share/suggest controls so local DocumentView can inject its extra buttons. Define emits: preview, download, reanalyze, retry-analysis, analyze. The single analysis action slot swaps Analyze/Re-analyze/Retry by analysisAction.kind (never accumulates buttons — UI-SPEC Re-Analyze contract). Use the Copywriting Contract literals: primary CTA "Analyze file" / "Preview file" / "Open file", empty heading "No analysis yet", empty body "Analyze this file to extract text and topics.". Use ONLY formatters from utils/formatters.js. Then refactor frontend/src/views/DocumentView.vue to render DocumentDetailSurface, feeding local document data and wiring its existing methods: pass reclassify() as the reanalyze handler, change the VISIBLE button label from "Re-classify" to "Re-analyze" (keep the internal classifyDocument API call name unchanged per D-09 / Codex discretion), keep suggestTopics and delete via the surface's secondary slots. Do not duplicate the card/grid markup in DocumentView after refactor — it becomes a data provider. Per CLAUDE.md no-dead-code rule, remove any now-unused local markup blocks in the same edit. cd frontend && npx vitest run src/views/__tests__/CloudDetailParity.test.js 2>&1 | tail -20 && grep -rc 'Re-classify' frontend/src/views/DocumentView.vue frontend/src/components/storage/DocumentDetailSurface.vue - `frontend/src/components/storage/DocumentDetailSurface.vue` exists and imports TopicBadge and formatters from utils/formatters.js (no local formatDate/formatSize/providerColor definitions): `grep -c "from '../../utils/formatters" frontend/src/components/storage/DocumentDetailSurface.vue` returns >= 1. - DocumentView renders the shared surface: `grep -c 'DocumentDetailSurface' frontend/src/views/DocumentView.vue` returns >= 1. - No rendered Re-classify in either file: `grep -c 'Re-classify' frontend/src/views/DocumentView.vue frontend/src/components/storage/DocumentDetailSurface.vue` returns 0. - Visible Re-analyze present: `grep -c 'Re-analyze' frontend/src/components/storage/DocumentDetailSurface.vue` returns >= 1. - The surface's analysis action region uses a single state-swapped slot (no two simultaneous Analyze+Re-analyze buttons) — confirm by reading the template region. - The paired detail section-order assertions in CloudDetailParity.test.js that target the shared surface pass. DocumentDetailSurface owns the local+cloud detail layout; DocumentView is a thin provider rendering it; visible copy says Re-analyze; no Re-classify remains in these files. Task 2: Cloud detail route + CloudDetailView.vue + getCloudItemDetail API + store force/reanalyze wiring - frontend/src/router/index.js (existing named routes /document/:id, cloud, cloud-folder; guard behavior) - frontend/src/api/cloud.js (openCloudFile, previewCloudFile, downloadCloudFile, enqueueAnalysis, retryAnalysisItem, jsonRequest helper — match the existing function/export style) - frontend/src/stores/cloudConnections.js (translateAnalysisStatus, enqueueAnalysis, retryItem, requestEstimate; reuse — do NOT add a second status translator) - frontend/src/views/CloudFolderView.vue (how cloud views resolve connectionId/route params, call api.* barrel, and push named routes with opaque provider_item_id) - frontend/src/components/storage/DocumentDetailSurface.vue (the surface this view feeds — created in Task 1) - .planning/phases/14.1-cloud-local-file-parity-hardening/14.1-UI-SPEC.md (Route And Navigation Contract; Detail State Contract; Preview/Download/Unsupported Contract; Re-Analyze confirmation copy) - .planning/phases/14.1-cloud-local-file-parity-hardening/14.1-CONTEXT.md (D-01, D-02, D-03, D-07, D-11, D-14) Add a named route cloud-file-detail to frontend/src/router/index.js under /cloud with an opaque provider item param, e.g. path /cloud/:connectionId/item/:itemId(.*) name 'cloud-file-detail' component () => import('../views/CloudDetailView.vue') meta { requiresAuth: true } — placed so it does not collide with the existing /cloud/:connectionId/:folderId(.*) cloud-folder route (use the distinct /item/ segment). Add getCloudItemDetail(connectionId, itemId) to frontend/src/api/cloud.js calling GET /api/cloud/connections/${connectionId}/items/${encodeURIComponent(itemId)}/detail via the same jsonRequest/get helper the other cloud methods use; never construct or expose provider URLs. Create frontend/src/views/CloudDetailView.vue as a thin data-provider view: read connectionId + itemId from route params (opaque — never split/decode provider IDs), call cloudStore/api getCloudItemDetail on mount, map the response into DocumentDetailSurface props (title=name, metadataLine from formatters, source={provider, location, isStale, statusLabel via store.translateAnalysisStatus}, topics, analysisStatus, extractedText, previewState from capabilities, downloadState from capabilities, analysisAction kind derived from analysis_status: 'analyze' when pending/none, 'reanalyze' when indexed/current/stale, 'retry' when failed/partial per D-08/Detail State Contract). Wire the surface events: preview → api.previewCloudFile (on unsupported_preview show "Preview unavailable" + backend reason and keep Download active; DO NOT auto-download — replace the CloudFolderView.onFileOpen auto-download behavior here per D-14); download → api.downloadCloudFile; analyze → store.enqueueAnalysis(scope file); reanalyze → confirm with the Copywriting Contract destructive confirmation ("Re-analyze this file? Existing extracted text and topics stay visible until the new analysis finishes."), then store.enqueueAnalysis with force:true (D-11); retry-analysis → store.retryItem or single-item retry path (D-12). Per D-07, when stale keep prior extracted_text/topics visible and promote Re-analyze. In frontend/src/stores/cloudConnections.js extend enqueueAnalysis to forward a force param into api.enqueueAnalysis params (and a convenience reanalyze action if cleaner), and add a fetchCloudItemDetail action calling api.getCloudItemDetail if components need store-level caching — keep translateAnalysisStatus as the single status source (do not add a second translator). Do not create a parallel cloud detail layout — CloudDetailView only provides data + handlers to DocumentDetailSurface. cd frontend && npx vitest run src/views/__tests__/CloudDetailParity.test.js 2>&1 | tail -25 - Named route present: `grep -c 'cloud-file-detail' frontend/src/router/index.js` returns 1 and references CloudDetailView. - API method present: `grep -c 'getCloudItemDetail' frontend/src/api/cloud.js` returns >= 1 and uses encodeURIComponent on itemId. - CloudDetailView feeds the shared surface: `grep -c 'DocumentDetailSurface' frontend/src/views/CloudDetailView.vue` returns >= 1. - Force re-analyze wired: `grep -nE 'force' frontend/src/views/CloudDetailView.vue frontend/src/stores/cloudConnections.js` shows force forwarded to enqueue. - No auto-download on unsupported preview in the detail flow: CloudDetailView's preview handler shows a reason and keeps download as a separate explicit action (confirm by reading; `grep -c 'Preview unavailable' frontend/src/views/CloudDetailView.vue` returns >= 1). - Only one status translator: `grep -c 'function translateAnalysisStatus' frontend/src/stores/cloudConnections.js` returns 1 and CloudDetailView contains no local status-translation function. - CloudDetailParity.test.js passes (route resolves, analyze→reanalyze by state, Re-analyze copy, no auto-download). cloud-file-detail route renders CloudDetailView feeding the shared surface; getCloudItemDetail loads CloudItem detail; force re-analyze confirms then enqueues with force; unsupported preview keeps Download explicit and never auto-downloads; Plan 01 cloud-detail tests pass.

<threat_model>

Trust Boundaries

Boundary Description
browser route → API connectionId + opaque provider itemId from route params; must be encoded, never parsed/decoded for provider structure
API response → rendered UI rendered detail must not expose provider URLs, credentials, or cache object keys

STRIDE Threat Register

Threat ID Category Component Disposition Mitigation Plan
T-14.1-07 Information Disclosure CloudDetailView render mitigate View renders only allowlisted CloudItemDetailOut fields; no provider URL/credential/object_key in template or store; UI-SPEC verification anchor
T-14.1-08 Tampering provider item id in route mitigate itemId passed opaque, encodeURIComponent in API call, Vue Router encodes route param; never split/decode
T-14.1-09 Spoofing/Surprise download unsupported preview mitigate Detail flow shows "Preview unavailable" + reason; Download is a separate explicit action (D-14); no auto-download
T-14.1-SC Tampering npm installs accept No package installs (RESEARCH: none)
</threat_model>
- `cd frontend && npx vitest run src/views/__tests__/CloudDetailParity.test.js` passes. - `cd frontend && npx vitest run 2>&1 | tail -10` — full frontend suite passes (DocumentView refactor breaks nothing). - `grep -rc 'Re-classify' frontend/src/views frontend/src/components` returns 0 for rendered templates (test snapshots may retain intentional references — confirm none are user-facing).

<success_criteria>

  • DocumentDetailSurface is the single shared detail layout for local + cloud.
  • DocumentView and CloudDetailView are thin data providers.
  • cloud-file-detail named route + getCloudItemDetail API + force re-analyze + no-auto-download all working.
  • Visible copy says Re-analyze; single translateAnalysisStatus source preserved. </success_criteria>

<artifacts_produced>

Artifacts this phase produces (Plan 03)

  • Component: frontend/src/components/storage/DocumentDetailSurface.vue — shared detail surface (props: title, metadataLine, source, topics, analysisStatus, extractedText, previewState, downloadState, analysisAction; emits: preview, download, reanalyze, retry-analysis, analyze).
  • View: frontend/src/views/CloudDetailView.vue — cloud detail data provider.
  • Route: named cloud-file-detail at /cloud/:connectionId/item/:itemId(.*) (frontend/src/router/index.js).
  • API method: getCloudItemDetail(connectionId, itemId) (frontend/src/api/cloud.js).
  • Store wiring: force-aware enqueueAnalysis (+ optional fetchCloudItemDetail) in frontend/src/stores/cloudConnections.js; DocumentView visible label changed to "Re-analyze". </artifacts_produced>
Create `.planning/phases/14.1-cloud-local-file-parity-hardening/14.1-03-SUMMARY.md` when done