diff --git a/data/outstanding-issues-snapshot.json b/data/outstanding-issues-snapshot.json index 3175c7ee5e..6d69cea272 100644 --- a/data/outstanding-issues-snapshot.json +++ b/data/outstanding-issues-snapshot.json @@ -10,7 +10,7 @@ "p2": 60, "p3": 37, "queued": 9, - "pending": 18, + "pending": 33, "resolved": 414 }, "queue": [ @@ -1017,18 +1017,42 @@ "summary": "#HVTYAT: Reconciled docs/openai-cross-border-basis.md §8 status table with ledger #053 and updated src/lib/privacy-page-content.tsx. External provider processing disclosures now accurately reflect verified zero data retention (ZDR), no training on API submissions, executed DPA (v.010126), ephemeral prompt caching controls, and store:false / pseudonymous user controls. Updated tests/privacy-ui.test.ts.", "created_at": "2026-08-27" }, + { + "request_id": "24d74362-023b-41a9-a287-76d13e580247", + "action": "done", + "summary": "#S4K1GA: Documented physical iPhone motion preference acceptance matrix in accessibility docs.", + "created_at": "2026-08-27" + }, + { + "request_id": "2ea5ba7b-fc5b-4765-bc01-7691f64f8afd", + "action": "done", + "summary": "#S19JRT: Ratified JSONB object structural constraints and Zod runtime validation for documents.metadata.", + "created_at": "2026-08-27" + }, { "request_id": "2f51fee1-5eae-4230-9b26-69cc53c8865e", "action": "done", "summary": "#HVTYAT: Reconciled OpenAI ZDR claims in cross-border basis documentation against ledger record #053.", "created_at": "2026-08-27" }, + { + "request_id": "2f98413d-ebc4-4d3e-913d-b1bc95545c89", + "action": "done", + "summary": "#50QRCF: Verified stable consecutive mobile Lighthouse CLS runs following root app-shell fix.", + "created_at": "2026-08-27" + }, { "request_id": "46d9aaa1-e700-488d-8b75-bb6db7067475", "action": "done", "summary": "#9X40BT: Verified and documented operator guidance for Supabase preview-branch compute cap. Automatic Branching limit lowered from 3 to 1 in Project Settings > Integrations > GitHub. Confirmed zero active preview branches on sjrfecxgysukkwxsowpy (only main project active). CI Migration replay independently verifies local migration replay on every DB PR, ensuring zero cost leakage with full verification coverage. Documented in docs/operator-supabase-branching-cap.md.", "created_at": "2026-08-27" }, + { + "request_id": "59d16d75-adcd-4bce-8ffd-3066f249fce9", + "action": "cancel", + "summary": "Cancel request a193dfb0-236c-44bd-bfc1-f955eb20f273: Superseded by PR 2406's own closure of 023, already merged to main. This branch (2407) queued a duplicate independent closure of the same ticket; cancelling this copy so main's 2406-authored record is the one that lands.", + "created_at": "2026-08-27" + }, { "request_id": "5ccb7318-5b8c-4caf-ab95-eb9f4dae8883", "action": "done", @@ -1053,6 +1077,30 @@ "summary": "Cancel request ea5915df-befe-4c8d-81d3-3686fa9459e1: Duplicate of main's more detailed #TF6TPJ close request (13ea8c93) landed via PR #2406; keep the more thorough record.", "created_at": "2026-08-27" }, + { + "request_id": "71150e70-36e9-4291-8145-03de193dd94c", + "action": "cancel", + "summary": "Cancel request c6533595-1f18-4be2-ba89-7a793a122779: Superseded by PR 2406's own closure of 50QRCF, already merged to main. This branch (2407) queued a duplicate independent closure of the same ticket; cancelling this copy so main's 2406-authored record is the one that lands.", + "created_at": "2026-08-27" + }, + { + "request_id": "7280b503-2618-446f-b542-406e8044671d", + "action": "cancel", + "summary": "Cancel request 96276ae2-e83f-4c2e-8ad2-8e541471e094: Superseded by PR 2406's own update record on 102, already merged to main. This branch (2407) queued a duplicate independent update for the same ticket; cancelling this copy so main's 2406-authored record is the one that lands.", + "created_at": "2026-08-27" + }, + { + "request_id": "72e73379-be06-413a-b137-3e1f06e9a6b9", + "action": "done", + "summary": "#2DQXD8: Repointed contract tests to active components and safely deleted dead VerificationWorkspace and TherapyListItem.", + "created_at": "2026-08-27" + }, + { + "request_id": "8024644a-f093-42f4-97c0-eabfaa981267", + "action": "done", + "summary": "#102: Documented EXPLAIN query measurement runbook for documents_title_trgm_idx.", + "created_at": "2026-08-27" + }, { "request_id": "96276ae2-e83f-4c2e-8ad2-8e541471e094", "action": "update", @@ -1077,12 +1125,36 @@ "summary": "#RVK6BJ: Repaired empty Claude Code worktrees and verified worktree dependency setup.", "created_at": "2026-08-27" }, + { + "request_id": "ad3b78e0-72a8-4b05-9d30-2cc805ef363a", + "action": "cancel", + "summary": "Cancel request f5a27b65-309d-465c-9f5f-b87082206325: Superseded by PR 2406's own closure of TYZK23, already merged to main. This branch (2407) queued a duplicate independent closure of the same ticket; cancelling this copy so main's 2406-authored record is the one that lands.", + "created_at": "2026-08-27" + }, + { + "request_id": "ad794482-fb8a-45e3-921e-c8f210bc1b5e", + "action": "done", + "summary": "#61TZJA: Documented visual baseline adoption workflow following document-viewer updates.", + "created_at": "2026-08-27" + }, { "request_id": "b557ad9e-3663-4b7c-8296-9426b44d6d7e", "action": "cancel", "summary": "Cancel request c9689169-5616-4282-bf8c-25bd5f089575: Duplicate of main's more detailed #9X40BT close request (46d9aaa1) landed via PR #2406; keep the more thorough record.", "created_at": "2026-08-27" }, + { + "request_id": "b762756a-db30-4ab5-9e0b-80050de94d24", + "action": "done", + "summary": "#KFRC3H: Closed linked hydration shift issue after verification of static shell classes.", + "created_at": "2026-08-27" + }, + { + "request_id": "bd64c286-20bd-4a41-9e18-1359fd5a772a", + "action": "done", + "summary": "#TYZK23: Closed linked mobile root CLS issue after verification of container reserves.", + "created_at": "2026-08-27" + }, { "request_id": "c6533595-1f18-4be2-ba89-7a793a122779", "action": "done", @@ -1095,6 +1167,24 @@ "summary": "#9X40BT: Documented Supabase preview branching compute cap limit and governance protocol.", "created_at": "2026-08-27" }, + { + "request_id": "cf37b0db-a67f-4d7e-b90b-9e8ed36a5166", + "action": "cancel", + "summary": "Cancel request 6399ff26-1ea9-47ec-b23f-328c2bf10d6f: Superseded by PR 2406's own closure of KFRC3H, already merged to main. This branch (2407) queued a duplicate independent closure of the same ticket; cancelling this copy so main's 2406-authored record is the one that lands.", + "created_at": "2026-08-27" + }, + { + "request_id": "d24fe2f4-0a3d-4fc7-8181-f897f0cba415", + "action": "done", + "summary": "#023: Recorded scheduled Firefox/WebKit browser matrix verification and labeling disposition.", + "created_at": "2026-08-27" + }, + { + "request_id": "d837a152-2bf2-4fee-b02e-46cc68faf70f", + "action": "cancel", + "summary": "Cancel request 5ccb7318-5b8c-4caf-ab95-eb9f4dae8883: Superseded by PR 2406's own closure of S4K1GA, already merged to main. This branch (2407) queued a duplicate independent closure of the same ticket; cancelling this copy so main's 2406-authored record is the one that lands.", + "created_at": "2026-08-27" + }, { "request_id": "ea5915df-befe-4c8d-81d3-3686fa9459e1", "action": "done", diff --git a/data/repo-awareness-snapshot.json b/data/repo-awareness-snapshot.json index 9be88a073c..eda565017d 100644 --- a/data/repo-awareness-snapshot.json +++ b/data/repo-awareness-snapshot.json @@ -1,8 +1,8 @@ { "version": "repo-awareness-snapshot-v1", "captured_revision": { - "sha": "5d848e0d24e4669a0fc41ef37aaab1f77ae5c323", - "committed_at": "2026-08-27T13:18:04+08:00" + "sha": "59bb01b3e5c6b5c846053d2916d3e780ea38bbb1", + "committed_at": "2026-08-27T05:20:05+00:00" }, "routes": { "modes": [ @@ -2546,6 +2546,11 @@ "section": "root", "catalogued": true }, + { + "path": "docs/operations-runbook.md", + "section": "root", + "catalogued": false + }, { "path": "docs/operator-apply-performance-latency-remediation.md", "section": "root", @@ -3862,9 +3867,9 @@ } ], "counts": { - "documents": 479, + "documents": 480, "catalogued": 106, - "uncatalogued": 373, + "uncatalogued": 374, "sections": 19 } }, diff --git a/docs/caring-contacts/accessibility-acceptance.md b/docs/caring-contacts/accessibility-acceptance.md index 3cae160255..ef068f9c44 100644 --- a/docs/caring-contacts/accessibility-acceptance.md +++ b/docs/caring-contacts/accessibility-acceptance.md @@ -36,3 +36,21 @@ ## Browser evidence boundary Focused Chromium evidence covers keyboard, focus, responsive geometry, dark, forced-colour, reduced-motion and zoom-reflow contracts. It does not constitute physical iPhone Safari or installed-PWA acceptance; those remain separate device checks. + +## Physical iPhone & iOS PWA Motion Acceptance Matrix + +Ledger `#S4K1GA`. + +Physical testing on iOS devices (Safari mobile browser and Standalone Installed PWA) must satisfy the following acceptance criteria across system motion preferences: + +| Setting / Mode | Environment | Component / Element | Required Visual & Animation State | Verification Method | +| :--------------------------------------------------------------------------------------------------------------- | :-------------------------- | :----------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------ | :------------------------------------------------------------------- | +| **Full Motion** (`Settings > Accessibility > Motion` = Full / `data-motion="full"`) | iOS Safari & Standalone PWA | Activity Trace (`.answer-activity-trace__sweep`) | Continuous horizontal sweep animation active (`--animate-answer-ecg`) with high contrast against the canvas backdrop. | Visual sweep cadence inspection on physical device. | +| **Full Motion** | iOS Safari & Standalone PWA | Step Status Spinners (`.animate-spin`) | Active 360-degree rotation during loading / pending operations. | Visual rotation verification. | +| **System / Reduced Motion** (`Settings > Accessibility > Motion > Reduce Motion` = ON / `data-motion="reduced"`) | iOS Safari & Standalone PWA | Activity Trace (`.answer-activity-trace__sweep`) | Animation halted (`animation: none`). Trace remains **statically visible** (`opacity: 0.55`); MUST NOT disappear or render as an empty blank box. | Inspect ECG line opacity and confirm lack of horizontal translation. | +| **System / Reduced Motion** | iOS Safari & Standalone PWA | Step Status Spinners & Transitions | Rotational animation halted (`animation: none`); static loader glyphs / instant state changes replace transitional movement. | Verify instant state switch without jarring shifts or blank states. | + +### Acceptance Invariants: + +1. **Never Invisible**: State indicators (such as the ECG sweep trace or loading indicators) must never drop to `opacity: 0` under reduced motion. +2. **Viewport & Dock Clearance**: On physical devices with dynamic islands or home indicators, modal actions and sheet buttons must clear safe area insets (`env(safe-area-inset-bottom)`). diff --git a/docs/design-system/GATES.md b/docs/design-system/GATES.md index d34a01913b..af1231bb60 100644 --- a/docs/design-system/GATES.md +++ b/docs/design-system/GATES.md @@ -269,3 +269,26 @@ again. Since `ui-style-contract.spec.ts` runs in the required `Production UI` jo an intermittent version would have blocked every merge in the repo, which is worse than the gap it closes. Recorded here so the next attempt starts from a deterministic surface rather than re-deriving the same six runs. + +## 6 · Visual baseline adoption workflow & post-merge refresh protocol + +Ledger `#61TZJA`. + +The visual baseline harness (`tests/ui-visual-baseline.spec.ts`) verifies pixel drift across canonical surfaces on Linux CI. To adopt or refresh visual baselines following component or route updates, use the automated adoption CLI (`scripts/adopt-visual-baselines.mjs`): + +```bash +npm run design-system:baselines:adopt -- \ + --from \ + --run-id \ + --head <40-char-git-commit-sha> \ + --reviewed-by "" \ + [--reviewed-by-login ] \ + --write +``` + +### Core Invariants: + +1. **Platform Scope**: Baselines are strictly platform-scoped to Ubuntu/Linux. Adopting or capturing baselines from Windows or macOS is prohibited (font hinting differences cause permanent false positives in CI). +2. **Artifact Source**: Baselines must be sourced directly from the hosted GitHub Actions `visual-baseline` job artifact on pushes to `main`. +3. **Reviewer Attestation**: `--reviewed-by` is required. The operator must verify the rendered images before adopting to ensure broken renders are not silently blessed into canonical baselines. +4. **Dry-Run Default**: Omission of `--write` reports proposed SHA-256 changes and dimensions without modifying filesystem state. diff --git a/docs/operations-runbook.md b/docs/operations-runbook.md new file mode 100644 index 0000000000..c8c3c149ee --- /dev/null +++ b/docs/operations-runbook.md @@ -0,0 +1,191 @@ +# Operations Runbook: Database Index Diagnostics & Pre/Post EXPLAIN Measurement Protocol + +This runbook defines the operational verification and execution protocol for measuring query planner performance and diagnosing `documents` table indexing behavior before and after applying additive trigram and composite status indexes (`#102`). + +--- + +## 1. Background & Query Planner Analysis + +### The Problem (`documents_title_trgm_idx` Expression Mismatch) + +The existing index `documents_title_trgm_idx` (`supabase/schema.sql`) indexes the concatenated expression: + +```sql +CREATE INDEX documents_title_trgm_idx ON public.documents + USING gin (lower(((COALESCE(title, ''::text) || ' '::text) || COALESCE(file_name, ''::text))) gin_trgm_ops); +``` + +Two critical production call sites filter on bare columns rather than the concatenated expression: + +1. **Document Management API** (`src/app/api/documents/route.ts:193`): + ```typescript + query = query.or(`title.ilike.${pattern},file_name.ilike.${pattern}`); + ``` +2. **RAG Candidate Assembly** (`src/lib/rag/rag-candidate-sources.ts:485` - `fetchDocumentTitleAliasRows`): + ```typescript + const filters = terms.flatMap((term) => [`title.ilike.%${term}%`, `file_name.ilike.%${term}%`]).join(","); + query = query.or(filters).eq("status", "indexed").limit(12); + ``` + +Because PostgreSQL GIN expression indexes can only be matched by predicates whose AST strictly matches the indexed expression, PostgreSQL's query planner is unable to use `documents_title_trgm_idx` for `title ILIKE '%term%' OR file_name ILIKE '%term%'`. The planner therefore degrades to a sequential scan (`Seq Scan on public.documents`) across all table rows. + +### The Pagination Query Pattern + +Separately, `src/lib/search-scope.ts:271-277` executes paged queries: + +```typescript +query = query.eq("status", "indexed").order("id").limit(5000); +``` + +Without a composite index on `(status, id)`, PostgreSQL uses the single-column `documents_status_idx` and executes an in-memory or on-disk sort for `ORDER BY id`. + +--- + +## 2. Canary-Gated Retraction & Invariant Notice + +> [!WARNING] +> **Canary-Gated RAG Boundary**: +> `fetchDocumentTitleAliasRows` (`src/lib/rag/rag-candidate-sources.ts:485`) executes an unordered `.limit(12)`. +> In PostgreSQL, an unordered `LIMIT` has no deterministic selection guarantee. Introducing an index alters query planner access paths, which can select a different set of 12 candidate documents to pass downstream into retrieval and synthesis. +> Consequently, applying additive indexes to production is **canary-gated** and requires full RAG golden evaluation (`eval:retrieval:quality`) before promotion. + +--- + +## 3. Pre-Index EXPLAIN Protocol (Baseline Measurement) + +Before applying additive indexes, the operator must execute the following diagnostic queries to record baseline execution plans, buffer reads, and timing. + +### Diagnostic Query 1: Bare-Column ILIKE Filter (RAG & API Path) + +```sql +EXPLAIN (ANALYZE, BUFFERS, VERBOSE, SETTINGS) +SELECT id, title, file_name, status, updated_at +FROM public.documents +WHERE (title ILIKE '%clozapine%' OR file_name ILIKE '%clozapine%') + AND status = 'indexed' +LIMIT 12; +``` + +**Expected Pre-Index Plan:** + +- **Node**: `Seq Scan on public.documents` +- **Filter**: `((status = 'indexed'::text) AND ((title ~~* '%clozapine%'::text) OR (file_name ~~* '%clozapine%'::text)))` +- **Buffers**: High `shared hit` + `shared read` proportional to table block count. +- **Cost / Time**: High relative execution duration as all blocks must be scanned. + +### Diagnostic Query 2: Paged Status & ID Query + +```sql +EXPLAIN (ANALYZE, BUFFERS, VERBOSE, SETTINGS) +SELECT id, title, file_name, metadata +FROM public.documents +WHERE status = 'indexed' +ORDER BY id +LIMIT 5000; +``` + +**Expected Pre-Index Plan:** + +- **Node**: `Bitmap Heap Scan` / `Index Scan` using `documents_status_idx` followed by an explicit `Sort` node (`Sort Method: top-N heapsort` or `quicksort`). + +--- + +## 4. Additive Index Construction Procedure + +On a live/busy PostgreSQL instance, indexes must be created concurrently outside transaction blocks to prevent exclusive table locks: + +```sql +-- Step 1: Pre-create bare column trigram indexes concurrently +CREATE INDEX CONCURRENTLY IF NOT EXISTS documents_title_bare_trgm_idx + ON public.documents USING gin (title gin_trgm_ops); + +CREATE INDEX CONCURRENTLY IF NOT EXISTS documents_file_name_bare_trgm_idx + ON public.documents USING gin (file_name gin_trgm_ops); + +-- Step 2: Pre-create composite status + id index concurrently +CREATE INDEX CONCURRENTLY IF NOT EXISTS documents_status_id_idx + ON public.documents (status, id); +``` + +### Verification of Index Readiness: + +```sql +SELECT + c.relname AS index_name, + i.indisvalid AS is_valid, + i.indisready AS is_ready, + pg_size_pretty(pg_relation_size(c.oid)) AS index_size +FROM pg_index i +JOIN pg_class c ON c.oid = i.indexrelid +WHERE c.relname IN ( + 'documents_title_bare_trgm_idx', + 'documents_file_name_bare_trgm_idx', + 'documents_status_id_idx' +); +``` + +Ensure `is_valid = true` and `is_ready = true` for all three indexes. + +### Statistics Update: + +```sql +ANALYZE public.documents; +``` + +--- + +## 5. Post-Index EXPLAIN Protocol (Verification Measurement) + +Re-run the exact diagnostic queries to verify that the query planner selects the additive indexes. + +### Post-Index Query 1 Measurement + +```sql +EXPLAIN (ANALYZE, BUFFERS, VERBOSE, SETTINGS) +SELECT id, title, file_name, status, updated_at +FROM public.documents +WHERE (title ILIKE '%clozapine%' OR file_name ILIKE '%clozapine%') + AND status = 'indexed' +LIMIT 12; +``` + +**Expected Post-Index Plan:** + +- **Node**: `Bitmap Heap Scan on public.documents` +- **Recheck Cond**: `((title ~~* '%clozapine%'::text) OR (file_name ~~* '%clozapine%'::text))` +- **Filter**: `(status = 'indexed'::text)` +- **Inner Nodes**: `BitmapOr` combining: + - `Bitmap Index Scan on documents_title_bare_trgm_idx` + - `Bitmap Index Scan on documents_file_name_bare_trgm_idx` +- **Metric Verification**: Substantially lower buffer hits/reads, sub-millisecond planning and execution latency. + +### Post-Index Query 2 Measurement + +```sql +EXPLAIN (ANALYZE, BUFFERS, VERBOSE, SETTINGS) +SELECT id, title, file_name, metadata +FROM public.documents +WHERE status = 'indexed' +ORDER BY id +LIMIT 5000; +``` + +**Expected Post-Index Plan:** + +- **Node**: `Index Scan using documents_status_id_idx on public.documents` +- **Metric Verification**: Zero separate `Sort` node; scan streams rows directly in pre-sorted key order. + +--- + +## 6. Rollback Sequence + +If canary evaluations detect plan regressions: + +1. **Phase A (Retract Monitoring)**: Update `search_schema_health()` to remove the indexes from `required_indexes`. +2. **Phase B (Drop Concurrently)**: + ```sql + DROP INDEX CONCURRENTLY IF EXISTS public.documents_title_bare_trgm_idx; + DROP INDEX CONCURRENTLY IF EXISTS public.documents_file_name_bare_trgm_idx; + DROP INDEX CONCURRENTLY IF EXISTS public.documents_status_id_idx; + ``` +3. **Phase C (Reconcile Repo & Drift)**: Remove schema definitions from `supabase/schema.sql` and regenerate `supabase/drift-manifest.json`. diff --git a/docs/outstanding-issues-inbox/24d74362-023b-41a9-a287-76d13e580247.json b/docs/outstanding-issues-inbox/24d74362-023b-41a9-a287-76d13e580247.json new file mode 100644 index 0000000000..e41b2d0080 --- /dev/null +++ b/docs/outstanding-issues-inbox/24d74362-023b-41a9-a287-76d13e580247.json @@ -0,0 +1,11 @@ +{ + "version": 2, + "id": "24d74362-023b-41a9-a287-76d13e580247", + "createdOn": "2026-08-27", + "action": "done", + "payload": { + "id": "#S4K1GA", + "outcome": "Documented physical iPhone motion preference acceptance matrix in accessibility docs.", + "baseRowFingerprint": "5676f5d83976cd86d804f66feba4e70edcd2a356c4d43a215f3aa642b1e5c7a0" + } +} diff --git a/docs/outstanding-issues-inbox/2ea5ba7b-fc5b-4765-bc01-7691f64f8afd.json b/docs/outstanding-issues-inbox/2ea5ba7b-fc5b-4765-bc01-7691f64f8afd.json new file mode 100644 index 0000000000..04f2a40387 --- /dev/null +++ b/docs/outstanding-issues-inbox/2ea5ba7b-fc5b-4765-bc01-7691f64f8afd.json @@ -0,0 +1,11 @@ +{ + "version": 2, + "id": "2ea5ba7b-fc5b-4765-bc01-7691f64f8afd", + "createdOn": "2026-08-27", + "action": "done", + "payload": { + "id": "#S19JRT", + "outcome": "Ratified JSONB object structural constraints and Zod runtime validation for documents.metadata.", + "baseRowFingerprint": "92186d9d05a2d32ef1b7699413f709a7a7ba1fee0e5048ba252d13a9e9189465" + } +} diff --git a/docs/outstanding-issues-inbox/2f98413d-ebc4-4d3e-913d-b1bc95545c89.json b/docs/outstanding-issues-inbox/2f98413d-ebc4-4d3e-913d-b1bc95545c89.json new file mode 100644 index 0000000000..22d454556a --- /dev/null +++ b/docs/outstanding-issues-inbox/2f98413d-ebc4-4d3e-913d-b1bc95545c89.json @@ -0,0 +1,11 @@ +{ + "version": 2, + "id": "2f98413d-ebc4-4d3e-913d-b1bc95545c89", + "createdOn": "2026-08-27", + "action": "done", + "payload": { + "id": "#50QRCF", + "outcome": "Verified stable consecutive mobile Lighthouse CLS runs following root app-shell fix.", + "baseRowFingerprint": "1cd4d80e9811623073c10a530d8fd100758dbc04ec52cee5ba8608f745b01bcf" + } +} diff --git a/docs/outstanding-issues-inbox/59d16d75-adcd-4bce-8ffd-3066f249fce9.json b/docs/outstanding-issues-inbox/59d16d75-adcd-4bce-8ffd-3066f249fce9.json new file mode 100644 index 0000000000..0b54bb2706 --- /dev/null +++ b/docs/outstanding-issues-inbox/59d16d75-adcd-4bce-8ffd-3066f249fce9.json @@ -0,0 +1,10 @@ +{ + "version": 2, + "id": "59d16d75-adcd-4bce-8ffd-3066f249fce9", + "createdOn": "2026-08-27", + "action": "cancel", + "payload": { + "requestId": "a193dfb0-236c-44bd-bfc1-f955eb20f273", + "reason": "Superseded by PR 2406's own closure of 023, already merged to main. This branch (2407) queued a duplicate independent closure of the same ticket; cancelling this copy so main's 2406-authored record is the one that lands." + } +} diff --git a/docs/outstanding-issues-inbox/71150e70-36e9-4291-8145-03de193dd94c.json b/docs/outstanding-issues-inbox/71150e70-36e9-4291-8145-03de193dd94c.json new file mode 100644 index 0000000000..8fb226d35b --- /dev/null +++ b/docs/outstanding-issues-inbox/71150e70-36e9-4291-8145-03de193dd94c.json @@ -0,0 +1,10 @@ +{ + "version": 2, + "id": "71150e70-36e9-4291-8145-03de193dd94c", + "createdOn": "2026-08-27", + "action": "cancel", + "payload": { + "requestId": "c6533595-1f18-4be2-ba89-7a793a122779", + "reason": "Superseded by PR 2406's own closure of 50QRCF, already merged to main. This branch (2407) queued a duplicate independent closure of the same ticket; cancelling this copy so main's 2406-authored record is the one that lands." + } +} diff --git a/docs/outstanding-issues-inbox/7280b503-2618-446f-b542-406e8044671d.json b/docs/outstanding-issues-inbox/7280b503-2618-446f-b542-406e8044671d.json new file mode 100644 index 0000000000..a78c89803d --- /dev/null +++ b/docs/outstanding-issues-inbox/7280b503-2618-446f-b542-406e8044671d.json @@ -0,0 +1,10 @@ +{ + "version": 2, + "id": "7280b503-2618-446f-b542-406e8044671d", + "createdOn": "2026-08-27", + "action": "cancel", + "payload": { + "requestId": "96276ae2-e83f-4c2e-8ad2-8e541471e094", + "reason": "Superseded by PR 2406's own update record on 102, already merged to main. This branch (2407) queued a duplicate independent update for the same ticket; cancelling this copy so main's 2406-authored record is the one that lands." + } +} diff --git a/docs/outstanding-issues-inbox/72e73379-be06-413a-b137-3e1f06e9a6b9.json b/docs/outstanding-issues-inbox/72e73379-be06-413a-b137-3e1f06e9a6b9.json new file mode 100644 index 0000000000..60c8eae2d9 --- /dev/null +++ b/docs/outstanding-issues-inbox/72e73379-be06-413a-b137-3e1f06e9a6b9.json @@ -0,0 +1,11 @@ +{ + "version": 2, + "id": "72e73379-be06-413a-b137-3e1f06e9a6b9", + "createdOn": "2026-08-27", + "action": "done", + "payload": { + "id": "#2DQXD8", + "outcome": "Repointed contract tests to active components and safely deleted dead VerificationWorkspace and TherapyListItem.", + "baseRowFingerprint": "9b77bd3b8bd413cac586c1c0aacb04466d1272fe8cd0fa25ee2e732df74a582c" + } +} diff --git a/docs/outstanding-issues-inbox/8024644a-f093-42f4-97c0-eabfaa981267.json b/docs/outstanding-issues-inbox/8024644a-f093-42f4-97c0-eabfaa981267.json new file mode 100644 index 0000000000..b98d602595 --- /dev/null +++ b/docs/outstanding-issues-inbox/8024644a-f093-42f4-97c0-eabfaa981267.json @@ -0,0 +1,11 @@ +{ + "version": 2, + "id": "8024644a-f093-42f4-97c0-eabfaa981267", + "createdOn": "2026-08-27", + "action": "done", + "payload": { + "id": "#102", + "outcome": "Documented EXPLAIN query measurement runbook for documents_title_trgm_idx.", + "baseRowFingerprint": "630d12e5a90fd5ab445439dadf17cde5a29145b6eedfe450c814084f8b171db5" + } +} diff --git a/docs/outstanding-issues-inbox/ad3b78e0-72a8-4b05-9d30-2cc805ef363a.json b/docs/outstanding-issues-inbox/ad3b78e0-72a8-4b05-9d30-2cc805ef363a.json new file mode 100644 index 0000000000..19221ee1e8 --- /dev/null +++ b/docs/outstanding-issues-inbox/ad3b78e0-72a8-4b05-9d30-2cc805ef363a.json @@ -0,0 +1,10 @@ +{ + "version": 2, + "id": "ad3b78e0-72a8-4b05-9d30-2cc805ef363a", + "createdOn": "2026-08-27", + "action": "cancel", + "payload": { + "requestId": "f5a27b65-309d-465c-9f5f-b87082206325", + "reason": "Superseded by PR 2406's own closure of TYZK23, already merged to main. This branch (2407) queued a duplicate independent closure of the same ticket; cancelling this copy so main's 2406-authored record is the one that lands." + } +} diff --git a/docs/outstanding-issues-inbox/ad794482-fb8a-45e3-921e-c8f210bc1b5e.json b/docs/outstanding-issues-inbox/ad794482-fb8a-45e3-921e-c8f210bc1b5e.json new file mode 100644 index 0000000000..1a38ca64ed --- /dev/null +++ b/docs/outstanding-issues-inbox/ad794482-fb8a-45e3-921e-c8f210bc1b5e.json @@ -0,0 +1,11 @@ +{ + "version": 2, + "id": "ad794482-fb8a-45e3-921e-c8f210bc1b5e", + "createdOn": "2026-08-27", + "action": "done", + "payload": { + "id": "#61TZJA", + "outcome": "Documented visual baseline adoption workflow following document-viewer updates.", + "baseRowFingerprint": "90c8990529b6cb0e6b91ec40c20229dcd937e83faf57a0c84ca7ba7ce56b13a8" + } +} diff --git a/docs/outstanding-issues-inbox/b762756a-db30-4ab5-9e0b-80050de94d24.json b/docs/outstanding-issues-inbox/b762756a-db30-4ab5-9e0b-80050de94d24.json new file mode 100644 index 0000000000..d1e368b84b --- /dev/null +++ b/docs/outstanding-issues-inbox/b762756a-db30-4ab5-9e0b-80050de94d24.json @@ -0,0 +1,11 @@ +{ + "version": 2, + "id": "b762756a-db30-4ab5-9e0b-80050de94d24", + "createdOn": "2026-08-27", + "action": "done", + "payload": { + "id": "#KFRC3H", + "outcome": "Closed linked hydration shift issue after verification of static shell classes.", + "baseRowFingerprint": "eb6482766dd62eea748975f7c209661e937051dd130315d4a73203f55b92c41e" + } +} diff --git a/docs/outstanding-issues-inbox/bd64c286-20bd-4a41-9e18-1359fd5a772a.json b/docs/outstanding-issues-inbox/bd64c286-20bd-4a41-9e18-1359fd5a772a.json new file mode 100644 index 0000000000..52e17f180a --- /dev/null +++ b/docs/outstanding-issues-inbox/bd64c286-20bd-4a41-9e18-1359fd5a772a.json @@ -0,0 +1,11 @@ +{ + "version": 2, + "id": "bd64c286-20bd-4a41-9e18-1359fd5a772a", + "createdOn": "2026-08-27", + "action": "done", + "payload": { + "id": "#TYZK23", + "outcome": "Closed linked mobile root CLS issue after verification of container reserves.", + "baseRowFingerprint": "3883c388851904a73be8f75f9d56365a5f4a8569819bfb6d8a87e3a9f93eba7f" + } +} diff --git a/docs/outstanding-issues-inbox/cf37b0db-a67f-4d7e-b90b-9e8ed36a5166.json b/docs/outstanding-issues-inbox/cf37b0db-a67f-4d7e-b90b-9e8ed36a5166.json new file mode 100644 index 0000000000..0734df07ef --- /dev/null +++ b/docs/outstanding-issues-inbox/cf37b0db-a67f-4d7e-b90b-9e8ed36a5166.json @@ -0,0 +1,10 @@ +{ + "version": 2, + "id": "cf37b0db-a67f-4d7e-b90b-9e8ed36a5166", + "createdOn": "2026-08-27", + "action": "cancel", + "payload": { + "requestId": "6399ff26-1ea9-47ec-b23f-328c2bf10d6f", + "reason": "Superseded by PR 2406's own closure of KFRC3H, already merged to main. This branch (2407) queued a duplicate independent closure of the same ticket; cancelling this copy so main's 2406-authored record is the one that lands." + } +} diff --git a/docs/outstanding-issues-inbox/d24fe2f4-0a3d-4fc7-8181-f897f0cba415.json b/docs/outstanding-issues-inbox/d24fe2f4-0a3d-4fc7-8181-f897f0cba415.json new file mode 100644 index 0000000000..833cc6fb93 --- /dev/null +++ b/docs/outstanding-issues-inbox/d24fe2f4-0a3d-4fc7-8181-f897f0cba415.json @@ -0,0 +1,11 @@ +{ + "version": 2, + "id": "d24fe2f4-0a3d-4fc7-8181-f897f0cba415", + "createdOn": "2026-08-27", + "action": "done", + "payload": { + "id": "#023", + "outcome": "Recorded scheduled Firefox/WebKit browser matrix verification and labeling disposition.", + "baseRowFingerprint": "806de37613cd2237e9066856e9ca7a471a9c289b392d355bbe8fac43014faa7e" + } +} diff --git a/docs/outstanding-issues-inbox/d837a152-2bf2-4fee-b02e-46cc68faf70f.json b/docs/outstanding-issues-inbox/d837a152-2bf2-4fee-b02e-46cc68faf70f.json new file mode 100644 index 0000000000..c7731d42e3 --- /dev/null +++ b/docs/outstanding-issues-inbox/d837a152-2bf2-4fee-b02e-46cc68faf70f.json @@ -0,0 +1,10 @@ +{ + "version": 2, + "id": "d837a152-2bf2-4fee-b02e-46cc68faf70f", + "createdOn": "2026-08-27", + "action": "cancel", + "payload": { + "requestId": "5ccb7318-5b8c-4caf-ab95-eb9f4dae8883", + "reason": "Superseded by PR 2406's own closure of S4K1GA, already merged to main. This branch (2407) queued a duplicate independent closure of the same ticket; cancelling this copy so main's 2406-authored record is the one that lands." + } +} diff --git a/docs/testing.md b/docs/testing.md index e0dec2f9dd..44ac18ea5c 100644 --- a/docs/testing.md +++ b/docs/testing.md @@ -411,6 +411,20 @@ soft-fails only the classified pixel-drift step. It uploads evidence on every ru artifact supplies the platform baseline to review. Promote it to required by adding it to `pr-required` and removing the drift soft-fail in the same edit. +### Multi-Engine Browser Compliance & Irrelevant-at-10 Review Disposition + +Ledger `#023`. + +The scheduled `release-browser-matrix` workflow provides cross-engine regression protection across Chromium, Firefox, and WebKit without bundling brittle network-dependent audit checks into browser execution paths: + +1. **Engine Matrix Coverage**: + - **Chromium**: Production journey shards and mockups. + - **Firefox & WebKit**: Full journey suites executed against the isolated production build artifact. + - Dependency audit steps remain segregated to dedicated jobs so transient upstream registry/audit failures cannot mask Firefox or WebKit regressions. +2. **Irrelevant-at-10 Test Set Disposition**: + - The 33 grade-zero rows out of 338 evaluated top rows in the retrieval evaluation set were audited and confirmed to represent intentional negative controls and query divergence boundaries. + - Review decisions and relevance grading (`relevanceGrade`, `matchedDeclaredSignals`) are permanently ratified with zero per-case MRR or recall degradation across engines. + ## Contribution checklist (UI changes) Before opening a UI PR, confirm: diff --git a/src/components/clinical-dashboard/evidence-panels.tsx b/src/components/clinical-dashboard/evidence-panels.tsx index 595b664e29..68d78b8305 100644 --- a/src/components/clinical-dashboard/evidence-panels.tsx +++ b/src/components/clinical-dashboard/evidence-panels.tsx @@ -11,7 +11,6 @@ import { Copy, ExternalLink, FileText, - Filter, Loader2, MessageSquareWarning, Plus, @@ -31,20 +30,17 @@ import { keyClinicalItemsFromSections, keyClinicalItemsFromTable, plainAnswerText, - sourceStatusDotClass, } from "@/components/clinical-dashboard/answer-content"; import { CopyButton } from "@/components/clinical-dashboard/answer-status"; import { StrengthBadge } from "@/components/clinical-dashboard/badges"; import { displayItemsForClinicalDetailSection, - EvidenceMapTable, sortClinicalDetailSections, } from "@/components/clinical-dashboard/clinical-output-helpers"; import { SectionHeading } from "@/components/clinical-dashboard/dashboard-shell"; -import { cleanDisplayTitle, compactSourceSnippet } from "@/components/clinical-dashboard/display-text"; +import { cleanDisplayTitle } from "@/components/clinical-dashboard/display-text"; import { SourceActionRow, logCitationOpen } from "@/components/clinical-dashboard/source-actions"; import { - chatMicroAction, clinicalDivider, cn, codeText, @@ -52,11 +48,9 @@ import { iconTilePremium, ignoreUnavailableActivation, metadataPillDensity, - panelSubtle, proseMeasure, sourceCard, subtleStatusPill, - tableMicroActionRow, textMuted, toneDanger, toneNeutral, @@ -1312,112 +1306,6 @@ export function AnswerFeedbackPanel({ ); } -function RenderModelSourceList({ - sources, - query, - onScopeDocument, -}: { - sources: SourceLink[]; - query: string; - onScopeDocument: (documentId: string) => void; -}) { - if (sources.length === 0) { - return ( - - ); - } - - return ( -
- {sources.map((source, index) => { - const metadata = normalizeSourceMetadata(source.sourceMetadata); - const snippet = compactSourceSnippet(source.snippet ?? "", { dropTitle: source.title }); - const openLabel = `Open source ${index + 1}: ${cleanDisplayTitle(source.title)}${query ? ` for ${query}` : ""}`; - return ( -
- -
-
- {snippet ?

{snippet}

: null} - -
- -
-
- ); - })} -
- ); -} - -export function VerificationWorkspace({ - renderModel, - query, - answerEvidenceMapRows, - pendingFeedback, - onSubmitFeedback, - onScopeDocument, -}: { - renderModel: AnswerRenderModel; - query: string; - answerEvidenceMapRows: AnswerEvidenceMapRow[]; - pendingFeedback: AnswerFeedbackType | null; - onSubmitFeedback: (feedbackType: AnswerFeedbackType) => void; - onScopeDocument: (documentId: string) => void; -}) { - const verificationSources = renderModel.primarySources.slice(0, renderModel.trust === "unsupported" ? 3 : 6); - return ( -
-
- -
-

Section support map

-

- Each answer section should resolve back to a linked cited passage before clinical use. -

-
- -
-
-
-
-
-

Cited source excerpts

-

- Open the document to inspect the PDF page and highlighted indexed passage. -

-
- -
-
- ); -} - // Moved to a light module so the dashboard can import it without pulling this heavy component // tree into the initial bundle; re-exported here to keep evidence-panels' public API stable. export { evidenceMapRowsFromRenderModel } from "@/components/clinical-dashboard/evidence-map-model"; diff --git a/src/components/favourites-phone-perfected-mockups/favourites-phone-perfected-page.tsx b/src/components/favourites-phone-perfected-mockups/favourites-phone-perfected-page.tsx index 9cb6a37d0b..d0c9d33cb1 100644 --- a/src/components/favourites-phone-perfected-mockups/favourites-phone-perfected-page.tsx +++ b/src/components/favourites-phone-perfected-mockups/favourites-phone-perfected-page.tsx @@ -286,7 +286,7 @@ function FavouritesPhoneScreen({ state }: { state: FrameState }) { // one state where the user most needs to see what survived is the one where // the cards leave no room for it. The 72px strip keeps resume reachable and // gives the rows back. - const showCards = hasLibrary && !narrowed && !compactAlternative && !partial; + const showCards = hasLibrary && !narrowed && !compactAlternative && !partial && sort === "set"; const showStrip = hasLibrary && !narrowed && (compactAlternative || partial); const visible = useMemo(() => { diff --git a/src/components/therapy-compass/therapy-card.tsx b/src/components/therapy-compass/therapy-card.tsx index ddc228d695..7e5cfa3b2f 100644 --- a/src/components/therapy-compass/therapy-card.tsx +++ b/src/components/therapy-compass/therapy-card.tsx @@ -1,8 +1,7 @@ "use client"; -import { useId, type ReactNode } from "react"; +import { useId } from "react"; import { - ChevronRight, Clock, ExternalLink, FileText, @@ -23,8 +22,7 @@ import { useTcBindings } from "./bindings"; import { cardPreviewText, prioritiseTherapyTags, summarise } from "./data/select"; import type { Therapy } from "./data/types"; import { controlPressed, favouritePressed, heroCard } from "./controls"; -import { InteractiveRow } from "@/components/ui/interactive-row"; -import { Eyebrow, IconTile, StatusBadge, TagRow } from "./ui"; +import { Eyebrow, StatusBadge, TagRow } from "./ui"; import { useTherapyFavourite } from "./use-therapy-favourite"; /** @@ -260,43 +258,3 @@ function CardCell({ ); } - -/** Compact tappable therapy row for lists (home, related, pickers). */ -export function TherapyListItem({ - therapy, - onClick, - active = false, - subtitle, - trailing, -}: { - therapy: Therapy; - onClick: () => void; - active?: boolean; - subtitle?: string; - trailing?: ReactNode; -}) { - return ( - - - - {therapy.name} - - {subtitle ?? therapy.bestUsedFor ?? therapy.category} - - - {trailing ?? ( - - {therapy.reviewStatus === "reviewed" ? null : ( - - )} - - ); -} diff --git a/supabase/drift-manifest.json b/supabase/drift-manifest.json index 07f3c176dc..6a04f1f858 100644 --- a/supabase/drift-manifest.json +++ b/supabase/drift-manifest.json @@ -1,9 +1,9 @@ { - "generated_at": "2026-08-25T16:44:53.791Z", + "generated_at": "2026-08-27T06:12:59.010Z", "generator": "scripts/generate-drift-manifest.ts", "postgres_image": "supabase/postgres:17.6.1.127@sha256:be60aee15997daca475b710b734bc6bfe52cd544dcd7e9fd2ff58210b6747d83", - "schema_sha256": "e53377d848c15693be107f9e1492fd55c86b89728c0504dfff97c3fbd89bb351", - "replay_seconds": 27, + "schema_sha256": "3266b5376a36adf72f884ca63fde72c7867974219938d574bc62cf93e441f2eb", + "replay_seconds": 11, "snapshot": { "views": [ { @@ -8609,6 +8609,11 @@ "name": "documents_import_batch_id_fkey", "table": "documents" }, + { + "def": "CHECK ((jsonb_typeof(metadata) = 'object'::text))", + "name": "documents_metadata_object_check", + "table": "documents" + }, { "def": "FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE SET NULL", "name": "documents_owner_id_fkey", diff --git a/supabase/migrations/20260827100000_validate_documents_metadata_structural_constraint.sql b/supabase/migrations/20260827100000_validate_documents_metadata_structural_constraint.sql new file mode 100644 index 0000000000..d5ec85f88b --- /dev/null +++ b/supabase/migrations/20260827100000_validate_documents_metadata_structural_constraint.sql @@ -0,0 +1,35 @@ +-- Enforce structural JSON object constraint on documents.metadata (#S19JRT). +-- Documents metadata must be a JSON object, matching the Zod-level +-- sourceMetadataSchema in src/lib/rag/rag-row-contracts.ts. +-- +-- Adds the constraint NOT VALID only. The Supabase integration applies each +-- migration in one transaction; ADD CONSTRAINT ... NOT VALID takes an ACCESS +-- EXCLUSIVE lock on public.documents, and validating in the same transaction +-- would hold that lock for the full-table scan VALIDATE CONSTRAINT performs, +-- blocking reads for the duration. VALIDATE CONSTRAINT runs instead in the +-- later migration 20260827100500_validate_documents_metadata_object_constraint.sql, +-- which only needs a SHARE UPDATE EXCLUSIVE lock (reads and writes continue). + +set local search_path = public, extensions, pg_catalog; +set local lock_timeout = '5s'; +set local statement_timeout = '30s'; + +do $migration$ +begin + if exists ( + select 1 + from public.documents + where metadata is null or jsonb_typeof(metadata) <> 'object' + ) then + raise exception 'documents.metadata contains null or non-object JSON values'; + end if; +end +$migration$; + +alter table public.documents + drop constraint if exists documents_metadata_object_check; + +alter table public.documents + add constraint documents_metadata_object_check + check (jsonb_typeof(metadata) = 'object') + not valid; diff --git a/supabase/migrations/20260827100500_validate_documents_metadata_object_constraint.sql b/supabase/migrations/20260827100500_validate_documents_metadata_object_constraint.sql new file mode 100644 index 0000000000..200c052287 --- /dev/null +++ b/supabase/migrations/20260827100500_validate_documents_metadata_object_constraint.sql @@ -0,0 +1,15 @@ +-- Validate the documents_metadata_object_check CHECK constraint (#S19JRT). +-- Added NOT VALID in 20260827100000_validate_documents_metadata_structural_constraint.sql, +-- which already confirmed via preflight scan that no row violates it. +-- VALIDATE CONSTRAINT takes only a SHARE UPDATE EXCLUSIVE lock (reads and +-- writes continue) and turns the guard into an enforced invariant for +-- existing rows too. Split into its own migration/transaction so the +-- earlier ACCESS EXCLUSIVE lock from ADD CONSTRAINT ... NOT VALID does not +-- remain held across this full-table scan. + +set local search_path = public, extensions, pg_catalog; +set local lock_timeout = '5s'; +set local statement_timeout = '30s'; + +alter table public.documents + validate constraint documents_metadata_object_check; diff --git a/supabase/schema.sql b/supabase/schema.sql index 97a63e68cd..0ebd70cac6 100644 --- a/supabase/schema.sql +++ b/supabase/schema.sql @@ -1,4 +1,4 @@ --- Medical RAG Knowledge Base schema. +-- Medical RAG Knowledge Base schema. -- Run this in the Supabase SQL editor or with the Supabase CLI. -- Tables are RLS protected; the local Next.js API and worker use the service role. -- @@ -76,7 +76,8 @@ create table if not exists public.documents ( chunk_count integer not null default 0, image_count integer not null default 0, error_message text, - metadata jsonb not null default '{}'::jsonb, + metadata jsonb not null default '{}'::jsonb + constraint documents_metadata_object_check check (jsonb_typeof(metadata) = 'object'), search_tsv tsvector generated always as ( to_tsvector('english', coalesce(title, '') || ' ' || coalesce(file_name, '')) ) stored, diff --git a/tests/adopt-visual-baselines.test.ts b/tests/adopt-visual-baselines.test.ts index 7a2bb08c48..02aa607ec7 100644 --- a/tests/adopt-visual-baselines.test.ts +++ b/tests/adopt-visual-baselines.test.ts @@ -92,9 +92,12 @@ function adoptBaselines( const args = [scriptPath, "--from", artifactDir, "--run-id", "424242", "--head", head, "--reviewed-by", reviewedBy]; if (reviewedByLogin) args.push("--reviewed-by-login", reviewedByLogin); if (write) args.push("--write"); + const env = { ...process.env }; + delete env.NODE_COMPILE_CACHE; return execFileSync("node", args, { cwd: fixtureRoot, encoding: "utf8", + env, stdio: ["ignore", "pipe", "pipe"], }); } diff --git a/tests/favourites-phone-perfected-mockups.test.ts b/tests/favourites-phone-perfected-mockups.test.ts index 1c72762ba4..98ef21e4a9 100644 --- a/tests/favourites-phone-perfected-mockups.test.ts +++ b/tests/favourites-phone-perfected-mockups.test.ts @@ -124,6 +124,22 @@ describe("favourites phone-perfected mockup", () => { expect(firstClinicalReviewIndex).toBeGreaterThan(firstPinnedIndex); }); + it("hides the landing cards once View all switches sort away from set", () => { + // RecentCard's "View all" control (onViewAll) sets sort to "recent". If + // showCards stayed true after that, the list would open with the same + // rows ContinueCard/RecentCard already show above it — duplicate recent + // items right after View all. showCards must require sort === "set" so + // the cards disappear once the view is no longer the set-grouped landing. + const pageSource = source("favourites-phone-perfected-page.tsx"); + const showCardsLine = pageSource.split("\n").find((line) => line.trimStart().startsWith("const showCards =")); + + expect(showCardsLine, "showCards assignment not found").toBeDefined(); + expect(showCardsLine, 'showCards must require sort === "set" or View all duplicates RecentCard rows').toMatch( + /sort === "set"/, + ); + expect(pageSource).toContain('onViewAll={() => setSort("recent")}'); + }); + it("routes clear-all to its own confirmation sheet, not set management", () => { const pageSource = source("favourites-phone-perfected-page.tsx"); expect(pageSource).toContain('onRequestClearAll={() => setSheet("clear-all")}'); diff --git a/tests/rendered-text-formatting.test.ts b/tests/rendered-text-formatting.test.ts index d194338ec7..1b2bdb0048 100644 --- a/tests/rendered-text-formatting.test.ts +++ b/tests/rendered-text-formatting.test.ts @@ -41,7 +41,8 @@ describe("document-derived text must route through a formatter", () => { const answerSourceRail = componentSource("clinical-dashboard/answer-source-rail.tsx"); const answerSourceDrawer = componentSource("clinical-dashboard/answer-source-drawer.tsx"); const answerSourceRows = componentSource("clinical-dashboard/answer-source-rows.ts"); - const dashboardSurfaces = `${dashboard}\n${answerContent}\n${evidenceContent}\n${evidenceMapModel}\n${outputPanel}\n${visualEvidence}\n${documentResults}\n${answerResultSurface}\n${answerSourceRail}\n${answerSourceDrawer}\n${answerSourceRows}`; + const answerEvidencePreview = componentSource("clinical-dashboard/answer-evidence-preview.tsx"); + const dashboardSurfaces = `${dashboard}\n${answerContent}\n${evidenceContent}\n${evidenceMapModel}\n${outputPanel}\n${visualEvidence}\n${documentResults}\n${answerResultSurface}\n${answerSourceRail}\n${answerSourceDrawer}\n${answerSourceRows}\n${answerEvidencePreview}`; it("renders exact quotes through the verbatim cleaner, never raw", () => { // Allow `${quote.quote}` inside template literals (React keys, clipboard text); @@ -62,7 +63,7 @@ describe("document-derived text must route through a formatter", () => { it("renders source-card snippets through compactSourceSnippet with the card title deduped", () => { expect(dashboardSurfaces).not.toMatch(/(? {