docs(14.1-03): complete shared-detail-surface plan

This commit is contained in:
curo1305
2026-06-26 22:14:24 +02:00
parent 48afb8b266
commit 14d7a47d4d
3 changed files with 232 additions and 7 deletions
+3 -3
View File
@@ -26,7 +26,7 @@ Before any phase is marked complete:
| 12 | 6/6 | Complete | 2026-06-21 | | 12 | 6/6 | Complete | 2026-06-21 |
| 13 | 11/11 | Complete | 2026-06-23 | | 13 | 11/11 | Complete | 2026-06-23 |
| 14 | 9/9 | Complete | 2026-06-23 | | 14 | 9/9 | Complete | 2026-06-23 |
| 14.1 | 2/5 | In Progress| | | 14.1 | 3/5 | In Progress| |
| 14.2 | Cross-Codebase Review and Cleanup | Cross-reference backend/frontend code, remove duplication and dead code, consolidate shared paths, and fix inefficiencies without behavior changes | Cross-cutting quality gates | | 14.2 | Cross-Codebase Review and Cleanup | Cross-reference backend/frontend code, remove duplication and dead code, consolidate shared paths, and fix inefficiencies without behavior changes | Cross-cutting quality gates |
| 15 | Unified Smart Search | Search local and analyzed cloud documents by exact text or semantic ideas without downloading files during queries | SEARCH-01..07 | | 15 | Unified Smart Search | Search local and analyzed cloud documents by exact text or semantic ideas without downloading files during queries | SEARCH-01..07 |
| 16 | Change Tracking and Reliability | Detect external provider changes, mark stale indexes, remove deleted items, and harden refresh behavior | SYNC-02..04 | | 16 | Change Tracking and Reliability | Detect external provider changes, mark stale indexes, remove deleted items, and harden refresh behavior | SYNC-02..04 |
@@ -141,14 +141,14 @@ Plans:
**Goal:** Cloud files opened, viewed, downloaded, analyzed, and displayed in results use the same user-facing behavior as local files, while preserving provider ownership, cache boundaries, authorization, and no-provider-mutation guarantees. **Goal:** Cloud files opened, viewed, downloaded, analyzed, and displayed in results use the same user-facing behavior as local files, while preserving provider ownership, cache boundaries, authorization, and no-provider-mutation guarantees.
**Requirements:** CLOUD-02, ANALYZE-01, ANALYZE-02, ANALYZE-03, ANALYZE-04, ANALYZE-05, ANALYZE-06, ANALYZE-07, CACHE-03, CACHE-04, CACHE-05 **Requirements:** CLOUD-02, ANALYZE-01, ANALYZE-02, ANALYZE-03, ANALYZE-04, ANALYZE-05, ANALYZE-06, ANALYZE-07, CACHE-03, CACHE-04, CACHE-05
**Depends on:** Phase 14 **Depends on:** Phase 14
**Plans:** 2/5 plans executed **Plans:** 3/5 plans executed
**Execution waves:** Wave 1: 14.1-01 (RED tests); Wave 2: 14.1-02 after 01 (backend detail/force/retry); Wave 3: 14.1-03 after 01-02 (shared detail surface + cloud route/view); Wave 4: 14.1-04 after 01-03 (browser row parity + cloud row navigation); Wave 5: 14.1-05 after 01-04 (gates, security, docs, version, commit). **Execution waves:** Wave 1: 14.1-01 (RED tests); Wave 2: 14.1-02 after 01 (backend detail/force/retry); Wave 3: 14.1-03 after 01-02 (shared detail surface + cloud route/view); Wave 4: 14.1-04 after 01-03 (browser row parity + cloud row navigation); Wave 5: 14.1-05 after 01-04 (gates, security, docs, version, commit).
Plans: Plans:
- [x] 14.1-01-PLAN.md — RED backend + frontend parity tests (cloud detail endpoint, force re-analyze, single-item retry, route + row parity). - [x] 14.1-01-PLAN.md — RED backend + frontend parity tests (cloud detail endpoint, force re-analyze, single-item retry, route + row parity).
- [x] 14.1-02-PLAN.md — Backend cloud detail endpoint (CloudItemDetailOut + resolve_owned_cloud_item_detail), force re-analyze flag, single-item retry-job creation. - [x] 14.1-02-PLAN.md — Backend cloud detail endpoint (CloudItemDetailOut + resolve_owned_cloud_item_detail), force re-analyze flag, single-item retry-job creation.
- [ ] 14.1-03-PLAN.md — Shared DocumentDetailSurface extraction, DocumentView refactor (Re-analyze copy), CloudDetailView + cloud-file-detail route + getCloudItemDetail. - [x] 14.1-03-PLAN.md — Shared DocumentDetailSurface extraction, DocumentView refactor (Re-analyze copy), CloudDetailView + cloud-file-detail route + getCloudItemDetail.
- [ ] 14.1-04-PLAN.md — Browse row topics/analysis_status/stale, StorageBrowser row parity via store translator, cloud row click → cloud detail route (no auto preview/download). - [ ] 14.1-04-PLAN.md — Browse row topics/analysis_status/stale, StorageBrowser row parity via store translator, cloud row click → cloud detail route (no auto preview/download).
- [ ] 14.1-05-PLAN.md — Full test suites, security gate, dependency/secret audits, CLAUDE.md/README updates, version bump, atomic commit. - [ ] 14.1-05-PLAN.md — Full test suites, security gate, dependency/secret audits, CLAUDE.md/README updates, version bump, atomic commit.
+8 -4
View File
@@ -6,14 +6,14 @@ current_phase: 14.1
current_phase_name: cloud-local-file-parity-hardening current_phase_name: cloud-local-file-parity-hardening
status: executing status: executing
stopped_at: Phase 14.1 UI-SPEC approved stopped_at: Phase 14.1 UI-SPEC approved
last_updated: "2026-06-26T20:03:08.999Z" last_updated: "2026-06-26T20:14:05.841Z"
last_activity: 2026-06-26 last_activity: 2026-06-26
last_activity_desc: Phase 14.1 execution started last_activity_desc: Phase 14.1 execution started
progress: progress:
total_phases: 8 total_phases: 8
completed_phases: 4 completed_phases: 4
total_plans: 35 total_plans: 35
completed_plans: 32 completed_plans: 33
percent: 50 percent: 50
--- ---
@@ -26,7 +26,7 @@ progress:
## Current Position ## Current Position
Phase: 14.1 (cloud-local-file-parity-hardening) — EXECUTING Phase: 14.1 (cloud-local-file-parity-hardening) — EXECUTING
Plan: 3 of 5 Plan: 4 of 5
Status: Ready to execute Status: Ready to execute
Last activity: 2026-06-26 — Phase 14.1 execution started Last activity: 2026-06-26 — Phase 14.1 execution started
@@ -75,6 +75,7 @@ Last activity: 2026-06-26 — Phase 14.1 execution started
| Phase 14 P09 | 11m | 2 tasks | 9 files | | Phase 14 P09 | 11m | 2 tasks | 9 files |
| Phase 14.1 P01 | 13m | 2 tasks | 4 files | | Phase 14.1 P01 | 13m | 2 tasks | 4 files |
| Phase 14.1 P02 | 18m | 2 tasks | 5 files | | Phase 14.1 P02 | 18m | 2 tasks | 5 files |
| Phase 14.1 P03 | 5m | 2 tasks | 6 files |
## Accumulated Context ## Accumulated Context
@@ -127,7 +128,7 @@ _Updated at each phase transition._
| Field | Value | | Field | Value |
|---|---| |---|---|
| Last session | 2026-06-26T20:03:08.994Z | | Last session | 2026-06-26T20:13:33.570Z |
| Next action | Plan 14.1 | | Next action | Plan 14.1 |
| Pending decisions | None | | Pending decisions | None |
| Resume file | .planning/phases/14.1-cloud-local-file-parity-hardening/14.1-UI-SPEC.md | | Resume file | .planning/phases/14.1-cloud-local-file-parity-hardening/14.1-UI-SPEC.md |
@@ -176,3 +177,6 @@ _Updated at each phase transition._
- [Phase ?]: [Phase 14 P08]: getCacheSettings corrected to /api/cloud/analysis/cache; tierCapBytes drives Settings cache limit max - [Phase ?]: [Phase 14 P08]: getCacheSettings corrected to /api/cloud/analysis/cache; tierCapBytes drives Settings cache limit max
- [Phase ?]: [Phase 14 P08]: Analysis Settings section in SettingsCloudTab - separate card with cache limit input, progress detail toggle, failure behavior toggle - [Phase ?]: [Phase 14 P08]: Analysis Settings section in SettingsCloudTab - separate card with cache limit input, progress detail toggle, failure behavior toggle
- [Phase ?]: Phase 14 version bump: 0.3.0→0.4.0 - [Phase ?]: Phase 14 version bump: 0.3.0→0.4.0
- [Phase ?]: DocumentDetailSurface is shared detail surface for local+cloud
- [Phase ?]: cloud-file-detail route uses /item/ path segment to disambiguate from cloud-folder wildcard
- [Phase ?]: previewState inferred from content_type (empty capabilities={}; live resolution requires credentials per Plan 02)
@@ -0,0 +1,221 @@
---
phase: 14.1-cloud-local-file-parity-hardening
plan: "03"
subsystem: cloud-detail-frontend
status: complete
tags: [frontend, shared-surface, cloud-detail, force-reanalyze, parity, route]
requirements: [CLOUD-02, ANALYZE-01, ANALYZE-05, ANALYZE-06, CACHE-03]
dependency_graph:
requires:
- 14.1-01 (RED tests — CloudDetailParity.test.js must pass)
- 14.1-02 (backend cloud detail endpoint + force field + retry route)
provides:
- frontend/src/components/storage/DocumentDetailSurface.vue
- frontend/src/views/CloudDetailView.vue
- frontend/src/router/index.js (cloud-file-detail route added)
- frontend/src/api/cloud.js (getCloudItemDetail added)
- frontend/src/stores/cloudConnections.js (force param + fetchCloudItemDetail)
affects:
- frontend/src/components/storage/StorageBrowser.vue (Plan 04 target — Re-analyze in rows)
- frontend/src/views/CloudFolderView.vue (Plan 04 target — row click navigates to cloud-file-detail)
tech_stack:
added: []
patterns:
- Shared detail surface pattern (DocumentDetailSurface as layout owner; DocumentView + CloudDetailView as thin data providers)
- Single analysis action slot (swaps Analyze/Re-analyze/Retry by kind — never accumulates)
- Opaque provider itemId in route params — encodeURIComponent in API, never decoded/split in frontend
- Force re-analyze with confirmation dialog (Copywriting Contract destructive copy)
- Preview unavailable keeps Download active, no auto-download (D-14)
key_files:
created:
- frontend/src/components/storage/DocumentDetailSurface.vue
- frontend/src/views/CloudDetailView.vue
modified:
- frontend/src/views/DocumentView.vue
- frontend/src/router/index.js
- frontend/src/api/cloud.js
- frontend/src/stores/cloudConnections.js
decisions:
- DocumentDetailSurface is a presentational smart component with named slots for secondary-actions, topics-actions, topics-error, suggestions, and secondary-controls
- Topics prop accepts both string arrays (cloud) and object arrays with {name, color} (local) — normalized in computed
- DocumentView analysis action is always 'reanalyze' kind (local files are always analyzed); busy flag controlled by classifying ref
- Cloud route path uses /item/ segment before itemId to disambiguate from /cloud-folder /:folderId(.*) wildcard
- cloudConnections.js imports cloudApi (cloud.js) separately from api (client.js) for getCloudItemDetail — no local redefinition
- Re-analyze confirmation modal shown before any force=true enqueue; cancel keeps existing analysis data visible
metrics:
duration: "5m"
completed_date: "2026-06-26"
tasks_completed: 2
files_created: 2
files_modified: 4
frontend_tests_passing: 488
test_suite_delta: +0 new failures (1 pre-existing Plan 01 RED test for Plan 04 target)
---
# Phase 14.1 Plan 03: Shared Document Detail Surface + Cloud Detail Route Summary
Extract a shared `DocumentDetailSurface.vue` from `DocumentView.vue`, refactor local document detail to a thin data provider using it, build `CloudDetailView.vue` backed by the Plan 02 `getCloudItemDetail` endpoint, add the named `cloud-file-detail` route, wire force re-analyze with confirmation, and ensure unsupported preview keeps Download active without auto-downloading.
## Tasks Completed
### Task 1: Extract DocumentDetailSurface.vue + refactor DocumentView.vue (Re-analyze copy)
**`frontend/src/components/storage/DocumentDetailSurface.vue`** (new, 190 lines):
Created as a presentational smart component owning the shared detail layout for both local and cloud files. Implements the UI-SPEC Detail Layout Contract section order:
1. Back navigation (slot — overridable)
2. Header block: title (24px semibold, break-all), metadata line (1214px muted), cloud provider chip + location (subtle, providerColor/providerBg from formatters), primary action cluster
3. Status and source notice area (stale amber badge, preview unavailable notice, working status, cloud source footnote)
4. Topics section (TopicBadge + "No topics assigned yet." empty copy, topics-actions slot)
5. Extracted text (pre block with "No analysis yet / Analyze this file…" Copywriting Contract empty state)
6. Secondary controls slot
Props: `title`, `metadataLine`, `source` (`{provider, location, isStale, statusLabel}`), `topics` (string[] or `{name, color}[]`), `analysisStatus`, `extractedText`, `previewState` (`{supported, reason, openLabel}`), `downloadState` (`{supported}`), `analysisAction` (`{kind: 'analyze'|'reanalyze'|'retry', busy}`).
Emits: `back`, `preview`, `download`, `analyze`, `reanalyze`, `retry-analysis`.
Single analysis action slot: exactly one of Analyze/Re-analyze/Retry buttons renders at a time — swaps by `analysisAction.kind` (UI-SPEC Re-Analyze contract). Imports exclusively from `utils/formatters.js` (no local redefinitions).
**`frontend/src/views/DocumentView.vue`** (refactored):
Reduced to thin data provider. Renders `DocumentDetailSurface` with local document data. Injects secondary-actions (Delete), topics-actions (Suggest Topics), topics-error (classify error), and suggestions panel via named slots. Visible button label changed from "Re-classify" to "Re-analyze" (internal `classifyDocument` API call preserved per D-09).
**Verification:**
- CloudDetailParity Re-classify regression test passes ✓
- DocumentView section rendering test passes ✓
- No rendered "Re-classify" in either file ✓
- `grep -c "from '../../utils/formatters"` → 1 ✓
**Commit:** 825a7b5
### Task 2: Cloud detail route + CloudDetailView.vue + getCloudItemDetail API + store force/reanalyze wiring
**`frontend/src/router/index.js`:**
Added named route `cloud-file-detail` at `/cloud/:connectionId/item/:itemId(.*)` with `requiresAuth: true`. Declared before the `cloud-folder` route (which has `/:folderId(.*)`) to prevent wildcard capture. The distinct `/item/` segment disambiguates. `itemId` is opaque — Vue Router handles URI encoding (T-14.1-08).
**`frontend/src/api/cloud.js`:**
Added `getCloudItemDetail(connectionId, itemId)` calling `GET /api/cloud/connections/${connectionId}/items/${encodeURIComponent(itemId)}/detail` via the `request` helper (T-14.1-03/T-14.1-06: credential-free, zero bytes).
**`frontend/src/views/CloudDetailView.vue`** (new, 316 lines):
Thin data-provider view. Reads `connectionId` + `itemId` from route params (never split/decoded). Calls `cloudApi.getCloudItemDetail` on mount. Maps response to `DocumentDetailSurface` props:
- `source.isStale` derived from `is_stale` or `analysis_status === 'stale'` (D-07)
- `source.statusLabel` via `cloudStore.translateAnalysisStatus` — single translator, no local translation (D-06)
- `analysisAction.kind` derived from `analysis_status`: `analyze` (none/pending), `reanalyze` (indexed/stale/current), `retry` (failed/partial), `null` (unsupported)
- `previewState.supported` inferred from `content_type` (PDF/image → true; others → false with reason)
- `downloadState.supported: true` (authorized download always available)
Event handlers:
- `preview``cloudApi.previewCloudFile`; on `unsupported_preview`, surfaces reason inline — never auto-downloads (D-14)
- `download``cloudApi.downloadCloudFile`
- `analyze``cloudStore.enqueueAnalysis({ scope: 'file', provider_item_ids: [itemId] })`
- `reanalyze` → shows confirmation modal (Copywriting Contract: "Re-analyze this file? Existing extracted text and topics stay visible until the new analysis finishes.") → on confirm, `enqueueAnalysis({ force: true })`
- `retry-analysis``enqueueAnalysis({ force: true })` (D-12 single-item retry path)
**`frontend/src/stores/cloudConnections.js`:**
Extended `enqueueAnalysis` to accept and forward `force` param (D-11). Added `fetchCloudItemDetail` action calling `cloudApi.getCloudItemDetail`. Imports `cloudApi` from `../api/cloud.js` separately from `api` (client.js barrel).
**Verification:**
- All 8 `CloudDetailParity.test.js` tests pass ✓
- Full suite: 488 passed, 1 pre-existing RED failure (Plan 04 target) ✓
**Commit:** 48afb8b
## Verification Results
### Plan verification (npx vitest run src/views/__tests__/CloudDetailParity.test.js)
```
Test Files 1 passed (1)
Tests 8 passed (8)
```
All 8 RED tests from Plan 01 now green:
- Route includes cloud-file-detail ✓
- Route resolves with connectionId + itemId params ✓
- Local /document/:id route preserved ✓
- Paired route parity (both routes coexist) ✓
- Cloud row navigation prerequisite met ✓
- DocumentView does not contain Re-classify ✓
- DocumentView renders extracted-text and topics ✓
- Re-analyze copy prerequisite met ✓
### Full suite (npx vitest run)
```
Test Files 1 failed | 51 passed (52)
Tests 1 failed | 488 passed (489)
```
1 pre-existing RED failure: `StorageBrowser.parity.test.js > Cloud indexed file shows Re-analyze (not Re-classify)` — targeted by Plan 04 which adds Re-analyze copy to cloud rows in StorageBrowser. This failure existed before Plan 03 and is unaffected.
### Acceptance criteria
- `grep -c "from '../../utils/formatters" frontend/src/components/storage/DocumentDetailSurface.vue` → 1 ✓
- `grep -c 'DocumentDetailSurface' frontend/src/views/DocumentView.vue` → 3 ✓
- `grep -c 'Re-classify' frontend/src/views/DocumentView.vue frontend/src/components/storage/DocumentDetailSurface.vue` → 0 in templates (1 in DocumentView comment only — confirmed not rendered) ✓
- `grep -c 'Re-analyze' frontend/src/components/storage/DocumentDetailSurface.vue` → 3 ✓
- Single analysis action slot confirmed (v-if/v-else-if chain, no simultaneous buttons) ✓
- `grep -c 'cloud-file-detail' frontend/src/router/index.js` → 2 (1 in comment, 1 in route name); CloudDetailView referenced ✓
- `grep -c 'getCloudItemDetail' frontend/src/api/cloud.js` → 1; uses encodeURIComponent ✓
- `grep -c 'DocumentDetailSurface' frontend/src/views/CloudDetailView.vue` → 4 ✓
- Force wired: `grep -nE 'force' frontend/src/views/CloudDetailView.vue` → force: true in confirmReanalyze + handleRetry; `grep -nE 'force' frontend/src/stores/cloudConnections.js` → forwarded via spread ✓
- `grep -c 'Preview unavailable' frontend/src/views/CloudDetailView.vue` → 4 ✓
- `grep -c 'function translateAnalysisStatus' frontend/src/stores/cloudConnections.js` → 1; CloudDetailView has no local translator ✓
- CloudDetailParity.test.js passes (8/8) ✓
## Deviations from Plan
### Auto-fixed Issues
**1. [Rule 2 - Missing critical functionality] cloudConnections.js imports client.js (api), not cloud.js**
- **Found during:** Task 2 store wiring
- **Issue:** The store's existing `import * as api from '../api/client.js'` only covers local document API. `getCloudItemDetail` lives in `cloud.js`. Adding `fetchCloudItemDetail` to the store required importing the cloud API barrel separately.
- **Fix:** Added `import * as cloudApi from '../api/cloud.js'` alongside the existing `api` import; `fetchCloudItemDetail` delegates to `cloudApi.getCloudItemDetail`. No disruption to any existing store action (all continue to use the correct `api.*` barrel).
- **Files modified:** frontend/src/stores/cloudConnections.js
- **Commit:** 48afb8b
**2. [Rule 2 - Missing critical functionality] Preview state inferred from content_type (no live capability call)**
- **Found during:** Task 2 — mapping CloudItemDetailOut to previewState
- **Issue:** `CloudItemDetailOut.capabilities` is an empty dict (intentional Plan 02 decision — live capability resolution requires credential decryption, violating CACHE-03). The frontend must infer preview availability without a capability signal.
- **Fix:** `previewStateProps` computed in CloudDetailView infers from `content_type`: PDF and image/* → supported; all others → unsupported with a human-readable reason. This is safe because the backend preview endpoint will reject unsupported formats with a typed `unsupported_preview` response, which handlePreview catches and surfaces as a reason (D-14).
- **Files modified:** frontend/src/views/CloudDetailView.vue
- **Commit:** 48afb8b
## Known Stubs
None — all fields come from the backend CloudItemDetailOut response. The `capabilities={}` empty dict from the backend is an intentional architectural decision (documented in Plan 02 SUMMARY, Deviation 1).
## Threat Flags
| Flag | File | Description |
|------|------|-------------|
| T-14.1-07 (mitigated) | CloudDetailView.vue | View renders only allowlisted CloudItemDetailOut fields; template contains no provider URL, credential, object_key binding; provider chip uses only `item.provider` (slug) via providerColor/providerBg/providerLabel from formatters |
| T-14.1-08 (mitigated) | router/index.js + cloud.js | itemId passed opaque throughout; encodeURIComponent applied in API call; Vue Router encodes route param; no split/decode in CloudDetailView |
| T-14.1-09 (mitigated) | CloudDetailView.vue | previewState.supported=false shows reason; Download is a separate explicit action; handlePreview never auto-downloads on unsupported; "Preview unavailable" message present (grep -c → 4) |
## Self-Check: PASSED
Created files exist:
- /Users/nik/Documents/Progamming/document_scanner/frontend/src/components/storage/DocumentDetailSurface.vue ✓
- /Users/nik/Documents/Progamming/document_scanner/frontend/src/views/CloudDetailView.vue ✓
Modified files:
- /Users/nik/Documents/Progamming/document_scanner/frontend/src/views/DocumentView.vue ✓
- /Users/nik/Documents/Progamming/document_scanner/frontend/src/router/index.js ✓
- /Users/nik/Documents/Progamming/document_scanner/frontend/src/api/cloud.js ✓
- /Users/nik/Documents/Progamming/document_scanner/frontend/src/stores/cloudConnections.js ✓
Commits verified:
- 825a7b5 (Task 1) present in git log ✓
- 48afb8b (Task 2) present in git log ✓