Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/outstanding-issues.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,6 @@ removed after current-main verification; it is not missing recommended work.
| #100 | P2 | rec | Buffered answer generation has no incremental verified delivery | **Design complete; runtime work remains provider-gated.** [`verified-answer-incremental-delivery-design.md`](verified-answer-incremental-delivery-design.md) records the clinical-governance decision and staged contract: keep the `progress`/`final`/`error` allowlist; disclose bounded, owner-scoped evidence only after the canonical danger-level source-governance refusal permits it, then emit complete answer sections only after each reuses the full production verification boundary; reconcile every preview byte-for-byte with the authoritative `final`; discard all previews on error/cancel/retry; deploy behind separate parse/emission/render flags. Phase 0 contract proof and Phase 1 evidence preview can be developed offline, but visible rollout still needs clinical/browser proof. Phase 2 changes generation architecture and requires explicit approval for answer-quality evals plus a baseline/post live canary pair. **Naive token streaming remains REFUTED:** never re-land `token`, `revising`, provisional prose, or a weaker stream-only verifier. Cross-references #021. | `docs/verified-answer-incremental-delivery-design.md`; `docs/audit/latency-audit-2026-07-28.md` L0-1; `src/lib/answer-stream-contract.ts:18-21` | 2026-07-30 |
| #101 | P3 | rec | Canary-gated retrieval parallelisation candidates | **Outcome:** independent retrieval stages stop running serially, proven by a live canary pair. Candidates: metadata/memory/visual hydration triples repeated on four branches (`rag.ts:2460,2493,2521` and three more) while `rag.ts:2751-2804` already parallelises three RPCs in one `Promise.all`, so the omission is inconsistency rather than intent; the nested `await`-in-loop scope enumeration (`search-scope.ts:202,328`); typeahead results never cached (`rag.ts:2698-2711`); universal-search coalescing (`/api/search` has it, `/api/search/universal` does not). Each changes candidate assembly, truncation, or what the next keystroke returns, so each needs 36/36 retrieval plus recall 1.0 and zero per-case rr regressions. Distinct from #001 (semantic rerank). Resolved #075 and #083 are the precedents for why these are gated rather than free. **Stop:** needs the #098 harness and explicit canary approval first. | `docs/audit/latency-audit-2026-07-28.md` L2-1/L2-2/L2-8/L1-5 | 2026-07-29 |
| #102 | P3 | task | Apply the additive `documents` index debt (operator) | **Outcome:** bare-column `ILIKE` and the paged status scan on `documents` are index-served on hosted. `documents_title_trgm_idx` indexes a CONCATENATED expression, so the bare-column predicates in `api/documents/route.ts:193` and `rag-candidate-sources.ts:477` (RAG path) cannot use it and fall back to scanning; `search-scope.ts:271-277` sorts per page against the single-column `documents_status_idx`. **Runbook prepared 2026-07-29 — NOT applied, item stays open:** three `CREATE INDEX CONCURRENTLY` statements authored and reviewed in `docs/operator-apply-performance-latency-remediation.md` — additive, though **the "recall is byte-identical" claim was RETRACTED on 2026-07-29 review**: `fetchDocumentTitleAliasRows` (`rag-candidate-sources.ts:482`) applies `.limit(12)` with no `ORDER BY`, so a new index can change which title-alias documents feed candidate assembly. Only the documents-list use stays ordering-safe; `(status,id)` is canary-gated too — see runbook, and making that `.limit(12)` deterministic first does **not** lift the gate — an unordered `LIMIT` has no stable selection to preserve, so imposing an order can pick a different twelve and is itself an ordering behaviour change on a retrieval surface, which AGENTS.md requires a canary pair for. Sequencing the ordering fix first is worthwhile (unordered `LIMIT` on a retrieval input is latent nondeterminism regardless) but yields two canary-gated changes, not one (PR #1377 review). **Deliberately NO migration file:** an additive-index migration without a synchronized `schema.sql` mirror and regenerated drift manifest is exactly what closed PR #1312, and the mirror cannot come first because `required_indexes` in `search_schema_health()` (`schema.sql:3178`) runs against live. **Next (operator):** **author the migration first** — `supabase/migrations/` is the source of truth and `schema.sql` only a mirror, so hand-run operator SQL never reaches staging, disaster-recovery replay, or a local `supabase db reset`, and a `required_indexes` registration would fail there (PR #1377 review); follow the `20260717170000_registry_projection_cleanup.sql` idempotent pattern. **That migration must also carry the health-function change** — `required_indexes` lives inside `search_schema_health()`, which is redefined by `create or replace function` in eleven migrations (copy `20260705180000_reconcile_search_health_indexes.sql:62`); editing `schema.sql:3177` alone moves only the mirror and leaves the indexes unmonitored on hosted (PR #1377 review). Then apply concurrently, confirm `indisvalid`, mirror both the index statements and the identical function body into `schema.sql`, run `npm run drift:manifest` (Docker), and deploy the migration LAST — in that order, in one change. Expect `check:drift` to report them as unexpected between steps 1 and 2. **Rollback is three deployed phases, not the reverse of one:** retract `required_indexes` via its own `create or replace function` migration and deploy → drop concurrently live → only then deploy the `schema.sql` removal plus an idempotent forward `drop index if exists` migration, because Supabase wraps migrations in a transaction and a plain `DROP INDEX` there takes the lock the concurrent procedure exists to avoid (PR #1377 review). | `docs/audit/latency-audit-2026-07-28.md` L2-3/L2-5; `docs/operator-apply-performance-latency-remediation.md` | 2026-07-29 |
| #103 | P2 | issue | Wide table-facts trigram index missing from `schema.sql` | **Outcome:** the migration chain and `schema.sql` agree on `document_table_facts` trigram indexes. `supabase/migrations/20260714190000_document_table_facts_trgm_idx.sql` creates a wide 5-column trigram index that is **absent from `supabase/schema.sql`**, so local replay and the live database can diverge. Distinct from #102: different owner and verification path. **Next:** confirm whether the wide index exists live, then take one of exactly two routes — **retained:** mirror `document_table_facts_text_trgm_idx` into `supabase/schema.sql` beside the narrow one and regenerate `drift-manifest.json`; **redundant:** drop it through a new forward migration, never by deleting `20260714190000`. **`drift-allowlist.json` is NOT a third option** (PR #1377 review): its own header scopes it to _"Known live-vs-`schema.sql` divergence"_, so it can silence a live drift finding but cannot reconcile the migration chain with the mirror — a fresh `supabase db reset` still runs `20260714190000` and creates the index while `schema.sql` still omits it, leaving this row's stated outcome unmet. **No offline gate catches this today:** the migration↔`schema.sql` parity test (`tests/drift-detection.test.ts:59-68`) only asserts one migration's `schema_drift_snapshot` function definition, not an index inventory — which is why this sits open rather than red in CI, and why a replay-to-schema inventory comparison is the check that would have caught it. Note the narrow `document_table_facts_title_row_param_trgm_idx` (`schema.sql:6425`) is the one the effective RPC expression (`:6726`) actually matches, so the wide index may be genuinely redundant — do not drop it without live scan evidence, per the monitored-not-auto-fixed index policy. | `docs/audit/latency-audit-2026-07-28.md` limitations; `npm run check:drift` | 2026-07-29 |
| #117 | P2 | rec | Therapy Compass catalogue payload is the mobile LCP outlier | **Outcome:** `/therapy-compass` mobile LCP lands near the other mobile routes instead of double them. **Measured 2026-07-30** by the new pre-merge Lighthouse budget: mobile LCP 5229 ms, TBT 612 ms, CLS 0.142, against 2123-2460 ms on every other mobile route and 826 ms on desktop — so it is client-side work under mobile CPU/network throttling, not server latency. **Cause before this PR:** `useTherapyData` fetched `public/therapy-compass-data/therapies-index.json` (the thin browse index; 205 records) for the home/search/pathways screens, so the download plus JSON parse sat on the critical path before content painted. **Current split:** home now fetches `public/therapy-compass-data/therapies-home.211dab554c4ec62d.json` (136,288 bytes raw), pathways use the thin browse index, and search loads the full prose corpus (#1471). 90% of the index weight is long-form clinical prose — indications 159 KB (26%), contraindicationsOrCautions 139 KB (23%), bestUsedFor 73 KB (12%), clinicalSummary 67 KB (11%), patientPopulation 59 KB (10%), targetSymptoms 48 KB (8%) — while name, slug, category, tags and setting together are 54 KB (7%). **Remaining decision for search/pathways: rendered on the card, matched by search, or neither.** `therapy-card.tsx` references five of those prose fields and the same index feeds the search screen, so stripping fields could silently change clinical display or search recall. **Next:** settle that per-field question, then either pre-truncate prose that only feeds card display, or move search matching server-side / load prose on first keystroke. **Gate:** `check:therapy-data-index` plus the therapy Playwright journeys; re-measure with `npm run verify:lighthouse`. **Stop:** do not drop a field from the catalogue payload without confirming no card renders it and no search path matches on it. Same class as #013 (route-chunk / catalogue JSON weight), different route and now measured. | session 2026-07-30 Lighthouse budget first run; PR #1404; PR #1489 | 2026-07-30 |
| #118 | P2 | task | Adopt the visual and Lighthouse baselines so the two new gates actually gate | **Outcome:** `visual-baseline` and `lighthouse-budget` stop reporting and start blocking. **Detail:** PR #1404 added both as `continue-on-error` jobs outside `pr-required`, deliberately. `tests/ui-visual-baseline.spec.ts` has no committed baselines, so all six targets fail with a missing-snapshot error by design; the job uploads them on every run (run 30513537912, artifact 8748062487, 31 files). `lighthouse-budget.json` ships `enforce: false` with `baseline: null`, so the grader warns rather than grades. **Next:** (1) download that artifact, review the six PNGs and commit them under the platform-scoped screenshots directory that `playwright.visual.config.ts` names in its `snapshotPathTemplate` — from CI, never a developer machine, because font hinting differs between them; (2) run `npm run check:lighthouse-budget -- --update` against a known-good CI build and flip `enforce`, but not before #117 or the baseline pins a known-slow route; (3) then add each job to `pr-required` and drop `continue-on-error` in the same edit. **Also:** PR #1404 added the first rendered-effect contract for #094, but 37 of the 38 unlayered visual classes still carry exemptions in `tests/helpers/style-contracts.ts` rather than contracts; and `scripts/run-lighthouse-budget.mjs` duplicates about 50 lines of the isolated-server boot in `scripts/run-playwright.mjs`, deferred to avoid destabilising the required UI gate in the same change. **Stop:** do not make a missing baseline skip instead of fail — that is the soft-skip-green pattern `AGENTS.md` forbids. | session 2026-07-30; PR #1404 | 2026-07-30 |
| #142 | P3 | task | Four loose dated docs need source and migration edits before they can be filed | **Outcome:** every dated point-in-time doc lives in `docs/audit/` or `docs/archive/` as `docs/README.md` requires, not loose at the `docs/` top level. **Detail:** PR #1436 filed the five that were docs-only moves. These four are referenced from outside `docs/`, so relocating them means editing source, tests and migration SQL comments — a different risk class than a docs tidy, and not worth bundling into one: `capacity-review.md` (`scripts/soak-test.ts`), `tenancy-defense-in-depth-review.md` (`src/lib/owner-scope.ts`, `tests/owner-scope-guard.test.ts`, two migrations, `SECURITY.md`, `.claude/agents/clinical-governance-reviewer.md`), `operator-apply-july8-batch.md` (three migrations plus `supabase/schema.sql`), `scale-readiness-review.md` (one migration). Also note `forward-codify-retrieval-rpcs-workorder.md` is indexed as a completed workorder but live is still ahead of the repo on those RPCs, so archiving it would misrepresent open operator work. **Next:** treat as low priority — the docs are correctly indexed and reachable where they are; only file them if a pass is already editing those migrations. Editing applied migration SQL is subject to `npm run check:migration-role`'s immutability pin. | PR #1436; session 2026-07-30 | 2026-07-30 |
Expand Down Expand Up @@ -281,4 +280,5 @@ Move resolved rows here with the resolution date and a one-line outcome. Keep th
| #116 | issue | Missing CI signal on conflicted PRs | RESOLVED 2026-07-30: `.github/workflows/pr-mergeability.yml` checks trusted `pull_request_target` events and refreshes unchanged heads after protected-base pushes. A conflicted PR gets a red `PR mergeability` check naming the conflict instead of an empty CI list. The base-push job alone has scoped `checks: write`; neither path checks out PR code or updates branches. Classifier: `scripts/pr-mergeability.mjs`; contract: `npm run check:pr-mergeability`. Behind-but-clean remains `sync:pr-branches`. | 2026-07-30 |
| #140 | issue | Union-driver table duplication — duplicate of #133 | CLOSED 2026-07-30 as a duplicate, superseded by `#133` and resolved by PR #1444. This row was opened mid-session for the union-driver damage before its author noticed `#133` had already recorded the same finding — and recorded it _earlier_, with the same conclusion ("**Do not** apply a `merge=union` driver — tested 2026-07-30 and it is worse"). Keeping both would have left two open rows describing one condition, and that condition no longer exists: PR #1444 removed `merge=union` from `.gitattributes`, inverted `check:outstanding-issues` to require an unspecified `merge` attribute, and added regression tests covering `union`, `-merge` and an unparsed reading. The surviving evidence — four merges on PR #1430 each reporting success while duplicating the entire open-items table — lives in `#133`, whose still-open half is the real conflict-frequency cause: fixed-width column padding makes any one-row edit re-pad every row, so git sees the whole table as one hunk. Read `#133`, not this. | 2026-07-30 |
| #130 | issue | Phone-chrome physical-device acceptance never run (#1396) — not pursued | CLOSED 2026-07-30 by owner decision — **not because the gate was run.** Stating that plainly so the record is not read as evidence later: `docs/phone-chrome-physical-acceptance.md` on `main` is still the blank checklist template, no PR comment attaches device evidence, and PR #1396's shared phone-chrome overlay change therefore shipped without the physical-device acceptance its own body and review thread said applied before merge (restated at least three times, including after the final fix). The owner has decided not to pursue it. Residual risk is unchanged and unmeasured: headless Chromium was explicitly stated as unable to certify Safari chrome-minimisation or cold-launch PWA paint (invariant 23), so nothing in CI covers what that checklist covers. The separately-identified missing guard — a pre-paint/cold-load hydration test comparing content position before and after hydration — was also never filed and is also not pursued. Reopen a new row if phone-chrome behaviour is reported wrong on a real device. | 2026-07-30 |
| #103 | issue | Wide table-facts trigram index missing from `schema.sql` | Refuted and archived 2026-07-31. The terminal migration chain already drops `document_table_facts_text_trgm_idx` in `20260717010000_harden_rag_scalability_patch.sql`; `schema.sql` deliberately retains only the expression-matching narrow index, and `tests/supabase-schema.test.ts` pins both facts. No hosted inspection or index re-addition is required. Verified against origin/main before landing: the drop is present in that migration and the index name appears nowhere in schema.sql. Disposition authored in another session's worktree and landed here. | 2026-07-31 |

Loading