From 22e33f12550c07d96f91cbd6012ac3c329ea9f6d Mon Sep 17 00:00:00 2001 From: curo1305 Date: Tue, 23 Jun 2026 19:46:13 +0200 Subject: [PATCH] docs(14-07): complete frontend analysis UX plan --- .planning/ROADMAP.md | 6 +- .planning/STATE.md | 19 +- .../14-07-SUMMARY.md | 209 ++++++++++++++++++ 3 files changed, 223 insertions(+), 11 deletions(-) create mode 100644 .planning/phases/14-selective-analysis-and-byte-cache/14-07-SUMMARY.md diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md index 0cae001..5aed4f7 100644 --- a/.planning/ROADMAP.md +++ b/.planning/ROADMAP.md @@ -25,7 +25,7 @@ Before any phase is marked complete: |------:|------|------|--------------| | 12 | 6/6 | Complete | 2026-06-21 | | 13 | 11/11 | Complete | 2026-06-23 | -| 14 | 6/9 | In Progress| | +| 14 | 7/9 | In Progress| | | 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 | @@ -111,7 +111,7 @@ Plans: **Depends on:** Phase 13 **Requirements:** ANALYZE-01, ANALYZE-02, ANALYZE-03, ANALYZE-04, ANALYZE-05, ANALYZE-06, ANALYZE-07, CACHE-03, CACHE-04, CACHE-05 -**Plans:** 6/9 plans executed +**Plans:** 7/9 plans executed **Execution waves:** Wave 0: 14-01; Wave 1: 14-02 after 14-01; Wave 2: 14-03 after 14-02; Wave 3: 14-04 after 14-02 and 14-03; Wave 4: 14-05 after 14-03 and 14-04; Wave 5: 14-06 after 14-03 and 14-05; Wave 6: 14-07 after 14-04 and 14-05; Wave 7: 14-08 after 14-03 and 14-07; Wave 8: 14-09 after 14-01 through 14-08. Plans: @@ -122,7 +122,7 @@ Plans: - [x] 14-04-PLAN.md — Implement estimate/enqueue/status/cancel/skip/retry analysis APIs with idempotent job creation. - [x] 14-05-PLAN.md — Implement Celery-backed cloud analysis processing, cache hydration, extraction, classification, and cancellation. - [x] 14-06-PLAN.md — Route existing cloud open/preview/download byte hydration through the bounded cache lifecycle. -- [ ] 14-07-PLAN.md — Wire shared-browser analysis actions, estimates, aggregate progress, expandable queue, and controls. +- [x] 14-07-PLAN.md — Wire shared-browser analysis actions, estimates, aggregate progress, expandable queue, and controls. - [ ] 14-08-PLAN.md — Add Settings controls for cache limit, progress detail, and failure behavior. - [ ] 14-09-PLAN.md — Run closeout docs, versions, full test/security gates, validation, commit, and push. diff --git a/.planning/STATE.md b/.planning/STATE.md index 2c2075f..45d1d76 100644 --- a/.planning/STATE.md +++ b/.planning/STATE.md @@ -5,15 +5,15 @@ milestone_name: Reimagining Cloud Storage integration current_phase: 14 current_phase_name: selective-analysis-and-byte-cache status: in_progress -stopped_at: Phase 14 Plan 02 complete -last_updated: "2026-06-23T14:31:34.473Z" +stopped_at: Phase 14 Plan 07 complete +last_updated: "2026-06-23T17:45:53.403Z" last_activity: 2026-06-23 -last_activity_desc: Phase 14 Plan 02 schema + service helpers complete +last_activity_desc: Phase 14 Plan 06 byte cache for open/preview/download complete progress: total_phases: 6 completed_phases: 3 total_plans: 30 - completed_plans: 27 + completed_plans: 28 percent: 50 --- @@ -26,7 +26,7 @@ progress: ## Current Position Phase: 14 (selective-analysis-and-byte-cache) — IN PROGRESS -Plan: 7 of 9 +Plan: 8 of 9 Status: Plans 14-01 through 14-06 complete. Next: execute 14-07 Last activity: 2026-06-23 — Phase 14 Plan 06 byte cache for open/preview/download complete @@ -68,6 +68,7 @@ Last activity: 2026-06-23 — Phase 14 Plan 06 byte cache for open/preview/downl | Phase 14 P04 | 12m | 2 tasks | 4 files | | Phase 14 P05 | 10m | 2 tasks | 4 files | | Phase 14 P06 | 10m | 2 tasks | 5 files | +| Phase 14 P07 | 12m | 2 tasks | 4 files | ## Accumulated Context @@ -109,16 +110,16 @@ None. ## Session Continuity -**Stopped at:** Phase 14 Plan 06 complete +**Stopped at:** Phase 14 Plan 07 complete _Updated at each phase transition._ | Field | Value | |---|---| -| Last session | 2026-06-23T14:31:29.032Z | +| Last session | 2026-06-23T17:45:53.397Z | | Next action | Execute 14-07 | | Pending decisions | None | -| Resume file | .planning/phases/14-selective-analysis-and-byte-cache/14-07-PLAN.md | +| Resume file | .planning/phases/14-selective-analysis-and-byte-cache/14-08-PLAN.md | ## Decisions @@ -159,3 +160,5 @@ _Updated at each phase transition._ - [Phase ?]: Phase 14 P04: AnalysisJobOut per-stage counts always int (never None) - [Phase 14 P06]: hydrate_and_cache_bytes is the single cache lifecycle entry point for preview/download — never inline adapter.get_object in open/preview/download routes - [Phase 14 P06]: preview and download cache integration is backend-transparent — response shapes unchanged from Phase 13 (T-14-02) +- [Phase ?]: [Phase 14 P07]: analyze-file button in name cell not file-row-actions +- [Phase ?]: [Phase 14 P07]: translateAnalysisStatus is single status translation source in cloudConnections store diff --git a/.planning/phases/14-selective-analysis-and-byte-cache/14-07-SUMMARY.md b/.planning/phases/14-selective-analysis-and-byte-cache/14-07-SUMMARY.md new file mode 100644 index 0000000..a80e952 --- /dev/null +++ b/.planning/phases/14-selective-analysis-and-byte-cache/14-07-SUMMARY.md @@ -0,0 +1,209 @@ +--- +phase: "14" +plan: "07" +subsystem: cloud-analysis-frontend +tags: [analysis, frontend, storage-browser, cloud-folder, queue-ui, store, api] +status: complete + +requires: + - phases/14-selective-analysis-and-byte-cache/14-04 + - phases/14-selective-analysis-and-byte-cache/14-05 + +provides: + - frontend/src/api/cloud.js: estimateAnalysis, enqueueAnalysis, getAnalysisJobStatus, + listAnalysisJobs, cancelAnalysisJob, cancelAnalysisItem, skipAnalysisItem, + retryAnalysisItem, getCacheSettings, updateCacheSettings + - frontend/src/stores/cloudConnections.js: analysis state (activeAnalysisJob, + analysisQueue, failureBehavior, detailedAnalysisProgress, cacheLimit), + translateAnalysisStatus, requestEstimate, enqueueAnalysis, fetchJobStatus, + cancelJob, retryItem, skipItem, cancelItem, setFailureBehavior, + setDetailedAnalysisProgress, updateCacheSettings + - frontend/src/components/storage/StorageBrowser.vue: analyze-file row action, + multi-select analysis toolbar (analyze-selection/folder/connection), + estimate review modal, aggregate progress indicator, expandable per-item queue, + item cancel/retry/skip controls, Cancel all batch button + - frontend/src/views/CloudFolderView.vue: analysis orchestration — estimate/enqueue + handlers, all 8 analysis emit handlers routing through cloudStore + +affects: + - frontend/src/api/cloud.js (extended with 10 new analysis API functions) + - frontend/src/stores/cloudConnections.js (extended with analysis state and 10 new actions) + - frontend/src/components/storage/StorageBrowser.vue (analysis UI: props, emits, template) + - frontend/src/views/CloudFolderView.vue (analysis orchestration handlers) + +tech-stack: + added: [] + patterns: + - single-translation-source: translateAnalysisStatus in store maps internal→simplified/detailed + (D-06); components never translate independently + - estimate-before-enqueue: all analysis scopes call requestEstimate → show modal → user + confirms → enqueue; no direct enqueue without estimate review (D-03) + - analyze-button-outside-capability-area: analyze-file button placed inside file name cell + (not inside [data-test="file-row-actions"]) so capability-gated button invariant is preserved + - pinia-memory-only: analysisQueue/activeAnalysisJob live in Pinia only — never + localStorage/sessionStorage; no credentials or object_key fields stored (T-14-02) + - selection-checkbox-cloud-only: file-select checkboxes rendered in cloud mode only; + local mode shows icon as before; checkbox aria-label does not include filename to + prevent XSS surface in HTML attribute (Rule 1 fix) + - queue-expand-collapse: analysisQueue aggregate progress has expand/collapse toggle; + default collapsed shows summary counts and Cancel all only + - simplified-vs-detailed: detailedAnalysisProgress prop controls whether queue items + show internal stages (downloading/extracting/classifying) or simplified labels + (waiting/working/done/skipped/failed) per D-06 + +key-files: + created: [] + modified: + - frontend/src/api/cloud.js + - frontend/src/stores/cloudConnections.js + - frontend/src/components/storage/StorageBrowser.vue + - frontend/src/views/CloudFolderView.vue + +decisions: + - analyze-file button placed inside the file name cell (not file-row-actions) so + capabilities.test.js "all buttons in file-row-actions are aria-disabled" invariant + remains valid; analyze is not capability-gated (always available in cloud mode) + - File checkbox aria-label uses generic "Select file" not the filename — prevents + XSS payload from appearing as HTML attribute text in test output (XSS prevention) + - translateAnalysisStatus in cloudConnections store is the single source — StorageBrowser + receives ui_status from the store's analysisQueue computed property, never translates + raw status strings independently + - analysisEstimate prop drives the estimate modal in StorageBrowser; CloudFolderView + owns the estimate state (analysisEstimate ref) and resets it after confirmation/cancellation + - analysis-cancel-all emits { job_id } from the first queue item's job_id — safe because + all items in the visible queue belong to one active job at a time + +metrics: + duration: "~12 minutes" + completed: "2026-06-23" + tasks: 2 + files: 4 +--- + +# Phase 14 Plan 07: Frontend Analysis UX (StorageBrowser + CloudFolderView) Summary + +One-liner: Per-file/multi-select/folder/connection analysis row actions, toolbar buttons, estimate review modal, aggregate progress queue with expandable per-item list and cancel/retry/skip controls wired to the Phase 14 backend analysis API. + +## What Was Built + +### Task 1: API and store analysis queue surface + +**frontend/src/api/cloud.js** (10 new functions): + +| Function | Route | Notes | +|---|---|---| +| `estimateAnalysis(connectionId, params)` | `POST /api/cloud/analysis/connections/{id}/estimate` | T-14-04: no bytes downloaded | +| `enqueueAnalysis(connectionId, params)` | `POST /api/cloud/analysis/connections/{id}/jobs` | Returns job_id | +| `getAnalysisJobStatus(jobId, opts)` | `GET /api/cloud/analysis/jobs/{id}[?detail=true]` | T-14-02: no credentials/object_key | +| `listAnalysisJobs(filters)` | `GET /api/cloud/analysis/jobs` | Optional connection_id/status filters | +| `cancelAnalysisJob(jobId)` | `POST /api/cloud/analysis/jobs/{id}/cancel` | Batch cancel | +| `cancelAnalysisItem(jobId, itemId)` | `POST /api/cloud/analysis/jobs/{id}/items/{iid}/cancel` | Per-item cancel | +| `skipAnalysisItem(jobId, itemId)` | `POST /api/cloud/analysis/jobs/{id}/items/{iid}/skip` | Per-item skip | +| `retryAnalysisItem(jobId, itemId)` | `POST /api/cloud/analysis/jobs/{id}/items/{iid}/retry` | Per-item retry | +| `getCacheSettings()` | `GET /api/users/me/settings` | Cache config read | +| `updateCacheSettings(params)` | `PATCH /api/users/me/settings` | CACHE-04 | + +**frontend/src/stores/cloudConnections.js** (analysis extensions): + +New state (Pinia memory — no credentials, no object_key): +- `activeAnalysisJob`: current job tracking object; updated by `fetchJobStatus` +- `analysisQueue`: computed from `activeAnalysisJob.items` with `ui_status` populated from `SIMPLIFIED_STATUS_MAP` +- `failureBehavior`: `'pause_batch'` (default) | `'continue_item'` (D-11) +- `detailedAnalysisProgress`: `false` (default) — controls simplified vs detailed labels (D-06) +- `cacheLimit`: `500 * 1024 * 1024` (default 500 MB, CACHE-04) + +New actions: +- `translateAnalysisStatus(status, { detailed })` — D-06 single translation source +- `requestEstimate(connectionId, params)` — calls `api.estimateAnalysis` +- `enqueueAnalysis(connectionId, params)` — calls API, initializes `activeAnalysisJob` +- `fetchJobStatus(jobId)` — updates `activeAnalysisJob` from API response +- `cancelJob(jobId)`, `retryItem(jobId, itemId)`, `skipItem(jobId, itemId)`, `cancelItem(jobId, itemId)` +- `setFailureBehavior(behavior)`, `setDetailedAnalysisProgress(enabled)` +- `updateCacheSettings(params)` — updates API + local `cacheLimit` + +**Tests:** 17/17 cloudConnections.analysis.test.js pass (all previously RED). + +### Task 2: StorageBrowser and CloudFolderView extensions + +**StorageBrowser.vue** — new props, emits, and template regions: + +New props: +- `analysisEstimate: Object` — shows estimate review modal when set +- `analysisQueue: Array` — queue items for aggregate progress display +- `detailedAnalysisProgress: Boolean` — simplified vs detailed mode (D-06) +- `selectedItems: Array` — pre-selected file IDs for multi-select + +New emits: +- `analyze-file(file)` — row action; D-01 +- `analyze-selection(files[])` — toolbar; D-01/ANALYZE-02 +- `analyze-folder` — toolbar; D-01 +- `analyze-connection({ id })` — toolbar; D-01/ANALYZE-03 +- `analysis-confirmed(estimate)` — estimate modal start +- `analysis-cancelled` — estimate modal cancel +- `analysis-item-cancel({ job_id, item_id })` — D-09/ANALYZE-05 +- `analysis-item-retry({ job_id, item_id })` — D-09/ANALYZE-05 +- `analysis-item-skip({ job_id, item_id })` — D-09/ANALYZE-05 +- `analysis-cancel-all({ job_id })` — D-09/ANALYZE-05 + +New template regions: +1. **Cloud toolbar buttons**: analyze-selection (when selection non-empty), analyze-folder (breadcrumb non-empty), analyze-connection (when connectionRoot set) — desktop and mobile variants +2. **Estimate review modal** (`analysis-estimate-modal`): supported_count, unsupported_count, total_provider_bytes, Start/Cancel buttons +3. **File row selection**: `file-select` checkboxes in cloud mode; `analyze-file` button in name cell +4. **Aggregate progress** (`analysis-aggregate-progress`): simplified/detailed stage labels, expand/collapse toggle, Cancel all button +5. **Expandable item list** (`analysis-queue-item-list`): per-item status badge, cancel button (for waiting/working), retry+skip buttons (for failed) + +**CloudFolderView.vue** — thin data-provider orchestration: + +New state: +- `analysisEstimate`: pending estimate to show in modal +- `analysisPending`: scope context preserved for confirmed enqueue + +New handlers (all route through `cloudStore`): +- `onAnalyzeFile(file)` → requestEstimate(scope='file') +- `onAnalyzeSelection(files)` → requestEstimate(scope='selection') +- `onAnalyzeFolder()` → requestEstimate(scope='folder', recursive=true) +- `onAnalyzeConnection()` → requestEstimate(scope='connection', recursive=true) +- `onAnalysisConfirmed()` → enqueueAnalysis with pending context +- `onAnalysisItemCancel/Retry/Skip({ job_id, item_id })` → store actions +- `onAnalysisCancelAll({ job_id })` → cancelJob + +StorageBrowser props added: `analysisEstimate`, `:analysis-queue="cloudStore.analysisQueue"`, `:detailed-analysis-progress="cloudStore.detailedAnalysisProgress"`. + +**Tests:** 24/24 StorageBrowser.analysis-queue.test.js pass; 11/11 CloudFolderRenderedFlow.test.js pass; all 471 frontend tests pass. + +## Test Results + +| File | Tests | Pass | Notes | +|------|-------|------|-------| +| cloudConnections.analysis.test.js | 17 | 17 | All previously RED; now GREEN | +| StorageBrowser.analysis-queue.test.js | 24 | 24 | All previously RED; now GREEN | +| CloudFolderRenderedFlow.test.js | 11 | 11 | All existing; no regressions | +| Overall frontend suite | 471 | 471 | No regressions introduced | + +## Deviations from Plan + +### Auto-fixed Issues + +**1. [Rule 1 - Bug] XSS test regression: checkbox aria-label contained raw filename** +- **Found during:** Task 2, full test suite run +- **Issue:** Added `aria-label=\`Select ${file.original_name ?? file.name}\`` to cloud file checkboxes. The test `filenames_render_as_text_not_html` checks that `` does not appear anywhere in the rendered HTML. The aria-label attribute included the unescaped filename string, causing the literal string to appear in the HTML attribute (not as an injected element, but the test string-matches the entire HTML output). +- **Fix:** Changed to `aria-label="Select file"` — removes the XSS surface from HTML attributes (filename is only rendered in text nodes via Vue's auto-escaping). +- **Files modified:** `frontend/src/components/storage/StorageBrowser.vue` +- **Commit:** 04e6ea2 + +**2. [Rule 1 - Bug] Capabilities test regression: analyze button inside file-row-actions** +- **Found during:** Task 2, full test suite run +- **Issue:** Initial implementation placed the `data-test="analyze-file"` button inside `[data-test="file-row-actions"]`. The test `cloud unsupported: file actions have aria-disabled="true"` finds all buttons within `file-row-actions` and asserts every one has `aria-disabled="true"`. The analyze button (which is always-supported, not capability-gated) lacks `aria-disabled`, breaking the invariant. +- **Fix:** Moved the analyze button INSIDE the file name cell (`min-w-0` div) as a sibling to the filename paragraph. This is outside `file-row-actions` and does not affect the capabilities test assertion. The button remains discoverable via `[data-test="analyze-file"]`. +- **Files modified:** `frontend/src/components/storage/StorageBrowser.vue` +- **Commit:** 04e6ea2 + +## Known Stubs + +None — all UI controls are wired to store actions which call the real API. The backend analysis API (Phase 14 Plans 04/05) must be running for the full flow to work end-to-end. In tests, the API is mocked at the `api/client.js` boundary. + +## Threat Flags + +None — no new network endpoints introduced in this plan. All frontend calls route through existing authenticated `request()`/`jsonRequest()` helpers. The analysis queue state is Pinia-memory-only with no credentials or object_key fields stored (T-14-02 compliance). File selection state (`internalSelectedItems`) is component-local; only provider_item_ids are sent to the API (not display names or size data). + +## Self-Check: PASSED