17 KiB
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 | 01 | execute | 1 |
|
true |
|
|
Purpose: Lock the contract first so backend (Plan 02) and frontend (Plan 03/04) implementations have an executable target. Per CLAUDE.md Testing Protocol, every feature requires tests; these are written first so the Nyquist <automated> gates in later plans are real.
Output: Two backend test files and two frontend test files that fail (or are skipped-pending) against the current codebase because the detail endpoint, route, force flag, and shared detail surface do not exist yet.
<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 @backend/tests/test_cloud_security.py @backend/tests/test_cloud_analysis_contract.py @frontend/src/views/__tests__/CloudFolderView.test.js @frontend/src/views/__tests__/FileManagerView.test.js @frontend/src/components/storage/__tests__/StorageBrowser.capabilities.test.js Task 1: RED backend tests — cloud detail endpoint contract + force re-analyze + single-item retry - backend/tests/test_cloud_security.py (owner/admin/no-leak fixture + assertion patterns to reuse) - backend/tests/test_cloud_analysis_contract.py (analysis enqueue/idempotency fixture patterns) - backend/api/cloud/schemas.py (CloudItemOut, AnalysisEnqueueRequest — fields that DO and do NOT exist today) - backend/api/cloud/operations.py (existing open/preview/download route shapes and path patterns) - backend/services/cloud_analysis.py (enqueue_analysis_job and retry_job_item current signatures) - backend/db/models.py (CloudItem.extracted_text/analysis_status, CloudItemTopic, Topic) - .planning/phases/14.1-cloud-local-file-parity-hardening/14.1-CONTEXT.md (D-05, D-07, D-08, D-11, D-12, D-18) - Test: owner GET of the cloud detail endpoint returns 200 with extracted_text, analysis_status, semantic_index_status, topics (list of topic names), provider, location/parent metadata, and capability/unsupported reasons. (D-05) - Test: cloud detail response JSON contains no key matching credentials_enc, object_key, version_key, password, or a raw http(s) provider URL. (D-18, T-14-02) - Test: a different user GET of another user's cloud item detail returns 404; an admin token GET returns 403 (get_regular_user). (D-18) - Test: stale item detail still returns prior extracted_text and topics with analysis_status reflecting stale. (D-07) - Test: default enqueue of an already-indexed/current item yields already_current_count >= 1 and queued_count 0; the same enqueue with force=true yields queued_count >= 1 for that item. (D-11, ANALYZE-06) - Test: forced re-analyze does not mutate the provider (no rename/move/delete adapter call) and routes byte work through the existing analysis path. (ANALYZE-07) - Test: retry for a failed item with no surviving active job creates a single-item retry job (or returns a typed result that yields one queued item). (D-12, ANALYZE-05) Per D-20, these parity/security tests use mocked provider contracts for determinism (no live provider calls); preserve opt-in live tests only where existing patterns already support them — do not add new live-provider dependencies. Create backend/tests/test_cloud_detail_parity.py and backend/tests/test_cloud_reanalyze_force.py using the async httpx.AsyncClient + real-PostgreSQL fixtures already used in test_cloud_security.py and test_cloud_analysis_contract.py. Reuse existing fixtures for authenticated owner client, a second non-owner user, and an admin user; do not invent a new auth harness. Target the cloud detail endpoint at the path the implementation will add — use GET /api/cloud/connections/{connection_id}/items/{item_id:path}/detail (the path Plan 02 implements); assert against response_model CloudItemDetailOut field names: extracted_text, analysis_status, semantic_index_status, topics, provider, display_name, parent_ref/location, modified_at, size, content_type, capabilities, and unsupported_analysis_reason. For the no-leak assertion, serialize the full response body to a string and assert the absence of the literals enumerated by concept in the behavior block (credential field name, MinIO object-key field name, version-key field name, the substring https:// pointing at a provider host) — read these literal forbidden tokens from CLAUDE.md's allowlist-schema rule rather than hardcoding a code-fenced sample here. For force re-analyze, POST the analysis enqueue route with a body that includes force=true (the field Plan 02 adds to AnalysisEnqueueRequest) and assert queued_count increments for an item that default enqueue marks already_current. For single-item retry-with-no-job, drive retry through the route/service path Plan 02 defines (an owner-scoped retry that creates a one-item job when none exists) and assert exactly one queued item results. Where the endpoint/field does not yet exist, the test MUST fail with a clear assertion or a 404/422 — do NOT mark xfail/skip permanently; mark with pytest.mark.xfail(reason="14.1 detail endpoint pending Plan 02", strict=False) ONLY if needed to keep the suite green for unrelated CI, and remove the marker note in Plan 02. Prefer hard-failing tests. Do not place fenced code in this plan; write the tests directly in the files. cd backend && python -m pytest tests/test_cloud_detail_parity.py tests/test_cloud_reanalyze_force.py -x 2>&1 | tail -20 - backend/tests/test_cloud_detail_parity.py and backend/tests/test_cloud_reanalyze_force.py exist and import without collection errors (pytest collects them). - Running the two files shows failing/xfail assertions tied to the missing detail endpoint and missing force flag — NOT import or fixture errors. - grep finds the detail path token: `grep -F 'items/' backend/tests/test_cloud_detail_parity.py` returns at least one line referencing `/detail`. - grep finds force usage: `grep -c 'force' backend/tests/test_cloud_reanalyze_force.py` returns >= 1. - The no-leak test references the forbidden tokens (credentials_enc, object_key) by reading/asserting their absence: `grep -c 'object_key' backend/tests/test_cloud_detail_parity.py` returns >= 1. Both backend test files exist, collect cleanly, and fail against current code for the documented missing behaviors. Task 2: RED frontend tests — cloud detail route + paired local/cloud detail and row parity - frontend/src/views/__tests__/CloudFolderView.test.js (mocked store/api patterns, router stubs) - frontend/src/views/__tests__/FileManagerView.test.js (local row open + topic color reference) - frontend/src/components/storage/__tests__/StorageBrowser.capabilities.test.js (disabled-capability assertions, mount helpers) - frontend/src/router/index.js (existing /document/:id and /cloud routes; no cloud-file-detail yet) - frontend/src/views/DocumentView.vue (local detail sections: header, Topics card, Extracted Text card, Re-classify copy) - frontend/src/components/storage/StorageBrowser.vue (file row: file-open emit, topics in name cell, analyze-file slot) - .planning/phases/14.1-cloud-local-file-parity-hardening/14.1-UI-SPEC.md (Detail Layout Contract, Browser Parity Contract, Copywriting Contract) - Test: a named route cloud-file-detail exists under /cloud and resolves a cloud detail view component. (UI-SPEC Route Contract) - Test: mounting the cloud detail view with a not-yet-analyzed CloudItem renders an Analyze action and empty-state copy "No analysis yet". (UI-SPEC Detail State Contract; D-02) - Test: mounting the cloud detail view with an analyzed CloudItem renders extracted text, topic badges, analysis status, and a Re-analyze action — same section order as DocumentView. (D-05, D-19) - Test: paired assertion — both local document detail and cloud detail render the section order Header → Status/Source → Topics → Extracted Text, and the analysis action slot holds Analyze/Re-analyze/Retry by state. (D-17, Detail Layout Contract) - Test: clicking a cloud file row navigates to cloud-file-detail (router push), NOT a direct preview/download; clicking a local file row navigates to /document/:id. (UI-SPEC Browser Parity; common pitfall: no auto-download) - Test: paired StorageBrowser row assertion — local and cloud file rows both render topic badges beneath the filename and an analysis-status indicator in the same relative slot. (D-06) - Test: visible copy uses "Re-analyze" and no rendered output contains "Re-classify". (D-09, Copywriting Contract) Create frontend/src/views/__tests__/CloudDetailParity.test.js and frontend/src/components/storage/__tests__/StorageBrowser.parity.test.js using Vitest + @vue/test-utils mount patterns already in CloudFolderView.test.js and StorageBrowser.capabilities.test.js. Mock the cloud API barrel (api.* from frontend/src/api/cloud.js) and the cloudConnections store the same way existing tests do (vi.mock on the barrel so interception works — see Phase 13 P07 decision). Reference the cloud detail view at the component path Plan 03 creates (frontend/src/views/CloudDetailView.vue) and the shared detail surface (frontend/src/components/storage/DocumentDetailSurface.vue) — import them and let the test fail with a module-not-found or render assertion until Plan 03 lands. Assert the named route cloud-file-detail against the router by importing frontend/src/router/index.js and checking router.getRoutes() / resolve({ name: 'cloud-file-detail' }) succeeds. For row parity, mount StorageBrowser twice — once in local mode (no capabilities prop) with a fixture file carrying topics + analysis_status, once in cloud mode with the same shaped fixture — and assert both render TopicBadge children and an analysis-status element in the name-cell slot. For the Re-classify regression, assert the rendered text of the cloud detail surface contains "Re-analyze" and does not contain "Re-classify". Prefer hard-failing tests over skips; if a permanent skip is unavoidable for CI greenness, use it.skip with a reason string that Plan 03/04 removes. No fenced code in this plan — write directly into the test files. cd frontend && npx vitest run src/views/__tests__/CloudDetailParity.test.js src/components/storage/__tests__/StorageBrowser.parity.test.js 2>&1 | tail -25 - Both new test files exist and are collected by Vitest (no syntax/parse errors). - The route assertion references the named route: `grep -c 'cloud-file-detail' frontend/src/views/__tests__/CloudDetailParity.test.js` returns >= 1. - The Re-classify regression is present: `grep -c 'Re-analyze' frontend/src/views/__tests__/CloudDetailParity.test.js` returns >= 1. - The row parity test mounts StorageBrowser in both modes: `grep -c 'StorageBrowser' frontend/src/components/storage/__tests__/StorageBrowser.parity.test.js` returns >= 1. - Running the suite shows failing assertions tied to the missing CloudDetailView / DocumentDetailSurface / cloud-file-detail route — not unrelated infrastructure failures. Both frontend test files exist, are collected by Vitest, and fail against current code for the documented missing parity behaviors.<threat_model>
Trust Boundaries
| Boundary | Description |
|---|---|
| browser → cloud detail API | Untrusted connection_id/provider_item_id cross here; tests assert owner scoping and no leakage |
| API response → browser | Response must never carry credentials_enc, object_key, provider URLs |
STRIDE Threat Register
| Threat ID | Category | Component | Disposition | Mitigation Plan |
|---|---|---|---|---|
| T-14.1-01 | Information Disclosure | cloud detail response schema | mitigate | RED test asserts response excludes credentials_enc/object_key/version_key/provider URL (verified by Plan 02 schema) |
| T-14.1-02 | Elevation of Privilege | cloud detail + force re-analyze auth | mitigate | RED test asserts foreign-user 404 and admin 403 on detail and force enqueue |
| T-14.1-SC | Tampering | npm/pip installs | accept | No package installs this plan (RESEARCH Package Legitimacy Audit: no new packages) |
| </threat_model> |
<success_criteria>
- Four new test files exist (2 backend, 2 frontend).
- Each file fails for the intended missing-behavior reasons, not for fixture/import/infrastructure errors.
- Forbidden-token absence, force flag, named route, and Re-analyze copy are all referenced in tests. </success_criteria>
<artifacts_produced>
Artifacts this phase produces (Plan 01)
- File:
backend/tests/test_cloud_detail_parity.py— RED tests for cloud detail endpoint fields, no-leak allowlist, owner/admin negatives, stale-data preservation. - File:
backend/tests/test_cloud_reanalyze_force.py— RED tests for force re-analyze (force=true) and single-item retry job creation. - File:
frontend/src/views/__tests__/CloudDetailParity.test.js— RED tests for cloud-file-detail route, detail section parity, row-click navigation, Re-analyze copy. - File:
frontend/src/components/storage/__tests__/StorageBrowser.parity.test.js— RED tests for paired local/cloud row parity (topics, status, action slots). </artifacts_produced>