From f430466e7c8d83b0e465de5e76c8a14ceed8287c Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 27 Jul 2026 22:05:33 +0000 Subject: [PATCH 01/19] perf(bundle): precompute cross-mode differentials index to slim the lazy chunk (#012) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The cross-mode "Also in your library" strip dynamically imports cross-mode-differentials.ts, which statically pulled the full ~1.2 MB differentials snapshot (via @/lib/differentials) just to build a tiny {slug,title,clinicalHinge} + presentations + aliases catalog. That path is the only reason the snapshot landed in the lazy cross-mode chunk. Precompute the trimmed catalog at build time and import that instead: - scripts/build-cross-mode-differentials-index.mjs projects data/differentials-snapshot.json into src/data/cross-mode-differentials- index.json (~53 KB vs ~1.2 MB), mirroring the therapies-index generator. `--check` fails when the committed index is stale (parsed-value compare, so Prettier formatting never trips it). - cross-mode-differentials.ts now returns the precomputed JSON and no longer imports @/lib/differentials, so the lazy chunk drops the snapshot. - check:cross-mode-index is wired into verify:cheap next to check:therapy-data-index. - tests/cross-mode-differentials-index.test.ts asserts the index equals the live projection over the full snapshot, guarding against both snapshot-content and projection/alias-filter drift. No behaviour change: crossModeDifferentialCatalog() returns the same catalog, proven equal to the live projection. Resolves ledger #012. RAG impact: no retrieval behaviour change — this is a client bundle slim of a display-only cross-mode links catalog; no retrieval/ranking/ selection/eval or answer-generation code is touched. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01UbhUVWVJRwDibC2YtJ6aRX --- docs/outstanding-issues.md | 68 +- package.json | 3 +- .../build-cross-mode-differentials-index.mjs | 78 ++ src/data/cross-mode-differentials-index.json | 1188 +++++++++++++++++ src/lib/cross-mode-differentials.ts | 28 +- tests/cross-mode-differentials-index.test.ts | 42 + 6 files changed, 1356 insertions(+), 51 deletions(-) create mode 100644 scripts/build-cross-mode-differentials-index.mjs create mode 100644 src/data/cross-mode-differentials-index.json create mode 100644 tests/cross-mode-differentials-index.test.ts diff --git a/docs/outstanding-issues.md b/docs/outstanding-issues.md index 8c9d955b8d..d4520e94d2 100644 --- a/docs/outstanding-issues.md +++ b/docs/outstanding-issues.md @@ -87,40 +87,39 @@ removed after current-main verification; it is not missing recommended work. > > **RAG reconciliation correction (2026-07-23):** fresh current-main live evidence supersedes the broad diagnosis in #018. The three named misses are not one composer defect. Lithium reproduced an unrelated-table retrieval fast-path defect; ADHD still retrieves a relevant chart-heavy CAMHS source but exhausts the extractive route budget; metabolic retrieves the correct AKG source but selects schedule-free prose; #019 remains post-retrieval comparison source selection. A narrow lithium subject-evidence guard improved its targeting result from 0 to 1 with golden recall 1.0 and no reciprocal-rank regressions, but was reverted and rejected because the required full canary failed. Keep #029 open for the remaining fallback-stub cases. Do not combine these residuals or change ranking scores, comparator ordering, aliases, clamps, or semantic reranking without a separate reproducer and passing canary pair. -| ID | Pri | Type | Summary | Detail / next action | Source | Added | -| ---- | --- | ----- | --------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- | ---------- | -| #059 | P1 | task | Verify containment of every credential reported exposed in chat | **Outcome:** every reported exposed credential is rejected or retired. **Next:** in approved security windows, verify and revoke or rotate the GitHub token, OpenAI key, Supabase service-role JWT, database password, and E2E credential; create replacements only when required and update only intended secret stores. **Success:** provider evidence confirms the old credentials cannot authenticate, replacements are distinct and minimally scoped, presence/readiness checks pass, and secret scans remain clean. **Stop:** no provider or secret-store action without approval; never print or paste values into Git, logs, issues, or chat. | session 2026-07-24 security reconciliation; AI Agent Target Manifest | 2026-07-24 | -| #065 | P2 | task | Complete the paused compact document source-text accordion | **Outcome:** the document viewer uses compact nested disclosures while retaining complete text, citation/search navigation, print behavior, and composer clearance. **Next:** only when the user explicitly resumes, reconcile `codex/chat-document-text-accordion-7cb4` with current `main` and complete the focused 320/390/1280 px tests. **Success:** default disclosures are closed; deep links and search open only the active passage; printing expands/restores state; no overflow. **Verify:** focused document-viewer Playwright, `verify:cheap`, `verify:ui`, and static production-readiness. **Stop:** remain paused until explicit user return; no provider calls. | paused document-viewer task; `codex/chat-document-text-accordion-7cb4` | 2026-07-24 | -| #001 | P2 | task | Semantic reranking still gated off | `RAG_SEMANTIC_RERANK_ENABLED=false` from PR #901. Do not enable until the provider-backed 36/36 retrieval-quality gate **and** an ambiguity-focused canary are explicitly approved and recorded. | `docs/process-hardening.md` (Semantic reranking rollout debt); PR #901 | 2026-07-21 | -| #069 | P3 | task | Live-profile table-facts plpgsql+EXECUTE latency | Migration `20260724120000_table_facts_plpgsql_execute.sql` plus P3 follow-ups (`20260724130000_*`, `20260724130100_*`) are in PR #1133. Hosted apply is blocked in this environment (no `SUPABASE_DB_URL`; Supabase MCP `needsAuth`). A live `profile:retrieval --rpc match_document_table_facts_text --analyze` attempt returned `Unregistered API key` against the injected service-role secret. **Next:** operator applies the three pending migrations on the live target project, then re-runs approval-gated `profile:retrieval` / `explain_retrieval_rpc` and confirms ~70ms-class plans with no ranking change. **Stop** without mutating ranking. | session 2026-07-24 Database interface audit; PR #1133 | 2026-07-24 | -| #053 | P1 | task | Execute cross-border privacy/legal package | Execute OpenAI and Railway DPAs; decide ZDR and Australian data residency; obtain prompt-cache behavior in writing; review subprocessors; obtain APP 8 and APP 5/1 counsel sign-off. Do not represent the release as privacy-approved or alter final public privacy wording before sign-off. | `docs/openai-cross-border-basis.md`; `docs/privacy-impact-assessment.md` | 2026-07-24 | -| #055 | P2 | task | Run one exact-SHA full release and PR gate | Scheduled CI run `30216361999` stopped at `npm audit --omit=dev --audit-level=high` on the existing ExcelJS/brace-expansion chain, so its dependent Firefox/WebKit matrix was skipped. Let the already-open dependency PRs own that upgrade; then run one exact-SHA local/provider release gate, browser matrix, hosted CI and review-thread closure. Stop at the first actionable failure and rerun only the repaired smallest gate. | `docs/launch-operator-runbook.md`; run `30216361999`; Dependabot PRs #1268/#1269 | 2026-07-24 | -| #056 | P2 | task | Reconcile the existing isolated staging environment | `Clinical KB Staging` Supabase (`ikoiolksxqxfxgiyqpnu`) and Railway staging are active, healthy, offline-provider mode, empty of documents/registry content, and pass `search_schema_health()`. Staging migration history is 23 versions behind production (last applied `20260719055623`). Apply the exact repository chain with a credentialed Supabase CLI/database connection—do not use a helper that invents replacement timestamps—then repeat indexing, identity, health and empty-data-boundary proof. | `docs/staging-setup.md`; `docs/operator-backlog.md`; read-only provider proof 2026-07-27 | 2026-07-24 | -| #057 | P2 | task | Complete staging soak and rollback rehearsal | After #056, run the documented soak and rollback against an exact candidate; retain latency/error/rollback evidence. Stop on unsafe data, identity mismatch, or an unowned rollback decision. | `docs/launch-operator-runbook.md`; `docs/capacity-review.md` | 2026-07-24 | -| #005 | P3 | rec | `finalScore` saturates at clamp ceiling | Base + ~40 stacked boosts routinely exceed 1.0, so strong matches tie at 1.0 and order by an arbitrary `document_id` tiebreak. If ranking is ever revisited, break ties by the **pre-clamp** score rather than raising the `[0,1]` ceiling (downstream gates assume `[0,1]`). Ordering already sorts by the unbounded pre-clamp `rankScore` (`clinical-search.ts:1735,1927,1950-1955`), so the clamp confines only the reported confidence value, not result order. Not a defect on the current golden set; any change here is a protected RAG surface (canary required). | `docs/rag-hybrid-findings-and-todo.md` P1 item 4; `src/lib/clinical-search.ts:1735` | 2026-07-21 | -| #011 | P3 | task | Auth DB-connection allocation is operator-only | Supabase Auth (GoTrue) is capped at ~10 absolute DB connections (Supabase perf advisor). Switch to **percentage-based** allocation in the Supabase **dashboard** before the first compute scale-up — **not settable via SQL/MCP** (operator-owned). Verify via a staging soak + an approval-gated read-only advisor re-check. | `docs/auth-connection-cap-runbook.md`; `docs/process-hardening.md` (Known follow-up debts) | 2026-07-21 | -| #012 | P3 | rec | Slim the lazy cross-mode differentials chunk | `cross-mode-differentials.ts` is dynamically imported (correctly code-split **out** of the initial/dashboard bundle — verified), but it pulls the full ~860 KB differentials snapshot (~125 KB gzip lazy chunk) just to build a tiny `{slug,title,clinicalHinge}` + presentations + aliases catalog. A precomputed lightweight index (generator + drift check, like the `specifiers-content` split / medications `fields=index`) would cut that lazy chunk ~5–10├ù. Not a bundle leak — an M-effort slim. | `src/lib/cross-mode-differentials.ts`; `src/components/clinical-dashboard/cross-mode-links.tsx:150`; session 2026-07-21 (build:analyze) | 2026-07-21 | -| #013 | P3 | rec | Route-chunk + mockup catalogue JSON weight | `build:analyze`: `/specifiers` ships `specifiers-search-index.json` (~180 KB parsed), `/forms` ships `forms-catalog.json` (~132 KB), `/formulation` ships `formulation-content.json` (~52 KB, client-side local search — needs index/full split or a search endpoint, architectural). All route-scoped (not initial bundle). Also `*-mockups.tsx` (~100 KB across chunks) build though `/mockups` 404s in prod — exclude from the prod artifact. | session 2026-07-21 (build:analyze) | 2026-07-21 | -| #016 | P3 | rec | "Big but not easy" structural + motion perf | Deferred larger levers: (a) nonce-CSP forces every product route to `╞Æ Dynamic` (zero static generation) — evaluate Partial Prerendering / static shells for the static clinical catalogues (DSM/differentials/therapy/specifiers/formulation); (b) sidebar expand/collapse animates `grid-template-columns` (biggest smoothness cost, motion-gated — needs a transform-overlay rethink); (c) Therapy Compass fetches 692 KB / 2.5 MB JSON client-side (defer until interaction + confirm brotli); (d) settings/setup/admin dialogs static-imported into the home chunk (`next/dynamic` them). | session 2026-07-21 (build route table + design audit) | 2026-07-21 | -| #017 | P3 | task | Field Web-Vitals baseline via live Lighthouse | In-sandbox runtime vitals were blocked (prod server hard-requires Supabase secrets; dev-mode CLS measured excellent at 0.00–0.04, content-first pages 0.000). Run Lighthouse against `psychiatry.tools` for real LCP/INP/CLS to prioritize #012–#016 by measured impact rather than reasoning. | session 2026-07-21 (measurement pass) | 2026-07-21 | -| #018 | P2 | task | Split the lithium, ADHD and metabolic residuals by mechanism | Revalidated on current main 2026-07-23: these are not one composer defect. Lithium reproduced an unrelated-table retrieval fast-path defect; ADHD retrieves a relevant chart-heavy CAMHS source but exhausts the extractive route budget; metabolic retrieves the correct AKG source but selects schedule-free prose. The narrow lithium subject-evidence guard improved targeting from 0 to 1 with golden recall 1.0 and no reciprocal-rank regressions, but it was reverted because the full canary failed. After #051 stabilises the canary, add independent current-main reproducers and assess each mechanism separately. Do not widen the matcher or combine these into a broad ranking/composer change. | runs `30007833352` and `30009207429`; PR #1093; session 2026-07-23 | 2026-07-21 | -| #019 | P2 | task | Admission doc dropped after deterministic comparison packing | Reconfirmed in both baseline `30018289898` and scheduled run `30216191889`: retrieval remained 36/36 and retrieved the admission source, while both admission/discharge answer cases omitted it after comparison fallback. PR #1096 already proves deterministic answer ranking and cross-document packing retain both sources, so retrieval scores, aliases and comparator ordering are not the fix. Next: create a red fallback-layer unit reproducer using the live source shape; any behavior change needs a passing baseline/post canary. | runs `30018289898`/`30216191889`; PR #1096; session 2026-07-27 | 2026-07-21 | -| #021 | P3 | rec | E-3d H2 residual: strong/comparison generation discards | approx. 6 generation attempts per full 44-case run still fail the final quality gate and fall to extractive on strong-route comparison/complex shapes (the designed-conservative outcome). PARKED: weakest cost/benefit on the queue — a wave (approx. $2-4 pair + reviewer cycle) to shave seconds off a few hard cases. Revisit only if latency/waste complaints or a cheaper lever appears. | E-3c design record; runs #59-#61 diagnostics | 2026-07-21 | -| #022 | P2 | task | Source-governance metadata refresh (operator) | **Worklist generated 2026-07-22 ($0, read-only): `docs/source-governance-refresh-worklist-2026-07-22.md`.** Reframed - this is NOT 59 clinical reviews. Of the 124 documents surfacing in canary top results, 59 are review-required, and **38 (64 pct) are the BMJ published-reference tier all sitting at `clinical_validation_status: unverified`** - one attestation-policy decision, not 38 reviews. The remaining 21 are genuine local WA health-service reviews (FSH 7, NMHS 4, CAMHS 3, AKG 2, KEMH 2, RPBG 2, RKPG 1), mostly `document_status: review_due`. Burn-down: top-10 documents clear 44 pct of flagged slots, top-20 clear 66 pct. Next: decide the BMJ attestation policy, then attest local docs by visibility (start `Clozapine Management by GP (NMHS)`, 22 slots at rank 1). | runs #61/#57 Source Governance data; `docs/source-governance-refresh-worklist-2026-07-22.md` | 2026-07-21 | -| #023 | P2 | task | Complete scheduled browser and labeling disposition | The 2026-07-26 canary artifacts are read and disposed under resolved #051. Scheduled CI run `30216361999` failed its existing production dependency audit before the dependent Firefox/WebKit job, while production Chromium passed. After the already-open dependency PRs clear the audit, capture one scheduled/manual browser-matrix datapoint; separately record the human decision for the stable irrelevant-at-10 set. Do not rerun or spend on RAG for this item. | runs `30216191889`/`30216361999`; Dependabot PRs #1268/#1269; session 2026-07-27 | 2026-07-21 | -| #025 | P2 | task | Activate the three webhooks (operator secrets) | Merged (#968/#1100) + deployed but inert — verified live: `POST /api/webhooks/railway` returns `503 webhook_not_configured`; the Supabase document-change trigger exists but lacks both activation inputs. To turn on: (1) Railway ΓåÆ set `RAILWAY_WEBHOOK_SECRET` + add the `?token=…` webhook URL; (2) set `SLACK_WEBHOOK_URL`/`DISCORD_WEBHOOK_URL` in BOTH the Railway **app/server env** and **GitHub repo secrets**; (3) set one matching document-change secret in the Railway app env as `SUPABASE_INGESTION_WEBHOOK_SECRET` and in Supabase Vault as `ingestion_webhook_secret`, then set the per-environment database GUC `app.ingestion_webhook_base_url` to the deployed app origin. Each path fails closed until fully configured, so this is pure ops. See `docs/webhooks.md` for verification and rotation. | sessions 2026-07-22/24; PRs #968/#1100; docs/webhooks.md | 2026-07-22 | -| #027 | P3 | rec | External uptime monitor independent of GitHub/Railway | `live-domain-monitor.yml` runs on GitHub's cron, so it won't run in exactly the outage it should catch (Actions or the deploy itself down). Add an off-platform synthetic monitor (UptimeRobot / Better Stack / Checkly) hitting `/api/health` with a webhook alert. Provider setup, not code. | session 2026-07-22 webhook review | 2026-07-22 | -| #028 | P3 | rec | Runtime error tracking (Sentry or similar) | No error tracking in the repo — production exceptions on `psychiatry.tools`, including how often `RAG_PROVIDER_MODE=auto` silently degrades to source-only, are invisible. Weigh adding `@sentry/nextjs` (dependency + DSN secret + instrumentation) vs cost; alert ΓåÆ chat/issue. Provider-backed; needs explicit sign-off before adding the dependency. | session 2026-07-22 webhook review | 2026-07-22 | -| #029 | P2 | issue | 12 of 30 answer-quality cases return the fallback stub | run #61 --dump-answers: 12/30 quality cases emit the source_backed_review_fallback boilerplate with answer_sections: [], all grounded with 4-6 citations. Some still PASS targeting because the stub echoes query keywords (the contraindication/document_lookup matchers need only a keyword), so the targeting metric MASKS the problem for those intents. Superset of #018 — fix in the extractive composer, validate with the provider-backed answer eval. | run #61 dump artifact; session 2026-07-22 | 2026-07-22 | -| #033 | P3 | rec | Source governance metadata absent from the LLM prompt | `buildRagSourceBlock` omits `document_status`, `clinical_validation_status`, and `extraction_quality`, so the model cannot self-caveat during generation and governance is enforced only post-hoc. Generation-surface change: needs `eval:rag` plus `eval:quality --rag-only` (grounded-supported must not drop, citation-failure 0) and explicit approval. Carries the same "unknown Γëá bad" hazard as #032 — on a partially-enriched corpus the model would likely over-caveat correct sources, so design the prompt wording before spending an eval. | `src/lib/rag/rag-source-block.ts:126-198`; PR #1051 audit item 8 | 2026-07-22 | -| #035 | P3 | rec | Threshold-conflict detection covers only 3 params | `detectThresholdDisagreements` checks only ANC, WBC, and platelets paired with withholding verbs, so cross-source conflicts on medication doses, lithium/thyroid levels, or vital signs go undetected. Deliberately narrow (see the comment at `:469-474`). Broadening changes when an answer is classified `conflicting` and adds warnings — real false-positive risk. Needs new fixtures plus a behaviour review before any change. | `src/lib/evidence.ts:469-574`; PR #1051 audit item 7 | 2026-07-22 | -| #036 | P3 | rec | No explicit `is_public` visibility flag on documents | Public-corpus visibility is implicit: `owner_id IS NULL` on an `indexed` document (`resolveSearchScope`). The `metadata.public_corpus` marker is written by the promotion migrations but never used as a retrieval filter. Promotion is unconditional on `clinical_validation_status`, so unverified documents are publicly searchable — compensated by keeping `unverified_source` in the frontend-visible warning set. A hard schema flag touches RLS and the clinical-risk-gated retrieval RPCs; weigh against the existing compensating control before acting. | `supabase/schema.sql:61-108`; `src/lib/search-scope.ts:181-236`; PR #1051 audit item 3 | 2026-07-22 | -| #037 | P3 | rec | D5 trust-cap-all-claims flag parked OFF | `NEXT_PUBLIC_RAG_TRUST_CAP_ALL_CLAIMS` extends authority gating from high-risk claims to **all** supported claims (`deriveTrust`). Ships OFF by design; flipping it caps trust to `medium` for routine claims across the board — a product/clinical-UX decision, not a defect. Both states are test-pinned. Next action: product decision, then flip and re-baseline the UI expectations. | `src/lib/answer-render-policy.ts:159-177`; PR #1051 audit item 11 | 2026-07-22 | -| #038 | P3 | rec | Consolidate shared comparison behavior | Several clinical modes expose comparison workflows with similar selection, empty-state and mobile-dock needs. Define one shared behavioral contract before another comparison surface is added; keep mode-specific clinical content separate. This is a design-system recommendation, not a current defect. | design audit reconciliation; session 2026-07-22 | 2026-07-22 | -| #039 | P3 | rec | Consolidate catalogue toolbar patterns | Catalogue/search pages have independently evolved filter, sort, result-count and mobile toolbar behavior. Inventory the existing implementations and converge only the repeated interaction contract; do not flatten mode-specific search semantics. | design audit reconciliation; session 2026-07-22 | 2026-07-22 | -| #040 | P3 | rec | Add targeted visual-regression baselines | Keep a small approved baseline set for high-value desktop/mobile surfaces and accessibility modes instead of screenshotting every route. Start with account/settings, document viewer, mode homes and bottom-composer interactions; define an intentional-update workflow before enabling blocking comparisons. | design audit reconciliation; session 2026-07-22 | 2026-07-22 | -| #079 | P3 | task | Disposition retained worktrees in bounded cleanup batches | **Outcome:** the retained reconciliation tail is gradually classified without another disruptive all-worktree sweep. **Next:** process no more than ten worktrees per explicitly scheduled pass using current owner/process metadata, open-PR state, exact review-ledger coverage, ancestry, and cherry-pick-aware content proof. **Success:** remove only clean, inactive, bundled worktrees whose content is merged or explicitly rejected; record every disposition and retain recovery evidence. **Stop:** preserve dirty, active, secret-bearing, post-freeze, paused, or ambiguous work and never use reset, force deletion, broad clean, or process killing. | final reconciliation inventory retained 104 independent worktrees; session 2026-07-24 | 2026-07-24 | -| #080 | P3 | rec | Re-test the removed admission-to-discharge alias widening | **Outcome:** decide with measured evidence whether the user-approved 2026-07-21 widening (NMHS admission-to-discharge titles satisfying the admission expectation) belongs back on the wide-tier `AdmissionCommunityPts` list. #030 removed it so one document could not fill both comparison slots, but coverage now enforces distinct document identities by maximum matching, so the widening is no longer a false-pass route and its removal may instead cost legitimate admission credit. **Next:** with approval, run an eval-canary baseline/post pair over the admission-discharge comparison cases with and without those two titles. **Success:** restore only on measured non-regression (recall pinned 1.0, zero per-case reciprocal-rank regressions); otherwise record keep-removed and close. **Stop:** never restore on offline reasoning alone. | `src/lib/eval-document-matching.ts`; #030 archive row; session 2026-07-25 | 2026-07-25 | +| ID | Pri | Type | Summary | Detail / next action | Source | Added | +| ---- | --- | ----- | --------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- | ---------- | +| #059 | P1 | task | Verify containment of every credential reported exposed in chat | **Outcome:** every reported exposed credential is rejected or retired. **Next:** in approved security windows, verify and revoke or rotate the GitHub token, OpenAI key, Supabase service-role JWT, database password, and E2E credential; create replacements only when required and update only intended secret stores. **Success:** provider evidence confirms the old credentials cannot authenticate, replacements are distinct and minimally scoped, presence/readiness checks pass, and secret scans remain clean. **Stop:** no provider or secret-store action without approval; never print or paste values into Git, logs, issues, or chat. | session 2026-07-24 security reconciliation; AI Agent Target Manifest | 2026-07-24 | +| #065 | P2 | task | Complete the paused compact document source-text accordion | **Outcome:** the document viewer uses compact nested disclosures while retaining complete text, citation/search navigation, print behavior, and composer clearance. **Next:** only when the user explicitly resumes, reconcile `codex/chat-document-text-accordion-7cb4` with current `main` and complete the focused 320/390/1280 px tests. **Success:** default disclosures are closed; deep links and search open only the active passage; printing expands/restores state; no overflow. **Verify:** focused document-viewer Playwright, `verify:cheap`, `verify:ui`, and static production-readiness. **Stop:** remain paused until explicit user return; no provider calls. | paused document-viewer task; `codex/chat-document-text-accordion-7cb4` | 2026-07-24 | +| #001 | P2 | task | Semantic reranking still gated off | `RAG_SEMANTIC_RERANK_ENABLED=false` from PR #901. Do not enable until the provider-backed 36/36 retrieval-quality gate **and** an ambiguity-focused canary are explicitly approved and recorded. | `docs/process-hardening.md` (Semantic reranking rollout debt); PR #901 | 2026-07-21 | +| #069 | P3 | task | Live-profile table-facts plpgsql+EXECUTE latency | Migration `20260724120000_table_facts_plpgsql_execute.sql` plus P3 follow-ups (`20260724130000_*`, `20260724130100_*`) are in PR #1133. Hosted apply is blocked in this environment (no `SUPABASE_DB_URL`; Supabase MCP `needsAuth`). A live `profile:retrieval --rpc match_document_table_facts_text --analyze` attempt returned `Unregistered API key` against the injected service-role secret. **Next:** operator applies the three pending migrations on the live target project, then re-runs approval-gated `profile:retrieval` / `explain_retrieval_rpc` and confirms ~70ms-class plans with no ranking change. **Stop** without mutating ranking. | session 2026-07-24 Database interface audit; PR #1133 | 2026-07-24 | +| #053 | P1 | task | Execute cross-border privacy/legal package | Execute OpenAI and Railway DPAs; decide ZDR and Australian data residency; obtain prompt-cache behavior in writing; review subprocessors; obtain APP 8 and APP 5/1 counsel sign-off. Do not represent the release as privacy-approved or alter final public privacy wording before sign-off. | `docs/openai-cross-border-basis.md`; `docs/privacy-impact-assessment.md` | 2026-07-24 | +| #055 | P2 | task | Run one exact-SHA full release and PR gate | Scheduled CI run `30216361999` stopped at `npm audit --omit=dev --audit-level=high` on the existing ExcelJS/brace-expansion chain, so its dependent Firefox/WebKit matrix was skipped. Let the already-open dependency PRs own that upgrade; then run one exact-SHA local/provider release gate, browser matrix, hosted CI and review-thread closure. Stop at the first actionable failure and rerun only the repaired smallest gate. | `docs/launch-operator-runbook.md`; run `30216361999`; Dependabot PRs #1268/#1269 | 2026-07-24 | +| #056 | P2 | task | Reconcile the existing isolated staging environment | `Clinical KB Staging` Supabase (`ikoiolksxqxfxgiyqpnu`) and Railway staging are active, healthy, offline-provider mode, empty of documents/registry content, and pass `search_schema_health()`. Staging migration history is 23 versions behind production (last applied `20260719055623`). Apply the exact repository chain with a credentialed Supabase CLI/database connection—do not use a helper that invents replacement timestamps—then repeat indexing, identity, health and empty-data-boundary proof. | `docs/staging-setup.md`; `docs/operator-backlog.md`; read-only provider proof 2026-07-27 | 2026-07-24 | +| #057 | P2 | task | Complete staging soak and rollback rehearsal | After #056, run the documented soak and rollback against an exact candidate; retain latency/error/rollback evidence. Stop on unsafe data, identity mismatch, or an unowned rollback decision. | `docs/launch-operator-runbook.md`; `docs/capacity-review.md` | 2026-07-24 | +| #005 | P3 | rec | `finalScore` saturates at clamp ceiling | Base + ~40 stacked boosts routinely exceed 1.0, so strong matches tie at 1.0 and order by an arbitrary `document_id` tiebreak. If ranking is ever revisited, break ties by the **pre-clamp** score rather than raising the `[0,1]` ceiling (downstream gates assume `[0,1]`). Ordering already sorts by the unbounded pre-clamp `rankScore` (`clinical-search.ts:1735,1927,1950-1955`), so the clamp confines only the reported confidence value, not result order. Not a defect on the current golden set; any change here is a protected RAG surface (canary required). | `docs/rag-hybrid-findings-and-todo.md` P1 item 4; `src/lib/clinical-search.ts:1735` | 2026-07-21 | +| #011 | P3 | task | Auth DB-connection allocation is operator-only | Supabase Auth (GoTrue) is capped at ~10 absolute DB connections (Supabase perf advisor). Switch to **percentage-based** allocation in the Supabase **dashboard** before the first compute scale-up — **not settable via SQL/MCP** (operator-owned). Verify via a staging soak + an approval-gated read-only advisor re-check. | `docs/auth-connection-cap-runbook.md`; `docs/process-hardening.md` (Known follow-up debts) | 2026-07-21 | +| #013 | P3 | rec | Route-chunk + mockup catalogue JSON weight | `build:analyze`: `/specifiers` ships `specifiers-search-index.json` (~180 KB parsed), `/forms` ships `forms-catalog.json` (~132 KB), `/formulation` ships `formulation-content.json` (~52 KB, client-side local search — needs index/full split or a search endpoint, architectural). All route-scoped (not initial bundle). Also `*-mockups.tsx` (~100 KB across chunks) build though `/mockups` 404s in prod — exclude from the prod artifact. | session 2026-07-21 (build:analyze) | 2026-07-21 | +| #016 | P3 | rec | "Big but not easy" structural + motion perf | Deferred larger levers: (a) nonce-CSP forces every product route to `╞Æ Dynamic` (zero static generation) — evaluate Partial Prerendering / static shells for the static clinical catalogues (DSM/differentials/therapy/specifiers/formulation); (b) sidebar expand/collapse animates `grid-template-columns` (biggest smoothness cost, motion-gated — needs a transform-overlay rethink); (c) Therapy Compass fetches 692 KB / 2.5 MB JSON client-side (defer until interaction + confirm brotli); (d) settings/setup/admin dialogs static-imported into the home chunk (`next/dynamic` them). | session 2026-07-21 (build route table + design audit) | 2026-07-21 | +| #017 | P3 | task | Field Web-Vitals baseline via live Lighthouse | In-sandbox runtime vitals were blocked (prod server hard-requires Supabase secrets; dev-mode CLS measured excellent at 0.00–0.04, content-first pages 0.000). Run Lighthouse against `psychiatry.tools` for real LCP/INP/CLS to prioritize #012–#016 by measured impact rather than reasoning. | session 2026-07-21 (measurement pass) | 2026-07-21 | +| #018 | P2 | task | Split the lithium, ADHD and metabolic residuals by mechanism | Revalidated on current main 2026-07-23: these are not one composer defect. Lithium reproduced an unrelated-table retrieval fast-path defect; ADHD retrieves a relevant chart-heavy CAMHS source but exhausts the extractive route budget; metabolic retrieves the correct AKG source but selects schedule-free prose. The narrow lithium subject-evidence guard improved targeting from 0 to 1 with golden recall 1.0 and no reciprocal-rank regressions, but it was reverted because the full canary failed. After #051 stabilises the canary, add independent current-main reproducers and assess each mechanism separately. Do not widen the matcher or combine these into a broad ranking/composer change. | runs `30007833352` and `30009207429`; PR #1093; session 2026-07-23 | 2026-07-21 | +| #019 | P2 | task | Admission doc dropped after deterministic comparison packing | Reconfirmed in both baseline `30018289898` and scheduled run `30216191889`: retrieval remained 36/36 and retrieved the admission source, while both admission/discharge answer cases omitted it after comparison fallback. PR #1096 already proves deterministic answer ranking and cross-document packing retain both sources, so retrieval scores, aliases and comparator ordering are not the fix. Next: create a red fallback-layer unit reproducer using the live source shape; any behavior change needs a passing baseline/post canary. | runs `30018289898`/`30216191889`; PR #1096; session 2026-07-27 | 2026-07-21 | +| #021 | P3 | rec | E-3d H2 residual: strong/comparison generation discards | approx. 6 generation attempts per full 44-case run still fail the final quality gate and fall to extractive on strong-route comparison/complex shapes (the designed-conservative outcome). PARKED: weakest cost/benefit on the queue — a wave (approx. $2-4 pair + reviewer cycle) to shave seconds off a few hard cases. Revisit only if latency/waste complaints or a cheaper lever appears. | E-3c design record; runs #59-#61 diagnostics | 2026-07-21 | +| #022 | P2 | task | Source-governance metadata refresh (operator) | **Worklist generated 2026-07-22 ($0, read-only): `docs/source-governance-refresh-worklist-2026-07-22.md`.** Reframed - this is NOT 59 clinical reviews. Of the 124 documents surfacing in canary top results, 59 are review-required, and **38 (64 pct) are the BMJ published-reference tier all sitting at `clinical_validation_status: unverified`** - one attestation-policy decision, not 38 reviews. The remaining 21 are genuine local WA health-service reviews (FSH 7, NMHS 4, CAMHS 3, AKG 2, KEMH 2, RPBG 2, RKPG 1), mostly `document_status: review_due`. Burn-down: top-10 documents clear 44 pct of flagged slots, top-20 clear 66 pct. Next: decide the BMJ attestation policy, then attest local docs by visibility (start `Clozapine Management by GP (NMHS)`, 22 slots at rank 1). | runs #61/#57 Source Governance data; `docs/source-governance-refresh-worklist-2026-07-22.md` | 2026-07-21 | +| #023 | P2 | task | Complete scheduled browser and labeling disposition | The 2026-07-26 canary artifacts are read and disposed under resolved #051. Scheduled CI run `30216361999` failed its existing production dependency audit before the dependent Firefox/WebKit job, while production Chromium passed. After the already-open dependency PRs clear the audit, capture one scheduled/manual browser-matrix datapoint; separately record the human decision for the stable irrelevant-at-10 set. Do not rerun or spend on RAG for this item. | runs `30216191889`/`30216361999`; Dependabot PRs #1268/#1269; session 2026-07-27 | 2026-07-21 | +| #025 | P2 | task | Activate the three webhooks (operator secrets) | Merged (#968/#1100) + deployed but inert — verified live: `POST /api/webhooks/railway` returns `503 webhook_not_configured`; the Supabase document-change trigger exists but lacks both activation inputs. To turn on: (1) Railway ΓåÆ set `RAILWAY_WEBHOOK_SECRET` + add the `?token=…` webhook URL; (2) set `SLACK_WEBHOOK_URL`/`DISCORD_WEBHOOK_URL` in BOTH the Railway **app/server env** and **GitHub repo secrets**; (3) set one matching document-change secret in the Railway app env as `SUPABASE_INGESTION_WEBHOOK_SECRET` and in Supabase Vault as `ingestion_webhook_secret`, then set the per-environment database GUC `app.ingestion_webhook_base_url` to the deployed app origin. Each path fails closed until fully configured, so this is pure ops. See `docs/webhooks.md` for verification and rotation. | sessions 2026-07-22/24; PRs #968/#1100; docs/webhooks.md | 2026-07-22 | +| #027 | P3 | rec | External uptime monitor independent of GitHub/Railway | `live-domain-monitor.yml` runs on GitHub's cron, so it won't run in exactly the outage it should catch (Actions or the deploy itself down). Add an off-platform synthetic monitor (UptimeRobot / Better Stack / Checkly) hitting `/api/health` with a webhook alert. Provider setup, not code. | session 2026-07-22 webhook review | 2026-07-22 | +| #028 | P3 | rec | Runtime error tracking (Sentry or similar) | No error tracking in the repo — production exceptions on `psychiatry.tools`, including how often `RAG_PROVIDER_MODE=auto` silently degrades to source-only, are invisible. Weigh adding `@sentry/nextjs` (dependency + DSN secret + instrumentation) vs cost; alert ΓåÆ chat/issue. Provider-backed; needs explicit sign-off before adding the dependency. | session 2026-07-22 webhook review | 2026-07-22 | +| #029 | P2 | issue | 12 of 30 answer-quality cases return the fallback stub | run #61 --dump-answers: 12/30 quality cases emit the source_backed_review_fallback boilerplate with answer_sections: [], all grounded with 4-6 citations. Some still PASS targeting because the stub echoes query keywords (the contraindication/document_lookup matchers need only a keyword), so the targeting metric MASKS the problem for those intents. Superset of #018 — fix in the extractive composer, validate with the provider-backed answer eval. | run #61 dump artifact; session 2026-07-22 | 2026-07-22 | +| #033 | P3 | rec | Source governance metadata absent from the LLM prompt | `buildRagSourceBlock` omits `document_status`, `clinical_validation_status`, and `extraction_quality`, so the model cannot self-caveat during generation and governance is enforced only post-hoc. Generation-surface change: needs `eval:rag` plus `eval:quality --rag-only` (grounded-supported must not drop, citation-failure 0) and explicit approval. Carries the same "unknown Γëá bad" hazard as #032 — on a partially-enriched corpus the model would likely over-caveat correct sources, so design the prompt wording before spending an eval. | `src/lib/rag/rag-source-block.ts:126-198`; PR #1051 audit item 8 | 2026-07-22 | +| #035 | P3 | rec | Threshold-conflict detection covers only 3 params | `detectThresholdDisagreements` checks only ANC, WBC, and platelets paired with withholding verbs, so cross-source conflicts on medication doses, lithium/thyroid levels, or vital signs go undetected. Deliberately narrow (see the comment at `:469-474`). Broadening changes when an answer is classified `conflicting` and adds warnings — real false-positive risk. Needs new fixtures plus a behaviour review before any change. | `src/lib/evidence.ts:469-574`; PR #1051 audit item 7 | 2026-07-22 | +| #036 | P3 | rec | No explicit `is_public` visibility flag on documents | Public-corpus visibility is implicit: `owner_id IS NULL` on an `indexed` document (`resolveSearchScope`). The `metadata.public_corpus` marker is written by the promotion migrations but never used as a retrieval filter. Promotion is unconditional on `clinical_validation_status`, so unverified documents are publicly searchable — compensated by keeping `unverified_source` in the frontend-visible warning set. A hard schema flag touches RLS and the clinical-risk-gated retrieval RPCs; weigh against the existing compensating control before acting. | `supabase/schema.sql:61-108`; `src/lib/search-scope.ts:181-236`; PR #1051 audit item 3 | 2026-07-22 | +| #037 | P3 | rec | D5 trust-cap-all-claims flag parked OFF | `NEXT_PUBLIC_RAG_TRUST_CAP_ALL_CLAIMS` extends authority gating from high-risk claims to **all** supported claims (`deriveTrust`). Ships OFF by design; flipping it caps trust to `medium` for routine claims across the board — a product/clinical-UX decision, not a defect. Both states are test-pinned. Next action: product decision, then flip and re-baseline the UI expectations. | `src/lib/answer-render-policy.ts:159-177`; PR #1051 audit item 11 | 2026-07-22 | +| #038 | P3 | rec | Consolidate shared comparison behavior | Several clinical modes expose comparison workflows with similar selection, empty-state and mobile-dock needs. Define one shared behavioral contract before another comparison surface is added; keep mode-specific clinical content separate. This is a design-system recommendation, not a current defect. | design audit reconciliation; session 2026-07-22 | 2026-07-22 | +| #039 | P3 | rec | Consolidate catalogue toolbar patterns | Catalogue/search pages have independently evolved filter, sort, result-count and mobile toolbar behavior. Inventory the existing implementations and converge only the repeated interaction contract; do not flatten mode-specific search semantics. | design audit reconciliation; session 2026-07-22 | 2026-07-22 | +| #040 | P3 | rec | Add targeted visual-regression baselines | Keep a small approved baseline set for high-value desktop/mobile surfaces and accessibility modes instead of screenshotting every route. Start with account/settings, document viewer, mode homes and bottom-composer interactions; define an intentional-update workflow before enabling blocking comparisons. | design audit reconciliation; session 2026-07-22 | 2026-07-22 | +| #079 | P3 | task | Disposition retained worktrees in bounded cleanup batches | **Outcome:** the retained reconciliation tail is gradually classified without another disruptive all-worktree sweep. **Next:** process no more than ten worktrees per explicitly scheduled pass using current owner/process metadata, open-PR state, exact review-ledger coverage, ancestry, and cherry-pick-aware content proof. **Success:** remove only clean, inactive, bundled worktrees whose content is merged or explicitly rejected; record every disposition and retain recovery evidence. **Stop:** preserve dirty, active, secret-bearing, post-freeze, paused, or ambiguous work and never use reset, force deletion, broad clean, or process killing. | final reconciliation inventory retained 104 independent worktrees; session 2026-07-24 | 2026-07-24 | +| #080 | P3 | rec | Re-test the removed admission-to-discharge alias widening | **Outcome:** decide with measured evidence whether the user-approved 2026-07-21 widening (NMHS admission-to-discharge titles satisfying the admission expectation) belongs back on the wide-tier `AdmissionCommunityPts` list. #030 removed it so one document could not fill both comparison slots, but coverage now enforces distinct document identities by maximum matching, so the widening is no longer a false-pass route and its removal may instead cost legitimate admission credit. **Next:** with approval, run an eval-canary baseline/post pair over the admission-discharge comparison cases with and without those two titles. **Success:** restore only on measured non-regression (recall pinned 1.0, zero per-case reciprocal-rank regressions); otherwise record keep-removed and close. **Stop:** never restore on offline reasoning alone. | `src/lib/eval-document-matching.ts`; #030 archive row; session 2026-07-25 | 2026-07-25 | ## Resolved / archive @@ -128,6 +127,7 @@ Move resolved rows here with the resolution date and a one-line outcome. Keep th | ID | Type | Summary | Outcome | Resolved | | ---- | ----- | ---------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- | +| #012 | rec | Slim the lazy cross-mode differentials chunk | Precomputed a trimmed index (`src/data/cross-mode-differentials-index.json` via `scripts/build-cross-mode-differentials-index.mjs`) so the lazily-loaded cross-mode chunk imports a ~53 KB catalog instead of statically pulling the ~1.2 MB differentials snapshot (only that dynamic path reached it). A drift test plus `check:cross-mode-index` (in verify:cheap) lock the index to the live projection. | 2026-07-27 | | #082 | issue | Bot branch-sync heads leave required checks unapproved | Retired the automatic `GITHUB_TOKEN` PR branch-update workflow instead of weakening required-check approvals or introducing a privileged automation token. The existing helper remains dry-run by default, verifies its apply identity, and refuses missing or bot identities. The fast GitHub Actions policy check rejects both direct workflow `update-branch` calls and indirect apply-helper invocation, preventing recurrence without adding runtime or hosted-wait overhead. | 2026-07-27 | | #083 | issue | Documents-only universal search timed out on staging tenancy | A current staging nightly reproducer showed the documents-only search losing its synthetic fixture after the federated typeahead timeout was reduced to 750 ms. The search now retains 750 ms for multi-domain requests and uses the established 6,000 ms budget only when documents are the sole requested domain; fake-timer coverage proves both paths. RAG impact: no retrieval, ranking, ordering, alias, score, or result-selection change—only availability of the explicitly focused request. | 2026-07-27 | | #058 | task | Verify production content before any seed write | Read-only production counts on project `sjrfecxgysukkwxsowpy` found 276 clinical registry, 328 medication, and 232 differential records. The required tables are non-empty, so no seed or production write was needed. | 2026-07-27 | diff --git a/package.json b/package.json index dc602114bb..57e4b1eb3f 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,7 @@ "clean:worktree": "node scripts/clean-worktree.mjs", "verify:preflight": "npm run check:installed-lock-parity && npm run typecheck && npm run verify:cheap && npm run clean:worktree", "verify:cheap": "npm run verify:cheap:internal", - "verify:cheap:internal": "npm run check:runtime && npm run check:installed-lock-parity && npm run check:github-actions && npm run check:ci-scope && npm run check:ci-triage && npm run check:pr-policy && npm run check:gate-manifest && npm run check:branch-review-ledger && npm run sitemap:check && npm run docs:check-index && npm run docs:check-scripts && npm run docs:check-links && npm run check:knip && npm run check:maintainability-budgets && npm run brand:check && npm run check:assets && npm run check:therapy-data-index && npm run check:type-scale && npm run check:icon-scale && npm run check:design-system-contract && npm run check:migration-role && npm run check:function-grants && npm run check:owner-scope && npm run lint && npm run typecheck && npm run test", + "verify:cheap:internal": "npm run check:runtime && npm run check:installed-lock-parity && npm run check:github-actions && npm run check:ci-scope && npm run check:ci-triage && npm run check:pr-policy && npm run check:gate-manifest && npm run check:branch-review-ledger && npm run sitemap:check && npm run docs:check-index && npm run docs:check-scripts && npm run docs:check-links && npm run check:knip && npm run check:maintainability-budgets && npm run brand:check && npm run check:assets && npm run check:therapy-data-index && npm run check:cross-mode-index && npm run check:type-scale && npm run check:icon-scale && npm run check:design-system-contract && npm run check:migration-role && npm run check:function-grants && npm run check:owner-scope && npm run lint && npm run typecheck && npm run test", "verify:pr-local": "node scripts/verify-pr-local.mjs", "verify:phone-chrome": "node scripts/verify-phone-chrome.mjs", "audit:final-merge": "node scripts/final-merge-audit.mjs", @@ -77,6 +77,7 @@ "brand:update": "node scripts/run-tsx.mjs scripts/generate-brand-assets.ts", "brand:check": "node scripts/run-tsx.mjs scripts/generate-brand-assets.ts --check", "check:therapy-data-index": "node scripts/build-therapies-index.mjs --check", + "check:cross-mode-index": "node scripts/build-cross-mode-differentials-index.mjs --check", "check:runtime": "node scripts/run-tsx.mjs scripts/check-runtime.ts", "check:installed-lock-parity": "node scripts/check-installed-lock-parity.mjs", "check:codex-autofix-workflow": "node scripts/check-codex-autofix-workflow.mjs", diff --git a/scripts/build-cross-mode-differentials-index.mjs b/scripts/build-cross-mode-differentials-index.mjs new file mode 100644 index 0000000000..c2d96f11d5 --- /dev/null +++ b/scripts/build-cross-mode-differentials-index.mjs @@ -0,0 +1,78 @@ +// Generates src/data/cross-mode-differentials-index.json — a trimmed projection of +// data/differentials-snapshot.json for the cross-mode "Also in your library" strip. +// +// The full ~1.2 MB snapshot backs the Differentials mode. The cross-mode links only +// need a tiny {slug,title,clinicalHinge} diagnosis catalog plus presentation identity +// and the search-alias map. Importing this precomputed index (instead of +// @/lib/differentials) keeps the lazily-loaded cross-mode chunk from pulling the whole +// snapshot. Re-run after editing the snapshot, then run Prettier (which owns the +// committed file's exact formatting): +// +// node scripts/build-cross-mode-differentials-index.mjs && npm run format +// +// `--check` fails (non-zero) when the committed index is stale, comparing parsed +// values so Prettier's formatting never trips the gate. tests/cross-mode-differentials- +// index.test.ts additionally asserts the committed index equals the live projection. +import { readFileSync, writeFileSync } from "node:fs"; +import { dirname, join } from "node:path"; +import { fileURLToPath } from "node:url"; + +const root = join(dirname(fileURLToPath(import.meta.url)), ".."); +const source = join(root, "data", "differentials-snapshot.json"); +const target = join(root, "src", "data", "cross-mode-differentials-index.json"); +const checkOnly = process.argv.includes("--check"); + +const snapshot = JSON.parse(readFileSync(source, "utf8")); + +// Bare-number aliases (e.g. a field-weight "1.1" leaked from snapshot template +// metadata) would match unrelated records by substring — drop them, mirroring +// differentialSearchAliases() in src/lib/differentials.ts. +const isBareNumber = (value) => /^\d+(\.\d+)?$/.test(value.trim()); + +const catalog = { + diagnoses: snapshot.diagnoses.map((diagnosis) => ({ + slug: diagnosis.slug, + title: diagnosis.title, + clinicalHinge: diagnosis.clinicalHinge, + })), + presentations: snapshot.presentations.map((presentation) => ({ + id: presentation.id, + title: presentation.title, + subtitle: presentation.subtitle, + })), + aliases: Object.fromEntries( + Object.entries(snapshot.searchAliases) + .map(([token, aliases]) => [token, aliases.filter((alias) => !isBareNumber(alias))]) + .filter(([, aliases]) => aliases.length > 0), + ), +}; + +// Escape any embedded `sk-` so an ordinary word never trips the secret scanners on +// the committed artifact. `k` decodes back to `k`, so parsed values are exact. +const expected = `${JSON.stringify(catalog, null, 2).replace(/(?<=[A-Za-z0-9])sk-/g, "s\\u006b-")}\n`; + +const summary = `${catalog.diagnoses.length} diagnoses, ${catalog.presentations.length} presentations, ${Object.keys(catalog.aliases).length} alias keys`; + +if (checkOnly) { + let actual = ""; + try { + actual = readFileSync(target, "utf8"); + } catch { + throw new Error(`Missing generated cross-mode differentials index: ${target}`); + } + let parsed; + try { + parsed = JSON.parse(actual); + } catch { + throw new Error(`Cross-mode differentials index is invalid JSON: ${target}`); + } + if (JSON.stringify(parsed) !== JSON.stringify(catalog)) { + throw new Error( + `Cross-mode differentials index is stale: ${target} — re-run \`node scripts/build-cross-mode-differentials-index.mjs\`.`, + ); + } + console.log(`Cross-mode differentials index is current (${summary}).`); +} else { + writeFileSync(target, expected); + console.log(`Wrote cross-mode differentials index (${summary}) to ${target}.`); +} diff --git a/src/data/cross-mode-differentials-index.json b/src/data/cross-mode-differentials-index.json new file mode 100644 index 0000000000..8456058593 --- /dev/null +++ b/src/data/cross-mode-differentials-index.json @@ -0,0 +1,1188 @@ +{ + "diagnoses": [ + { + "slug": "acute-dystonia", + "title": "Acute dystonia", + "clinicalHinge": "Subjective inner restlessness is the key feature, often with observable motor restlessness." + }, + { + "slug": "acute-psychosis", + "title": "Acute psychosis", + "clinicalHinge": "Subjective bodily torment and urge to move dominate. Mood or psychosis may be absent." + }, + { + "slug": "acute-stress-reaction-adjustment-after-trauma", + "title": "Acute stress reaction", + "clinicalHinge": "Trauma-linked clusters dominate and often worsen with reminders." + }, + { + "slug": "adhd", + "title": "ADHD", + "clinicalHinge": "Shifts are reactive, brief, and relationship-linked rather than sustained episodic elevation with reduced need for sleep." + }, + { + "slug": "adjustment-crisis-demoralisation-severe-medical-suffering", + "title": "Adjustment crisis", + "clinicalHinge": "Sustained wish to die or escape, often with cognitive constriction and pessimism." + }, + { + "slug": "adverse-effect-driven-non-adherence", + "title": "Adverse-effect-driven non-adherence", + "clinicalHinge": "Altered reality testing drives refusal rather than simple preference." + }, + { + "slug": "akathisia", + "title": "Akathisia", + "clinicalHinge": "Subjective inner restlessness is the key feature, often with observable motor restlessness." + }, + { + "slug": "alcohol", + "title": "Alcohol", + "clinicalHinge": "Toxidrome clues: pupils, respiratory rate, nystagmus, tremor, diaphoresis, sympathetic overdrive." + }, + { + "slug": "alcohol-withdrawal", + "title": "Alcohol withdrawal", + "clinicalHinge": "Timing after last drink plus autonomic overdrive and tremor are classic." + }, + { + "slug": "alcohol-related-brain-injury-korsakoff-pattern", + "title": "Alcohol-related brain injury", + "clinicalHinge": "Acute and fluctuating with inattention = delirium (medical/toxic cause)." + }, + { + "slug": "alzheimer-vascular-ftd-with-psychosis", + "title": "Alzheimer", + "clinicalHinge": "Inattention and fluctuating awareness are the key separators; psychotic content is often fragmentary." + }, + { + "slug": "alzheimer-disease", + "title": "Alzheimer disease", + "clinicalHinge": "Acute and fluctuating with inattention = delirium (medical/toxic cause)." + }, + { + "slug": "anorexia-nervosa", + "title": "Anorexia nervosa", + "clinicalHinge": "Weight/shape psychopathology is the key psychological discriminator." + }, + { + "slug": "anticholinergic-burden-toxicity", + "title": "Anticholinergic burden", + "clinicalHinge": "Temporal association with antipsychotic initiation, dose change, or depot timing." + }, + { + "slug": "antidepressant-discontinuation-syndrome", + "title": "Antidepressant discontinuation syndrome", + "clinicalHinge": "Timing after last drink plus autonomic overdrive and tremor are classic." + }, + { + "slug": "antidepressant-induced-switching", + "title": "Antidepressant-induced switching", + "clinicalHinge": "Sustained episodic activation plus decreased need for sleep are the key separators." + }, + { + "slug": "antipsychotic-adverse-effects", + "title": "Antipsychotic adverse effects", + "clinicalHinge": "Temporal association with antipsychotic initiation, dose change, or depot timing." + }, + { + "slug": "antisocial-personality-disorder", + "title": "Antisocial personality disorder", + "clinicalHinge": "Interpersonal sensitivity, fear of abandonment, identity disturbance, recurrent dysregulation." + }, + { + "slug": "arfid", + "title": "ARFID", + "clinicalHinge": "Weight/shape psychopathology is the key psychological discriminator." + }, + { + "slug": "autism-intellectual-disability-sensory-overload", + "title": "Autism", + "clinicalHinge": "Aggression is often fear- or belief-driven rather than predatory." + }, + { + "slug": "autism-id-communication-disorder", + "title": "Autism", + "clinicalHinge": "Formal thought disorder plus broader psychotic syndrome and functional deterioration." + }, + { + "slug": "autism-intellectual-disability-baseline", + "title": "Autism", + "clinicalHinge": "Chronicity and psychosis history support primary negative symptoms." + }, + { + "slug": "autism-developmental-social-communication-difference", + "title": "Autism", + "clinicalHinge": "Interpersonal sensitivity, fear of abandonment, identity disturbance, recurrent dysregulation." + }, + { + "slug": "autism-spectrum-disorder", + "title": "Autism spectrum disorder", + "clinicalHinge": "Developmental onset and restricted/repetitive or sensory features are central." + }, + { + "slug": "autism-with-overload-shutdown-meltdown", + "title": "Autism with overload", + "clinicalHinge": "Shifts are reactive, brief, and relationship-linked rather than sustained episodic elevation with reduced need for sleep." + }, + { + "slug": "autistic-shutdown-regression-catatonia-on-developmental-baseline", + "title": "Autistic shutdown", + "clinicalHinge": "Developmental onset and restricted/repetitive or sensory features are central." + }, + { + "slug": "autoimmune-encephalitis-autoimmune-psychosis", + "title": "Autoimmune encephalitis", + "clinicalHinge": "Inattention and fluctuation dominate. Psychotic content is often fragmented or changeable." + }, + { + "slug": "autoimmune-encephalitis-anti-nmda-syndrome", + "title": "Autoimmune encephalitis", + "clinicalHinge": "Characteristic motor syndrome rather than simple low mood, psychosis, or \"behavioural refusal\". Mood disorders are a very common substrate." + }, + { + "slug": "benzodiazepine-withdrawal", + "title": "Benzodiazepine withdrawal", + "clinicalHinge": "Timing after last drink plus autonomic overdrive and tremor are classic." + }, + { + "slug": "benzodiazepines", + "title": "Benzodiazepines", + "clinicalHinge": "Toxidrome clues: pupils, respiratory rate, nystagmus, tremor, diaphoresis, sympathetic overdrive." + }, + { + "slug": "bipolar-depression-mixed-state", + "title": "Bipolar depression", + "clinicalHinge": "Sustained wish to die or escape, often with cognitive constriction and pessimism." + }, + { + "slug": "bipolar-depression", + "title": "Bipolar depression", + "clinicalHinge": "Sustained mood syndrome with guilt, hopelessness, reduced energy, impaired concentration, change in sleep/appetite." + }, + { + "slug": "bipolar-disorder", + "title": "Bipolar disorder", + "clinicalHinge": "Shifts are reactive, brief, and relationship-linked rather than sustained episodic elevation with reduced need for sleep." + }, + { + "slug": "bipolar-disorder-or-psychosis-mistaken-for-personality", + "title": "Bipolar disorder or psychosis mistaken for personality", + "clinicalHinge": "Interpersonal sensitivity, fear of abandonment, identity disturbance, recurrent dysregulation." + }, + { + "slug": "bipolar-disorder-with-psychosis", + "title": "Bipolar disorder with psychosis", + "clinicalHinge": "Inattention and fluctuation dominate. Psychotic content is often fragmented or changeable." + }, + { + "slug": "bipolar-i-mania", + "title": "Bipolar I mania", + "clinicalHinge": "Sustained episodic activation plus decreased need for sleep are the key separators." + }, + { + "slug": "bipolar-mania-hypomania", + "title": "Bipolar mania", + "clinicalHinge": "Sleep disturbance sits inside a broader syndrome. Reduced need for sleep is one of the most decisive bipolar bedside clues." + }, + { + "slug": "bipolar-mania-mixed-state", + "title": "Bipolar mania", + "clinicalHinge": "Subjective bodily torment and urge to move dominate. Mood or psychosis may be absent." + }, + { + "slug": "bipolar-mania-with-psychosis", + "title": "Bipolar mania with psychosis", + "clinicalHinge": "Often fixed, systematised, accompanied by hallucinations, thought disorder, or negative symptoms." + }, + { + "slug": "bipolar-relapse-postpartum-mania", + "title": "Bipolar relapse", + "clinicalHinge": "Psychotic or manic syndrome with abrupt change from baseline in the postpartum period." + }, + { + "slug": "body-dysmorphic-disorder", + "title": "Body dysmorphic disorder", + "clinicalHinge": "Abrupt peak over minutes, recurrent unexpected attacks, anticipatory anxiety or avoidance." + }, + { + "slug": "borderline-trauma-reactive-escalation", + "title": "Borderline", + "clinicalHinge": "Aggression is often fear- or belief-driven rather than predatory." + }, + { + "slug": "borderline-affective-instability-adhd-trait-impulsivity", + "title": "Borderline affective instability", + "clinicalHinge": "Sustained episodic activation plus decreased need for sleep are the key separators." + }, + { + "slug": "borderline-personality-stress-micropsychosis", + "title": "Borderline personality", + "clinicalHinge": "Usually chronic, internally generated but experienced as alien, embedded in broader psychotic syndrome." + }, + { + "slug": "borderline-personality-disorder", + "title": "Borderline personality disorder", + "clinicalHinge": "Shifts are reactive, brief, and relationship-linked rather than sustained episodic elevation with reduced need for sleep." + }, + { + "slug": "borderline-personality-pathway-recurrent-self-harm", + "title": "Borderline personality pathway", + "clinicalHinge": "Sustained wish to die or escape, often with cognitive constriction and pessimism." + }, + { + "slug": "borderline-stress-dissociation", + "title": "Borderline stress-dissociation", + "clinicalHinge": "Trauma-linked clusters dominate and often worsen with reminders." + }, + { + "slug": "bpsd-as-unmet-need-delirium-pain-mimic", + "title": "BPSD as unmet need", + "clinicalHinge": "Acute and fluctuating with inattention = delirium (medical/toxic cause)." + }, + { + "slug": "bulimia-nervosa-binge-purge-pattern", + "title": "Bulimia nervosa", + "clinicalHinge": "Weight/shape psychopathology is the key psychological discriminator." + }, + { + "slug": "cannabis", + "title": "Cannabis", + "clinicalHinge": "Toxidrome clues: pupils, respiratory rate, nystagmus, tremor, diaphoresis, sympathetic overdrive." + }, + { + "slug": "cannabis-withdrawal", + "title": "Cannabis withdrawal", + "clinicalHinge": "Timing after last drink plus autonomic overdrive and tremor are classic." + }, + { + "slug": "catatonia-severe-psychomotor-shutdown", + "title": "Catatonia", + "clinicalHinge": "Chronicity and psychosis history support primary negative symptoms." + }, + { + "slug": "catatonia-dementia-dysphagia-apraxia", + "title": "Catatonia", + "clinicalHinge": "Weight/shape psychopathology is the key psychological discriminator." + }, + { + "slug": "catatonia", + "title": "Catatonia", + "clinicalHinge": "Motor syndrome with characteristic catatonic signs. Not simply very depressed or non-engaging." + }, + { + "slug": "catatonia-in-mood-disorder", + "title": "Catatonia in mood disorder", + "clinicalHinge": "Characteristic motor syndrome rather than simple low mood, psychosis, or \"behavioural refusal\". Mood disorders are a very common substrate." + }, + { + "slug": "catatonia-in-psychotic-disorder", + "title": "Catatonia in psychotic disorder", + "clinicalHinge": "Characteristic motor syndrome rather than simple low mood, psychosis, or \"behavioural refusal\". Mood disorders are a very common substrate." + }, + { + "slug": "catatonic-excitement", + "title": "Catatonic excitement", + "clinicalHinge": "Subjective bodily torment and urge to move dominate. Mood or psychosis may be absent." + }, + { + "slug": "clozapine-specific-adverse-effects-toxicity", + "title": "Clozapine-specific adverse effects", + "clinicalHinge": "Temporal association with antipsychotic initiation, dose change, or depot timing." + }, + { + "slug": "cognitive-impairment-id-dementia", + "title": "Cognitive impairment", + "clinicalHinge": "Altered reality testing drives refusal rather than simple preference." + }, + { + "slug": "command-phenomena-and-internal-parts-experiences", + "title": "Command phenomena and internal \"parts\" experiences", + "clinicalHinge": "Often fixed, systematised, accompanied by hallucinations, thought disorder, or negative symptoms." + }, + { + "slug": "complex-ptsd", + "title": "Complex PTSD", + "clinicalHinge": "Trauma-linked clusters dominate and often worsen with reminders." + }, + { + "slug": "complex-ptsd-ptsd", + "title": "Complex PTSD", + "clinicalHinge": "Shifts are reactive, brief, and relationship-linked rather than sustained episodic elevation with reduced need for sleep." + }, + { + "slug": "cyclothymia-subthreshold-bipolarity", + "title": "Cyclothymia", + "clinicalHinge": "Shifts are reactive, brief, and relationship-linked rather than sustained episodic elevation with reduced need for sleep." + }, + { + "slug": "delirium", + "title": "Delirium", + "clinicalHinge": "Inattention plus altered awareness/arousal are the key separators. Usually precipitated by acute illness, surgery, injury, retention, constipation, pain, or medicines." + }, + { + "slug": "delusional-disorder", + "title": "Delusional disorder", + "clinicalHinge": "Often fixed, systematised, accompanied by hallucinations, thought disorder, or negative symptoms." + }, + { + "slug": "dementia-neurocognitive-disorder", + "title": "Dementia", + "clinicalHinge": "Chronicity and psychosis history support primary negative symptoms." + }, + { + "slug": "dementia-apathy-neurocognitive-disorder", + "title": "Dementia", + "clinicalHinge": "Motor syndrome with characteristic catatonic signs. Not simply very depressed or non-engaging." + }, + { + "slug": "dementia-with-lewy-bodies-parkinson-disease-dementia", + "title": "Dementia with Lewy bodies", + "clinicalHinge": "Inattention and fluctuating awareness are the key separators; psychotic content is often fragmentary." + }, + { + "slug": "dementia-with-superimposed-delirium", + "title": "Dementia with superimposed delirium", + "clinicalHinge": "Inattention plus altered awareness/arousal are the key separators. Usually precipitated by acute illness, surgery, injury, retention, constipation, pain, or medicines." + }, + { + "slug": "depression", + "title": "Depression", + "clinicalHinge": "Chronicity and psychosis history support primary negative symptoms." + }, + { + "slug": "depression-anxiety-with-bodily-amplification", + "title": "Depression", + "clinicalHinge": "Driven by excessive time, energy, and distress around symptoms, not by symptoms being fake." + }, + { + "slug": "depression-due-to-medical-condition", + "title": "Depression due to medical condition", + "clinicalHinge": "Sustained mood syndrome with guilt, hopelessness, reduced energy, impaired concentration, change in sleep/appetite." + }, + { + "slug": "depression-related-cognitive-syndrome", + "title": "Depression-related cognitive syndrome", + "clinicalHinge": "Acute and fluctuating with inattention = delirium (medical/toxic cause)." + }, + { + "slug": "depression-related-poor-intake", + "title": "Depression-related poor intake", + "clinicalHinge": "Weight/shape psychopathology is the key psychological discriminator." + }, + { + "slug": "dissociative-disorder", + "title": "Dissociative disorder", + "clinicalHinge": "Trauma-linked clusters dominate and often worsen with reminders." + }, + { + "slug": "dissociative-shutdown-freeze", + "title": "Dissociative shutdown", + "clinicalHinge": "Motor syndrome with characteristic catatonic signs. Not simply very depressed or non-engaging." + }, + { + "slug": "drug-induced-parkinsonism", + "title": "Drug-induced parkinsonism", + "clinicalHinge": "Subjective inner restlessness is the key feature, often with observable motor restlessness." + }, + { + "slug": "factitious-malingering", + "title": "Factitious", + "clinicalHinge": "Weight/shape psychopathology is the key psychological discriminator." + }, + { + "slug": "factitious-disorder-malingering", + "title": "Factitious disorder", + "clinicalHinge": "Driven by excessive time, energy, and distress around symptoms, not by symptoms being fake." + }, + { + "slug": "frontal-disinhibition-bvftd-intoxication", + "title": "Frontal disinhibition", + "clinicalHinge": "Interpersonal sensitivity, fear of abandonment, identity disturbance, recurrent dysregulation." + }, + { + "slug": "frontal-lobe-syndrome-bvftd-neurological-disorder", + "title": "Frontal lobe syndrome", + "clinicalHinge": "Shifts are reactive, brief, and relationship-linked rather than sustained episodic elevation with reduced need for sleep." + }, + { + "slug": "frontal-syndrome-bvftd-neurological-disinhibition", + "title": "Frontal syndrome", + "clinicalHinge": "Chronicity and psychosis history support primary negative symptoms." + }, + { + "slug": "frontotemporal-dementia", + "title": "Frontotemporal dementia", + "clinicalHinge": "Acute and fluctuating with inattention = delirium (medical/toxic cause)." + }, + { + "slug": "functional-neurological-disorder", + "title": "Functional neurological disorder", + "clinicalHinge": "Trauma-linked clusters dominate and often worsen with reminders." + }, + { + "slug": "functional-neurological-symptom-disorder", + "title": "Functional neurological symptom disorder", + "clinicalHinge": "Driven by excessive time, energy, and distress around symptoms, not by symptoms being fake." + }, + { + "slug": "gad-worry-depressive-rumination", + "title": "GAD worry", + "clinicalHinge": "Distinguishes intrusive/obsessional phenomena from psychotic or violent intent." + }, + { + "slug": "generalised-anxiety-disorder", + "title": "Generalised anxiety disorder", + "clinicalHinge": "Abrupt peak over minutes, recurrent unexpected attacks, anticipatory anxiety or avoidance." + }, + { + "slug": "generalised-anxiety-disorder-panic", + "title": "Generalised anxiety disorder", + "clinicalHinge": "Sleep disturbance sits inside a broader syndrome. Reduced need for sleep is one of the most decisive bipolar bedside clues." + }, + { + "slug": "genuine-neurological-or-medical-disease", + "title": "Genuine neurological or medical disease", + "clinicalHinge": "Driven by excessive time, energy, and distress around symptoms, not by symptoms being fake." + }, + { + "slug": "genuine-violent-homicidal-intent", + "title": "Genuine violent", + "clinicalHinge": "Separates perinatal intrusive thoughts from postpartum psychosis and genuine harm intent." + }, + { + "slug": "ghb", + "title": "GHB", + "clinicalHinge": "Toxidrome clues: pupils, respiratory rate, nystagmus, tremor, diaphoresis, sympathetic overdrive." + }, + { + "slug": "ghb-withdrawal", + "title": "GHB withdrawal", + "clinicalHinge": "Timing after last drink plus autonomic overdrive and tremor are classic." + }, + { + "slug": "grief-prolonged-grief-adjustment-depression", + "title": "Grief", + "clinicalHinge": "Sustained mood syndrome with guilt, hopelessness, reduced energy, impaired concentration, change in sleep/appetite." + }, + { + "slug": "hallucinogens", + "title": "Hallucinogens", + "clinicalHinge": "Toxidrome clues: pupils, respiratory rate, nystagmus, tremor, diaphoresis, sympathetic overdrive." + }, + { + "slug": "hepatic-encephalopathy", + "title": "Hepatic encephalopathy", + "clinicalHinge": "Inattention plus altered awareness/arousal are the key separators. Usually precipitated by acute illness, surgery, injury, retention, constipation, pain, or medicines." + }, + { + "slug": "histrionic-personality-disorder", + "title": "Histrionic personality disorder", + "clinicalHinge": "Interpersonal sensitivity, fear of abandonment, identity disturbance, recurrent dysregulation." + }, + { + "slug": "hypoactive-delirium", + "title": "Hypoactive delirium", + "clinicalHinge": "Characteristic motor syndrome rather than simple low mood, psychosis, or \"behavioural refusal\". Mood disorders are a very common substrate." + }, + { + "slug": "hypomania", + "title": "Hypomania", + "clinicalHinge": "Sustained episodic activation plus decreased need for sleep are the key separators." + }, + { + "slug": "iatrogenic-environment-driven-escalation", + "title": "Iatrogenic", + "clinicalHinge": "Aggression is often fear- or belief-driven rather than predatory." + }, + { + "slug": "illness-anxiety-disorder", + "title": "Illness anxiety disorder", + "clinicalHinge": "Driven by excessive time, energy, and distress around symptoms, not by symptoms being fake." + }, + { + "slug": "inhalants-volatile-substances", + "title": "Inhalants", + "clinicalHinge": "Toxidrome clues: pupils, respiratory rate, nystagmus, tremor, diaphoresis, sympathetic overdrive." + }, + { + "slug": "intellectual-disability", + "title": "Intellectual disability", + "clinicalHinge": "Developmental onset and restricted/repetitive or sensory features are central." + }, + { + "slug": "late-life-psychotic-depression-bipolar-disorder", + "title": "Late-life psychotic depression", + "clinicalHinge": "Inattention and fluctuating awareness are the key separators; psychotic content is often fragmentary." + }, + { + "slug": "lewy-body-dementia-parkinson-disease-dementia", + "title": "Lewy body dementia", + "clinicalHinge": "Usually chronic, internally generated but experienced as alien, embedded in broader psychotic syndrome." + }, + { + "slug": "lithium-physiological-withdrawal-tremor", + "title": "Lithium", + "clinicalHinge": "Subjective inner restlessness is the key feature, often with observable motor restlessness." + }, + { + "slug": "lithium-adverse-effects-toxicity", + "title": "Lithium adverse effects", + "clinicalHinge": "Temporal association with antipsychotic initiation, dose change, or depot timing." + }, + { + "slug": "major-depressive-disorder", + "title": "Major depressive disorder", + "clinicalHinge": "Sleep disturbance sits inside a broader syndrome. Reduced need for sleep is one of the most decisive bipolar bedside clues." + }, + { + "slug": "malingering-strategic-refusal", + "title": "Malingering", + "clinicalHinge": "Altered reality testing drives refusal rather than simple preference." + }, + { + "slug": "mania-mixed-state", + "title": "Mania", + "clinicalHinge": "Aggression is often fear- or belief-driven rather than predatory." + }, + { + "slug": "mania", + "title": "Mania", + "clinicalHinge": "Formal thought disorder plus broader psychotic syndrome and functional deterioration." + }, + { + "slug": "mania-hypomania", + "title": "Mania", + "clinicalHinge": "Altered reality testing drives refusal rather than simple preference." + }, + { + "slug": "medical-gi-endocrine-painful-organic-cause", + "title": "Medical", + "clinicalHinge": "Weight/shape psychopathology is the key psychological discriminator." + }, + { + "slug": "medical-or-physiological-mimic", + "title": "Medical or physiological mimic", + "clinicalHinge": "Abrupt peak over minutes, recurrent unexpected attacks, anticipatory anxiety or avoidance." + }, + { + "slug": "medication-over-sedation-drug-effect", + "title": "Medication over-sedation", + "clinicalHinge": "Motor syndrome with characteristic catatonic signs. Not simply very depressed or non-engaging." + }, + { + "slug": "medication-sedation-drug-induced-parkinsonism", + "title": "Medication sedation", + "clinicalHinge": "Chronicity and psychosis history support primary negative symptoms." + }, + { + "slug": "medication-toxicity-anticholinergic-burden-dopaminergic-or-steroid-psychosis", + "title": "Medication toxicity", + "clinicalHinge": "Inattention and fluctuating awareness are the key separators; psychotic content is often fragmentary." + }, + { + "slug": "methamphetamine-stimulant", + "title": "Methamphetamine", + "clinicalHinge": "Toxidrome clues: pupils, respiratory rate, nystagmus, tremor, diaphoresis, sympathetic overdrive." + }, + { + "slug": "mixed-state", + "title": "Mixed state", + "clinicalHinge": "Sustained episodic activation plus decreased need for sleep are the key separators." + }, + { + "slug": "mixed-state-agitated-depression", + "title": "Mixed state", + "clinicalHinge": "Sleep disturbance sits inside a broader syndrome. Reduced need for sleep is one of the most decisive bipolar bedside clues." + }, + { + "slug": "narcissistic-personality-disorder", + "title": "Narcissistic personality disorder", + "clinicalHinge": "Interpersonal sensitivity, fear of abandonment, identity disturbance, recurrent dysregulation." + }, + { + "slug": "neuroleptic-malignant-syndrome", + "title": "Neuroleptic malignant syndrome", + "clinicalHinge": "Characteristic motor syndrome rather than simple low mood, psychosis, or \"behavioural refusal\". Mood disorders are a very common substrate." + }, + { + "slug": "nicotine-withdrawal", + "title": "Nicotine withdrawal", + "clinicalHinge": "Timing after last drink plus autonomic overdrive and tremor are classic." + }, + { + "slug": "ocd", + "title": "OCD", + "clinicalHinge": "Abrupt peak over minutes, recurrent unexpected attacks, anticipatory anxiety or avoidance." + }, + { + "slug": "ocd-rigidity-mimic", + "title": "OCD", + "clinicalHinge": "Developmental onset and restricted/repetitive or sensory features are central." + }, + { + "slug": "opioid-withdrawal", + "title": "Opioid withdrawal", + "clinicalHinge": "Timing after last drink plus autonomic overdrive and tremor are classic." + }, + { + "slug": "opioids", + "title": "Opioids", + "clinicalHinge": "Toxidrome clues: pupils, respiratory rate, nystagmus, tremor, diaphoresis, sympathetic overdrive." + }, + { + "slug": "organic-late-onset-mania", + "title": "Organic late-onset \"mania\"", + "clinicalHinge": "Sustained episodic activation plus decreased need for sleep are the key separators." + }, + { + "slug": "osa-restless-legs-parasomnia", + "title": "OSA", + "clinicalHinge": "Sleep disturbance sits inside a broader syndrome. Reduced need for sleep is one of the most decisive bipolar bedside clues." + }, + { + "slug": "overvalued-idea", + "title": "Overvalued idea", + "clinicalHinge": "Distinguishes intrusive/obsessional phenomena from psychotic or violent intent." + }, + { + "slug": "panic-trauma-hyperarousal-dissociation", + "title": "Panic", + "clinicalHinge": "Subjective bodily torment and urge to move dominate. Mood or psychosis may be absent." + }, + { + "slug": "panic-hyperventilation-trauma-arousal-with-bodily-symptoms", + "title": "Panic", + "clinicalHinge": "Driven by excessive time, energy, and distress around symptoms, not by symptoms being fake." + }, + { + "slug": "panic-disorder", + "title": "Panic disorder", + "clinicalHinge": "Abrupt peak over minutes, recurrent unexpected attacks, anticipatory anxiety or avoidance." + }, + { + "slug": "paranoid-personality-disorder-overvalued-suspiciousness", + "title": "Paranoid personality disorder", + "clinicalHinge": "Often fixed, systematised, accompanied by hallucinations, thought disorder, or negative symptoms." + }, + { + "slug": "paranoid-personality-disorder", + "title": "Paranoid personality disorder", + "clinicalHinge": "Interpersonal sensitivity, fear of abandonment, identity disturbance, recurrent dysregulation." + }, + { + "slug": "perinatal-ocd-intrusive-infant-harm-thoughts", + "title": "Perinatal OCD", + "clinicalHinge": "Separates perinatal intrusive thoughts from postpartum psychosis and genuine harm intent." + }, + { + "slug": "persistent-depressive-disorder", + "title": "Persistent depressive disorder", + "clinicalHinge": "Sustained mood syndrome with guilt, hopelessness, reduced energy, impaired concentration, change in sleep/appetite." + }, + { + "slug": "personality-crisis-dissociation-trauma-overload", + "title": "Personality crisis", + "clinicalHinge": "Formal thought disorder plus broader psychotic syndrome and functional deterioration." + }, + { + "slug": "post-ictal-confusion", + "title": "Post-ictal state", + "clinicalHinge": "Inattention plus altered awareness/arousal are the key separators. Usually precipitated by acute illness, surgery, injury, retention, constipation, pain, or medicines." + }, + { + "slug": "postnatal-depression", + "title": "Postnatal depression", + "clinicalHinge": "Psychotic or manic syndrome with abrupt change from baseline in the postpartum period." + }, + { + "slug": "postpartum-psychosis", + "title": "Postpartum psychosis", + "clinicalHinge": "Separates perinatal intrusive thoughts from postpartum psychosis and genuine harm intent." + }, + { + "slug": "primary-insomnia-circadian-rhythm-disorder", + "title": "Primary insomnia", + "clinicalHinge": "Sleep disturbance sits inside a broader syndrome. Reduced need for sleep is one of the most decisive bipolar bedside clues." + }, + { + "slug": "psychosis-emerging-on-developmental-baseline", + "title": "Psychosis emerging on developmental baseline", + "clinicalHinge": "Developmental onset and restricted/repetitive or sensory features are central." + }, + { + "slug": "psychosis-driven-suicidality", + "title": "Psychosis-driven suicidality", + "clinicalHinge": "Sustained wish to die or escape, often with cognitive constriction and pessimism." + }, + { + "slug": "psychosis-related-food-refusal", + "title": "Psychosis-related food refusal", + "clinicalHinge": "Weight/shape psychopathology is the key psychological discriminator." + }, + { + "slug": "psychosis-related-poor-insight", + "title": "Psychosis-related poor insight", + "clinicalHinge": "Altered reality testing drives refusal rather than simple preference." + }, + { + "slug": "psychotic-delusion", + "title": "Psychotic delusion", + "clinicalHinge": "Distinguishes intrusive/obsessional phenomena from psychotic or violent intent." + }, + { + "slug": "psychotic-depression", + "title": "Psychotic depression", + "clinicalHinge": "Sustained mood syndrome with guilt, hopelessness, reduced energy, impaired concentration, change in sleep/appetite." + }, + { + "slug": "psychotic-depression-severe-melancholic-depression", + "title": "Psychotic depression", + "clinicalHinge": "Motor syndrome with characteristic catatonic signs. Not simply very depressed or non-engaging." + }, + { + "slug": "psychotic-depression-with-infant-harm-delusions", + "title": "Psychotic depression with infant-harm delusions", + "clinicalHinge": "Separates perinatal intrusive thoughts from postpartum psychosis and genuine harm intent." + }, + { + "slug": "ptsd-dissociation-linked-risk", + "title": "PTSD", + "clinicalHinge": "Sustained wish to die or escape, often with cognitive constriction and pessimism." + }, + { + "slug": "ptsd-dissociation-flashback-aggression", + "title": "PTSD", + "clinicalHinge": "Aggression is often fear- or belief-driven rather than predatory." + }, + { + "slug": "ptsd-trauma-re-experiencing", + "title": "PTSD", + "clinicalHinge": "Usually chronic, internally generated but experienced as alien, embedded in broader psychotic syndrome." + }, + { + "slug": "ptsd", + "title": "PTSD", + "clinicalHinge": "Trauma-linked clusters dominate and often worsen with reminders." + }, + { + "slug": "ptsd-hyperarousal-nightmares", + "title": "PTSD", + "clinicalHinge": "Sleep disturbance sits inside a broader syndrome. Reduced need for sleep is one of the most decisive bipolar bedside clues." + }, + { + "slug": "ptsd-complex-trauma", + "title": "PTSD", + "clinicalHinge": "Interpersonal sensitivity, fear of abandonment, identity disturbance, recurrent dysregulation." + }, + { + "slug": "ptsd-after-traumatic-birth-dissociative-state", + "title": "PTSD after traumatic birth", + "clinicalHinge": "Psychotic or manic syndrome with abrupt change from baseline in the postpartum period." + }, + { + "slug": "ptsd-after-traumatic-birth-dissociation", + "title": "PTSD after traumatic birth", + "clinicalHinge": "Separates perinatal intrusive thoughts from postpartum psychosis and genuine harm intent." + }, + { + "slug": "ptsd-hyperarousal-mislabelled-as-anxiety", + "title": "PTSD hyperarousal mislabelled as anxiety", + "clinicalHinge": "Abrupt peak over minutes, recurrent unexpected attacks, anticipatory anxiety or avoidance." + }, + { + "slug": "ptsd-hypervigilance-trauma-mistrust", + "title": "PTSD hypervigilance", + "clinicalHinge": "Often fixed, systematised, accompanied by hallucinations, thought disorder, or negative symptoms." + }, + { + "slug": "ptsd-intrusive-memories-flashbacks", + "title": "PTSD intrusive memories", + "clinicalHinge": "Distinguishes intrusive/obsessional phenomena from psychotic or violent intent." + }, + { + "slug": "qt-prolongation-ris\u006b-state", + "title": "QT prolongation risk state", + "clinicalHinge": "Temporal association with antipsychotic initiation, dose change, or depot timing." + }, + { + "slug": "schizophrenia", + "title": "Schizophrenia", + "clinicalHinge": "Formal thought disorder plus broader psychotic syndrome and functional deterioration." + }, + { + "slug": "schizophrenia-negative-syndrome", + "title": "Schizophrenia negative syndrome", + "clinicalHinge": "Motor syndrome with characteristic catatonic signs. Not simply very depressed or non-engaging." + }, + { + "slug": "schizophrenia-spectrum-delusions", + "title": "Schizophrenia-spectrum delusions", + "clinicalHinge": "Often fixed, systematised, accompanied by hallucinations, thought disorder, or negative symptoms." + }, + { + "slug": "schizophrenia-spectrum-disorder", + "title": "Schizophrenia-spectrum disorder", + "clinicalHinge": "Usually chronic, internally generated but experienced as alien, embedded in broader psychotic syndrome." + }, + { + "slug": "sedation-hypoarousal", + "title": "Sedation", + "clinicalHinge": "Subjective inner restlessness is the key feature, often with observable motor restlessness." + }, + { + "slug": "seizure-related-structural-later-life-neurological-psychosis", + "title": "Seizure-related", + "clinicalHinge": "Inattention and fluctuation dominate. Psychotic content is often fragmented or changeable." + }, + { + "slug": "sensory-impairment-charles-bonnet-hearing-related-misinterpretation", + "title": "Sensory impairment", + "clinicalHinge": "Inattention and fluctuating awareness are the key separators; psychotic content is often fragmentary." + }, + { + "slug": "serotonin-syndrome", + "title": "Serotonin syndrome", + "clinicalHinge": "Temporal association with antipsychotic initiation, dose change, or depot timing." + }, + { + "slug": "serotonin-toxicity", + "title": "Serotonin toxicity", + "clinicalHinge": "Characteristic motor syndrome rather than simple low mood, psychosis, or \"behavioural refusal\". Mood disorders are a very common substrate." + }, + { + "slug": "sleep-related-hallucinations-charles-bonnet-syndrome", + "title": "Sleep-related hallucinations", + "clinicalHinge": "Usually chronic, internally generated but experienced as alien, embedded in broader psychotic syndrome." + }, + { + "slug": "social-anxiety-disorder", + "title": "Social anxiety disorder", + "clinicalHinge": "Abrupt peak over minutes, recurrent unexpected attacks, anticipatory anxiety or avoidance." + }, + { + "slug": "somatic-symptom-disorder-illness-anxiety-disorder", + "title": "Somatic symptom disorder", + "clinicalHinge": "Trauma-linked clusters dominate and often worsen with reminders." + }, + { + "slug": "somatic-symptom-disorder", + "title": "Somatic symptom disorder", + "clinicalHinge": "Driven by excessive time, energy, and distress around symptoms, not by symptoms being fake." + }, + { + "slug": "ssri-snri-adverse-effects", + "title": "SSRI", + "clinicalHinge": "Temporal association with antipsychotic initiation, dose change, or depot timing." + }, + { + "slug": "stigma-identity-conflict-cultural-meaning", + "title": "Stigma", + "clinicalHinge": "Altered reality testing drives refusal rather than simple preference." + }, + { + "slug": "stimulant-intoxication-withdrawal", + "title": "Stimulant intoxication", + "clinicalHinge": "Subjective bodily torment and urge to move dominate. Mood or psychosis may be absent." + }, + { + "slug": "stimulant-withdrawal-crash", + "title": "Stimulant withdrawal", + "clinicalHinge": "Timing after last drink plus autonomic overdrive and tremor are classic." + }, + { + "slug": "structural-neurological-autoimmune-endocrine-seizure", + "title": "Structural", + "clinicalHinge": "Inattention and fluctuating awareness are the key separators; psychotic content is often fragmentary." + }, + { + "slug": "substance-intoxication", + "title": "Substance intoxication", + "clinicalHinge": "Inattention plus altered awareness/arousal are the key separators. Usually precipitated by acute illness, surgery, injury, retention, constipation, pain, or medicines." + }, + { + "slug": "substance-intoxication-or-withdrawal", + "title": "Substance intoxication or withdrawal", + "clinicalHinge": "Shifts are reactive, brief, and relationship-linked rather than sustained episodic elevation with reduced need for sleep." + }, + { + "slug": "substance-use-disorder", + "title": "Substance use disorder", + "clinicalHinge": "Chronicity and psychosis history support primary negative symptoms." + }, + { + "slug": "substance-withdrawal", + "title": "Substance withdrawal", + "clinicalHinge": "Inattention plus altered awareness/arousal are the key separators. Usually precipitated by acute illness, surgery, injury, retention, constipation, pain, or medicines." + }, + { + "slug": "substance-or-medication-induced-anxiety", + "title": "Substance- or medication-induced anxiety", + "clinicalHinge": "Abrupt peak over minutes, recurrent unexpected attacks, anticipatory anxiety or avoidance." + }, + { + "slug": "substance-or-medication-induced-depression", + "title": "Substance- or medication-induced depression", + "clinicalHinge": "Sustained mood syndrome with guilt, hopelessness, reduced energy, impaired concentration, change in sleep/appetite." + }, + { + "slug": "substance-or-medication-related-insomnia", + "title": "Substance- or medication-related insomnia", + "clinicalHinge": "Sleep disturbance sits inside a broader syndrome. Reduced need for sleep is one of the most decisive bipolar bedside clues." + }, + { + "slug": "substance-induced-hallucinations", + "title": "Substance-induced hallucinations", + "clinicalHinge": "Usually chronic, internally generated but experienced as alien, embedded in broader psychotic syndrome." + }, + { + "slug": "substance-induced-mania-activation", + "title": "Substance-induced mania", + "clinicalHinge": "Sustained episodic activation plus decreased need for sleep are the key separators." + }, + { + "slug": "substance-induced-psychosis", + "title": "Substance-induced psychosis", + "clinicalHinge": "Inattention and fluctuation dominate. Psychotic content is often fragmented or changeable." + }, + { + "slug": "substance-induced-threat-beliefs", + "title": "Substance-induced threat beliefs", + "clinicalHinge": "Often fixed, systematised, accompanied by hallucinations, thought disorder, or negative symptoms." + }, + { + "slug": "substance-related-chaotic-non-adherence", + "title": "Substance-related chaotic non-adherence", + "clinicalHinge": "Altered reality testing drives refusal rather than simple preference." + }, + { + "slug": "substance-related-suicidality", + "title": "Substance-related suicidality", + "clinicalHinge": "Sustained wish to die or escape, often with cognitive constriction and pessimism." + }, + { + "slug": "tardive-dyskinesia-tardive-syndromes", + "title": "Tardive dyskinesia", + "clinicalHinge": "Subjective inner restlessness is the key feature, often with observable motor restlessness." + }, + { + "slug": "temporal-lobe-epilepsy-neurological-aura", + "title": "Temporal lobe epilepsy", + "clinicalHinge": "Usually chronic, internally generated but experienced as alien, embedded in broader psychotic syndrome." + }, + { + "slug": "temporal-lobe-epilepsy-neurological-speech-disturbance", + "title": "Temporal lobe epilepsy", + "clinicalHinge": "Formal thought disorder plus broader psychotic syndrome and functional deterioration." + }, + { + "slug": "trauma-or-coercion-related-refusal", + "title": "Trauma- or coercion-related refusal", + "clinicalHinge": "Altered reality testing drives refusal rather than simple preference." + }, + { + "slug": "trauma-or-personality-linked-psychotic-like-symptoms", + "title": "Trauma- or personality-linked psychotic-like symptoms", + "clinicalHinge": "Inattention and fluctuation dominate. Psychotic content is often fragmented or changeable." + }, + { + "slug": "valproate-adverse-effects-toxicity", + "title": "Valproate adverse effects", + "clinicalHinge": "Temporal association with antipsychotic initiation, dose change, or depot timing." + }, + { + "slug": "vascular-dementia-vascular-cognitive-disorder", + "title": "Vascular dementia", + "clinicalHinge": "Acute and fluctuating with inattention = delirium (medical/toxic cause)." + }, + { + "slug": "very-late-onset-schizophrenia-like-psychosis-delusional-disorder", + "title": "Very-late-onset schizophrenia-like psychosis", + "clinicalHinge": "Inattention and fluctuating awareness are the key separators; psychotic content is often fragmentary." + }, + { + "slug": "wernicke-encephalopathy", + "title": "Wernicke encephalopathy", + "clinicalHinge": "Inattention plus altered awareness/arousal are the key separators. Usually precipitated by acute illness, surgery, injury, retention, constipation, pain, or medicines." + } + ], + "presentations": [ + { + "id": "acute-confusion-encephalopathy", + "title": "Delirium / Acute Confusion / Encephalopathy", + "subtitle": "Delirium and its encephalopathic mimics are acute medical emergencies needing same-hour vitals, glucose and precipitant work-up." + }, + { + "id": "psychosis-first-episode-medical-psychosis", + "title": "Psychosis / First-Episode Medical Psychosis", + "subtitle": "First-episode psychosis needs same-day assessment to exclude medical/substance causes; graded urgent unless medical instability or delirium escalates it to emergent." + }, + { + "id": "catatonia-psychomotor-shutdown-malignant-syndromes", + "title": "Catatonia / Psychomotor Shutdown / Malignant Syndromes", + "subtitle": "Malignant catatonia, NMS and serotonin toxicity are life-threatening; catatonia risks dehydration, VTE and autonomic instability." + }, + { + "id": "suicidal-ideation-suicide-attempt-self-harm", + "title": "Suicidal Ideation / Suicide Attempt / Self-Harm", + "subtitle": "Active suicidality or a recent attempt requires urgent safety review and medical clearance." + }, + { + "id": "aggression-violence-homicidal-ideation", + "title": "Aggression / Violence / Homicidal Ideation", + "subtitle": "Acute aggression or homicidal intent threatens immediate safety and may signal delirium, intoxication or mania." + }, + { + "id": "mania-hypomania-mixed-states", + "title": "Mania / Hypomania / Mixed States", + "subtitle": "Acute mania carries high risk from impaired judgement, exhaustion and behavioural dyscontrol; mixed states elevate suicide risk." + }, + { + "id": "substance-intoxication", + "title": "Substance Intoxication", + "subtitle": "Significant intoxication can cause airway compromise, hyperthermia or trauma and needs immediate ABC and toxidrome assessment." + }, + { + "id": "substance-withdrawal", + "title": "Substance Withdrawal", + "subtitle": "Alcohol and benzodiazepine withdrawal can progress to seizures and delirium tremens within hours." + }, + { + "id": "medication-side-effects-iatrogenic-toxicity-qt-risk", + "title": "Medication Side Effects / Iatrogenic Toxicity / QT Risk", + "subtitle": "Lithium toxicity, clozapine adverse effects, serotonin syndrome, NMS and QT prolongation are time-critical iatrogenic emergencies." + }, + { + "id": "akathisia-epse-tremor-sedation", + "title": "Akathisia / EPSE / Tremor / Sedation", + "subtitle": "Akathisia and EPSE cause marked distress and adherence breakdown and elevate suicide risk; acute dystonia can need faster intervention." + }, + { + "id": "hallucinations", + "title": "Hallucinations", + "subtitle": "New hallucinations need same-day assessment to separate delirium and substance causes from primary disorders." + }, + { + "id": "delusions-paranoia-thought-interference-command-phenomena", + "title": "Delusions / Paranoia / Thought Interference / Command Phenomena", + "subtitle": "Delusions / Paranoia / Thought Interference / Command Phenomena" + }, + { + "id": "disorganised-behaviour-disorganised-thought-or-speech", + "title": "Disorganised Behaviour / Disorganised Thought or Speech", + "subtitle": "Disorganised Behaviour / Disorganised Thought or Speech" + }, + { + "id": "depression", + "title": "Depression", + "subtitle": "Depression" + }, + { + "id": "anxiety-panic-obsessions-health-anxiety", + "title": "Anxiety / Panic / Obsessions / Health Anxiety", + "subtitle": "Anxiety / Panic / Obsessions / Health Anxiety" + }, + { + "id": "dissociation-trauma-response-somatisation-functional-neurological-symptoms", + "title": "Dissociation / Trauma Response / Somatisation / Functional Neurological Symptoms", + "subtitle": "Dissociation / Trauma Response / Somatisation / Functional Neurological Symptoms" + }, + { + "id": "sleep-disturbance-insomnia-reduced-need-for-sleep", + "title": "Sleep Disturbance / Insomnia / Reduced Need for Sleep", + "subtitle": "Sleep Disturbance / Insomnia / Reduced Need for Sleep" + }, + { + "id": "mood-lability-emotional-dysregulation", + "title": "Mood Lability / Emotional Dysregulation", + "subtitle": "Mood Lability / Emotional Dysregulation" + }, + { + "id": "cognitive-impairment-memory-dementia-bpsd", + "title": "Cognitive Impairment / Memory / Dementia / BPSD", + "subtitle": "Cognitive Impairment / Memory / Dementia / BPSD" + }, + { + "id": "poor-functioning-neglect-negative-symptoms-poor-judgement-poor-rapport", + "title": "Poor Functioning / Neglect / Negative Symptoms / Poor Judgement / Poor Rapport", + "subtitle": "Poor Functioning / Neglect / Negative Symptoms / Poor Judgement / Poor Rapport" + }, + { + "id": "treatment-refusal-non-adherence-poor-insight", + "title": "Treatment Refusal / Non-Adherence / Poor Insight", + "subtitle": "Treatment Refusal / Non-Adherence / Poor Insight" + }, + { + "id": "autism-intellectual-disability-adhd-developmental-overlap", + "title": "Autism / Intellectual Disability / ADHD / Developmental Overlap", + "subtitle": "Autism / Intellectual Disability / ADHD / Developmental Overlap" + }, + { + "id": "food-refusal-not-eating-eating-disorder-spectrum", + "title": "Food Refusal / Not Eating / Eating Disorder Spectrum", + "subtitle": "Food Refusal / Not Eating / Eating Disorder Spectrum" + }, + { + "id": "perinatal-acute-psychiatry", + "title": "Perinatal Acute Psychiatry", + "subtitle": "Perinatal Acute Psychiatry" + }, + { + "id": "personality-related-interpersonal-conflict-antisocial-conduct", + "title": "Personality-Related Interpersonal Conflict / Antisocial Conduct", + "subtitle": "Personality-Related Interpersonal Conflict / Antisocial Conduct" + }, + { + "id": "older-adult-first-presentation-psychosis-traps", + "title": "Older-Adult First-Presentation Psychosis Traps", + "subtitle": "Older-Adult First-Presentation Psychosis Traps" + }, + { + "id": "focused-comparator-perinatal", + "title": "Focused comparator (perinatal)", + "subtitle": "Separates perinatal intrusive thoughts from postpartum psychosis and genuine harm intent." + }, + { + "id": "psychomotor-agitation", + "title": "Psychomotor Agitation", + "subtitle": "Psychomotor Agitation" + }, + { + "id": "psychomotor-retardation-mutism-severe-withdrawal", + "title": "Psychomotor Retardation / Mutism / Severe Withdrawal", + "subtitle": "Psychomotor Retardation / Mutism / Severe Withdrawal" + }, + { + "id": "somatic-symptoms-illness-anxiety-functional-neurological-symptoms-factitious-presentation", + "title": "Somatic Symptoms / Illness Anxiety / Functional Neurological Symptoms / Factitious Presentation", + "subtitle": "Somatic Symptoms / Illness Anxiety / Functional Neurological Symptoms / Factitious Presentation" + }, + { + "id": "focused-diagnostic-trap-tables", + "title": "Focused Diagnostic Trap Tables", + "subtitle": "Distinguishes intrusive/obsessional phenomena from psychotic or violent intent." + } + ], + "aliases": { + "agitation": ["aggression", "akathisia", "restless"], + "agitated": ["agitation", "aggression", "restless"], + "psychosis": ["delusion", "hallucination", "paranoia"], + "psychotic": ["psychosis", "delusion", "hallucination"], + "paranoia": ["delusion", "psychosis", "suspicious"], + "suicide": ["suicidal", "self-harm", "ideation", "attempt"], + "suicidal": ["suicide", "self-harm", "ideation", "attempt"], + "delirium": ["confusion", "fluctuation", "encephalopathy"], + "confused": ["confusion", "delirium", "encephalopathy"], + "withdrawal": ["detox", "tremor", "autonomic"], + "intoxicated": ["intoxication", "substance", "toxicity"], + "mania": ["hypomania", "mixed", "activation"], + "trauma": ["ptsd", "dissociation", "functional"], + "older": ["older-adult", "neurocognitive", "dementia"], + "perinatal": ["postpartum", "pregnancy", "infant"], + "medication": ["iatrogenic", "toxicity", "qt", "adverse"], + "medicine": ["medication", "iatrogenic", "toxicity"], + "qt": ["qtc", "arrhythmia", "toxicity"], + "catatonia": ["mutism", "shutdown", "stupor"], + "field": ["weight"] + } +} diff --git a/src/lib/cross-mode-differentials.ts b/src/lib/cross-mode-differentials.ts index c95589f365..8facb5142a 100644 --- a/src/lib/cross-mode-differentials.ts +++ b/src/lib/cross-mode-differentials.ts @@ -1,20 +1,16 @@ +import crossModeDifferentialIndex from "@/data/cross-mode-differentials-index.json"; import type { CrossModeDifferentialCatalog } from "@/lib/cross-mode-links"; -import { differentialPresentations, differentialRecords, differentialSearchAliases } from "@/lib/differentials"; -// Load this module with a dynamic import only: it statically pulls the 1.2 MB -// differentials snapshot, which stays code-split out of the dashboard bundle. +// Returns the precomputed, lightweight cross-mode differentials catalog +// (src/data/cross-mode-differentials-index.json, generated by +// scripts/build-cross-mode-differentials-index.mjs). Importing the ~53 KB trimmed +// index — instead of @/lib/differentials — keeps the lazily-loaded cross-mode chunk +// from statically pulling the ~1.2 MB differentials snapshot just to build a tiny +// {slug,title,clinicalHinge} + presentations + aliases catalog. +// +// tests/cross-mode-differentials-index.test.ts asserts this equals the live +// projection over the full snapshot, and `npm run check:cross-mode-index` fails when +// the committed index is stale, so the two can never drift. export function crossModeDifferentialCatalog(): CrossModeDifferentialCatalog { - return { - diagnoses: differentialRecords.map((record) => ({ - slug: record.slug, - title: record.title, - clinicalHinge: record.clinicalHinge, - })), - presentations: differentialPresentations().map((presentation) => ({ - id: presentation.id, - title: presentation.title, - subtitle: presentation.subtitle, - })), - aliases: differentialSearchAliases(), - }; + return crossModeDifferentialIndex as CrossModeDifferentialCatalog; } diff --git a/tests/cross-mode-differentials-index.test.ts b/tests/cross-mode-differentials-index.test.ts new file mode 100644 index 0000000000..3270cf3c12 --- /dev/null +++ b/tests/cross-mode-differentials-index.test.ts @@ -0,0 +1,42 @@ +import { describe, expect, it } from "vitest"; + +import { crossModeDifferentialCatalog } from "@/lib/cross-mode-differentials"; +import { differentialPresentations, differentialRecords, differentialSearchAliases } from "@/lib/differentials"; + +// cross-mode-differentials.ts now returns a precomputed index +// (src/data/cross-mode-differentials-index.json) instead of projecting the full +// ~1.2 MB snapshot at runtime, to keep the lazy cross-mode chunk small. This locks +// the index to the live projection so the two can never silently drift — it catches +// both snapshot-content changes and any divergence in the projection / alias-filter +// logic. `npm run check:cross-mode-index` is the fast offline gate; this is the +// behavioural guarantee. +describe("cross-mode differentials precomputed index", () => { + it("matches the live projection over the full differentials snapshot", () => { + const live = { + diagnoses: differentialRecords.map((record) => ({ + slug: record.slug, + title: record.title, + clinicalHinge: record.clinicalHinge, + })), + presentations: differentialPresentations().map((presentation) => ({ + id: presentation.id, + title: presentation.title, + subtitle: presentation.subtitle, + })), + aliases: differentialSearchAliases(), + }; + + // JSON round-trip the live side so an undefined field (omitted by JSON) compares + // equal to the committed index rather than tripping on present-vs-absent keys. + expect(crossModeDifferentialCatalog()).toEqual(JSON.parse(JSON.stringify(live))); + }); + + it("carries the full catalogue and drops bare-number aliases", () => { + const catalog = crossModeDifferentialCatalog(); + expect(catalog.diagnoses.length).toBeGreaterThan(0); + expect(catalog.presentations.length).toBeGreaterThan(0); + const aliasValues = Object.values(catalog.aliases).flat(); + expect(aliasValues.length).toBeGreaterThan(0); + expect(aliasValues.every((alias) => !/^\d+(\.\d+)?$/.test(alias.trim()))).toBe(true); + }); +}); From 3dcc3148c28399d61dd468c00a82c3e6c7d210a3 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 27 Jul 2026 22:08:20 +0000 Subject: [PATCH 02/19] ci: enforce check:cross-mode-index in the static-pr job The gate-manifest guard requires every verify:cheap gate to also run explicitly in CI. Add the "Cross-mode differentials index drift" step next to the therapy-data-index one so check:cross-mode-index is enforced on PRs, matching the verify:cheap wiring. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01UbhUVWVJRwDibC2YtJ6aRX --- .github/workflows/ci.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b50e0fc634..204a61b598 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -251,6 +251,9 @@ jobs: - name: Therapy data index drift run: npm run check:therapy-data-index + - name: Cross-mode differentials index drift + run: npm run check:cross-mode-index + - name: Design-system contract run: npm run check:design-system-contract From 70d140ed70cb4f00d641890fddfb4ac891c40d9c Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 27 Jul 2026 22:30:22 +0000 Subject: [PATCH 03/19] docs(ledger): record Bugbot review for PR #1287 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Append-only review record for the cross-mode differentials index perf PR at 97ab067b — no P0/P1; residual P2 is missing import-graph lock. Co-authored-by: BigSimmo --- docs/branch-review-ledger.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/branch-review-ledger.md b/docs/branch-review-ledger.md index 466c6eb06f..eb9b0d3b5f 100644 --- a/docs/branch-review-ledger.md +++ b/docs/branch-review-ledger.md @@ -1145,3 +1145,4 @@ This file is append-only. Never rewrite or delete an existing review record; app | 2026-07-27 | PR #1275 / `codex/identify-and-fix-performance-issues-during-mode-switch` | `24605b57e288` | Bugbot + merge-tree review | NOT READY. Prefetch product change looks auth-safe/correct. GitHub DIRTY is staleness (merge-tree CLEAN). Blockers: ledger rewrite/corruption (~95 historical rows) + incomplete required CI. Sync main, restore ledger from origin/main, append one row, then recheck. | merge-tree CLEAN; ledger byte/corruption inspect; unresolved Codex/CodeRabbit threads; no provider checks. | | 2026-07-27 | PR #1280 / `claude/top-search-design-mockups-w53znc` | `93a9f90ff287` | Bugbot + CI debug | NOT READY until Production UI green. Product band rebuild looks sound; Advisory UI green. Hosted failure is Answer Suspense `Loading answer` strict-mode (2 nodes / one hidden) in ui-smoke — not caused by band diff. Optional P2: `useRailOverflow` can miss child-list changes. | Production UI log job 90037898852; unique diff vs main; focused band unit 9/9 on tip; no provider checks. | | 2026-07-27 | PR #1281 / `claude/safety-planning-tools-page-tsq4vs` | `a26e95fc9ac9` | Bugbot clinical review | APPROVE pending exact-head required CI + minor P2 polish. Incomplete plans get draft banner/clipboard marking; contact reach methods required for Ready/Finalise. P2: StepBuilderCard green check still uses entries.length; clipboard DRAFT text untested. No P0/P1. | unique diff review; GraphQL no cursor[bot] threads; no provider checks. | +| 2026-07-27 | PR #1287 / `claude/site-audit-quick-wins-21v9gb` | `97ab067bfdca644e0750bfbc717da7d58ecd27ee` | Bugbot defect hunt (cursoragent request; no hosted cursor[bot] threads) | APPROVE pending exact-head required CI. No P0/P1. Projection ≡ live helpers (201 diagnoses / 31 presentations / 20 alias keys); `--check` compares parsed values (Prettier-safe); CI `static-pr` + `verify:cheap` wire `check:cross-mode-index`. Residual P2: re-importing `@/lib/differentials` into `cross-mode-differentials.ts` would restore the ~1.2 MB lazy-chunk weight while data gates stay green — no import-graph lock yet. P3: stale comment in `cross-mode-links.tsx`; scripts-index omits new generator. | `check:cross-mode-index` PASS; vitest `cross-mode-differentials-index` 2/2; gate-manifest PASS; drift/invalid-JSON proofs FAIL closed; import-graph grep clean today; no provider-backed checks. | From 18fcfae24b41cdd5caebd2780afa963a2a1335a5 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 27 Jul 2026 22:35:27 +0000 Subject: [PATCH 04/19] test/docs: guard the cross-mode index import graph; fix stale refs (Bugbot #1287) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Address the three non-blocking Bugbot findings on #1287: - P2: add an import-graph guard so a future re-add of a static `import … from "@/lib/differentials"` (or the raw snapshot) to cross-mode-differentials.ts fails the test — the value-equality check alone stays green regardless of how the catalog is produced, so it could not catch the ~1.2 MB snapshot silently returning to the lazy chunk. - P3: update the stale cross-mode-links.tsx comment (it now loads the ~53 KB precomputed index, not the full snapshot). - P3: list build-cross-mode-differentials-index.mjs in docs/scripts-index.md next to build-therapies-index.mjs. Test-only + docs/comment; no runtime behaviour change. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01UbhUVWVJRwDibC2YtJ6aRX --- docs/scripts-index.md | 3 ++- .../clinical-dashboard/cross-mode-links.tsx | 5 +++-- tests/cross-mode-differentials-index.test.ts | 19 +++++++++++++++++++ 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/docs/scripts-index.md b/docs/scripts-index.md index 91eaa135a4..34a8cef2d4 100644 --- a/docs/scripts-index.md +++ b/docs/scripts-index.md @@ -66,7 +66,8 @@ For executable phone-chrome changes, use `verify:phone-chrome` before the broad ## Build & assets [live/infra] -`build-worker.mjs`, `build-analyze.mjs`, `build-therapies-index.mjs`, `build-ranking-snapshot.ts`, +`build-worker.mjs`, `build-analyze.mjs`, `build-therapies-index.mjs`, +`build-cross-mode-differentials-index.mjs`, `build-ranking-snapshot.ts`, `generate-site-map.ts`, `generate-brand-assets.ts`, `generate-sample-documents.ts`, `check-sample-extraction.ts`. diff --git a/src/components/clinical-dashboard/cross-mode-links.tsx b/src/components/clinical-dashboard/cross-mode-links.tsx index 9f1166e60d..2a285c8a63 100644 --- a/src/components/clinical-dashboard/cross-mode-links.tsx +++ b/src/components/clinical-dashboard/cross-mode-links.tsx @@ -143,8 +143,9 @@ export function CrossModeLinksSection({ const medications = useMedicationCatalog(undefined, { enabled, fields: "index" }); const [differentials, setDifferentials] = useState(null); useEffect(() => { - // Dynamic import keeps the 1.2 MB differentials snapshot out of the - // dashboard bundle; the catalog is loaded once per session. + // Dynamic import keeps the cross-mode catalog out of the dashboard bundle. + // cross-mode-differentials.ts now loads a precomputed ~53 KB index (not the + // full ~1.2 MB differentials snapshot); the catalog is loaded once per session. if (!enabled || differentials) return; let cancelled = false; import("@/lib/cross-mode-differentials").then((module) => { diff --git a/tests/cross-mode-differentials-index.test.ts b/tests/cross-mode-differentials-index.test.ts index 3270cf3c12..1487c603a0 100644 --- a/tests/cross-mode-differentials-index.test.ts +++ b/tests/cross-mode-differentials-index.test.ts @@ -1,3 +1,6 @@ +import { readFileSync } from "node:fs"; +import { fileURLToPath } from "node:url"; + import { describe, expect, it } from "vitest"; import { crossModeDifferentialCatalog } from "@/lib/cross-mode-differentials"; @@ -31,6 +34,22 @@ describe("cross-mode differentials precomputed index", () => { expect(crossModeDifferentialCatalog()).toEqual(JSON.parse(JSON.stringify(live))); }); + it("does not statically import the heavy differentials module (keeps the lazy chunk small)", () => { + // The value-equality test above stays green regardless of HOW the catalog is + // produced, so it cannot catch a regression that re-adds a static + // `import … from "@/lib/differentials"` (or the raw snapshot) — which would put + // the ~1.2 MB snapshot back into the lazily-loaded cross-mode chunk. Guard the + // import graph directly: cross-mode-differentials.ts must only pull the + // precomputed index. + const source = readFileSync( + fileURLToPath(new URL("../src/lib/cross-mode-differentials.ts", import.meta.url)), + "utf8", + ); + expect(source).not.toMatch(/from\s+["']@\/lib\/differentials["']/); + expect(source).not.toMatch(/differentials-snapshot/); + expect(source).toMatch(/cross-mode-differentials-index\.json/); + }); + it("carries the full catalogue and drops bare-number aliases", () => { const catalog = crossModeDifferentialCatalog(); expect(catalog.diagnoses.length).toBeGreaterThan(0); From b0b3fdf96c1a93728fa429229f0125d9cc5dd1a1 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 27 Jul 2026 22:44:36 +0000 Subject: [PATCH 05/19] test/docs: deepen the import-graph guard; refresh review ledger (CodeRabbit #1287) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Address CodeRabbit's two Minor findings on the follow-up head: - Import-graph guard was a shallow regex on one file's `from` imports; a transitive helper import or a dynamic import()/require form could reintroduce @/lib/differentials while staying green. Replace it with an allowlist: cross-mode-differentials.ts may import ONLY the precomputed index + the (type-only) catalog type — every collected specifier (from / import() / require / side-effect) must be in the allowlist, so any reintroduction surfaces as a disallowed specifier. (Comment mentions aren't specifiers, so the explanatory "@/lib/differentials" reference is correctly ignored.) - The branch-review-ledger is append-only, so instead of editing the Bugbot row, append a superseding record noting the three residuals (import-graph lock, stale comment, scripts-index entry) are addressed, with the final verification. Test-only + append-only ledger record; no runtime change. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01UbhUVWVJRwDibC2YtJ6aRX --- docs/branch-review-ledger.md | 1 + tests/cross-mode-differentials-index.test.ts | 31 ++++++++++++++------ 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/docs/branch-review-ledger.md b/docs/branch-review-ledger.md index eb9b0d3b5f..67b45bbda2 100644 --- a/docs/branch-review-ledger.md +++ b/docs/branch-review-ledger.md @@ -1146,3 +1146,4 @@ This file is append-only. Never rewrite or delete an existing review record; app | 2026-07-27 | PR #1280 / `claude/top-search-design-mockups-w53znc` | `93a9f90ff287` | Bugbot + CI debug | NOT READY until Production UI green. Product band rebuild looks sound; Advisory UI green. Hosted failure is Answer Suspense `Loading answer` strict-mode (2 nodes / one hidden) in ui-smoke — not caused by band diff. Optional P2: `useRailOverflow` can miss child-list changes. | Production UI log job 90037898852; unique diff vs main; focused band unit 9/9 on tip; no provider checks. | | 2026-07-27 | PR #1281 / `claude/safety-planning-tools-page-tsq4vs` | `a26e95fc9ac9` | Bugbot clinical review | APPROVE pending exact-head required CI + minor P2 polish. Incomplete plans get draft banner/clipboard marking; contact reach methods required for Ready/Finalise. P2: StepBuilderCard green check still uses entries.length; clipboard DRAFT text untested. No P0/P1. | unique diff review; GraphQL no cursor[bot] threads; no provider checks. | | 2026-07-27 | PR #1287 / `claude/site-audit-quick-wins-21v9gb` | `97ab067bfdca644e0750bfbc717da7d58ecd27ee` | Bugbot defect hunt (cursoragent request; no hosted cursor[bot] threads) | APPROVE pending exact-head required CI. No P0/P1. Projection ≡ live helpers (201 diagnoses / 31 presentations / 20 alias keys); `--check` compares parsed values (Prettier-safe); CI `static-pr` + `verify:cheap` wire `check:cross-mode-index`. Residual P2: re-importing `@/lib/differentials` into `cross-mode-differentials.ts` would restore the ~1.2 MB lazy-chunk weight while data gates stay green — no import-graph lock yet. P3: stale comment in `cross-mode-links.tsx`; scripts-index omits new generator. | `check:cross-mode-index` PASS; vitest `cross-mode-differentials-index` 2/2; gate-manifest PASS; drift/invalid-JSON proofs FAIL closed; import-graph grep clean today; no provider-backed checks. | +| 2026-07-27 | PR #1287 / `claude/site-audit-quick-wins-21v9gb` | `18fcfae24b41cdd5caebd2780afa963a2a1335a5` | Follow-up — Bugbot/CodeRabbit residuals addressed (supersedes the 97ab067 Bugbot row) | Residual P2 closed: import-graph lock added — an allowlist test asserts `cross-mode-differentials.ts` may import ONLY the precomputed index + the (type-only) catalog type, catching direct, transitive-via-new-import, and dynamic `import()`/`require` reintroductions of `@/lib/differentials`. P3s closed: stale `cross-mode-links.tsx` comment fixed; `build-cross-mode-differentials-index.mjs` listed in `scripts-index.md`. CodeRabbit's two Minor nits (guard depth + this ledger refresh) addressed. | vitest `cross-mode-differentials-index` 3/3; typecheck + lint PASS; `docs:check-scripts` + `docs:check-links` PASS; `check:cross-mode-index` PASS; no provider-backed checks. | diff --git a/tests/cross-mode-differentials-index.test.ts b/tests/cross-mode-differentials-index.test.ts index 1487c603a0..adc7ccfe50 100644 --- a/tests/cross-mode-differentials-index.test.ts +++ b/tests/cross-mode-differentials-index.test.ts @@ -34,20 +34,33 @@ describe("cross-mode differentials precomputed index", () => { expect(crossModeDifferentialCatalog()).toEqual(JSON.parse(JSON.stringify(live))); }); - it("does not statically import the heavy differentials module (keeps the lazy chunk small)", () => { + it("only imports the precomputed index — no static, dynamic, or transitive path to the heavy snapshot", () => { // The value-equality test above stays green regardless of HOW the catalog is - // produced, so it cannot catch a regression that re-adds a static - // `import … from "@/lib/differentials"` (or the raw snapshot) — which would put - // the ~1.2 MB snapshot back into the lazily-loaded cross-mode chunk. Guard the - // import graph directly: cross-mode-differentials.ts must only pull the - // precomputed index. + // produced, so it cannot catch a regression that reintroduces the ~1.2 MB + // snapshot into the lazily-loaded cross-mode chunk. Guard the import graph at + // its entry point with an allowlist: cross-mode-differentials.ts may import ONLY + // the precomputed index and the (type-only, runtime-erased) catalog type. + // Anything else fails — a direct `@/lib/differentials` re-import, a NEW helper + // that transitively pulls it, or a dynamic `import()` / `require()` form — because + // any such regression must add a new import specifier to THIS file. const source = readFileSync( fileURLToPath(new URL("../src/lib/cross-mode-differentials.ts", import.meta.url)), "utf8", ); - expect(source).not.toMatch(/from\s+["']@\/lib\/differentials["']/); - expect(source).not.toMatch(/differentials-snapshot/); - expect(source).toMatch(/cross-mode-differentials-index\.json/); + const allowed = new Set(["@/data/cross-mode-differentials-index.json", "@/lib/cross-mode-links"]); + const specifiers = [ + /\bfrom\s*["']([^"']+)["']/g, // static: import … from "x" + /\bimport\s*\(\s*["']([^"']+)["']/g, // dynamic: import("x") + /\brequire\s*\(\s*["']([^"']+)["']/g, // cjs: require("x") + /(?:^|\n)\s*import\s+["']([^"']+)["']/g, // side-effect: import "x" + ].flatMap((pattern) => [...source.matchAll(pattern)].map((match) => match[1])); + + // Every import specifier must be in the allowlist — so a re-import of + // `@/lib/differentials` (or a helper/dynamic form pulling the snapshot) surfaces + // as a disallowed specifier and fails here. Comment mentions don't count: only + // real `from`/`import()`/`require` specifiers are collected. + expect(specifiers.filter((specifier) => !allowed.has(specifier))).toEqual([]); + expect(specifiers).toContain("@/data/cross-mode-differentials-index.json"); }); it("carries the full catalogue and drops bare-number aliases", () => { From 09dbd2dcc5126e4ae7d6f9e99e7325853047f749 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 27 Jul 2026 22:57:03 +0000 Subject: [PATCH 06/19] test(perf): lock cross-mode differentials off the full snapshot Prevent a silent bundle regression: re-importing @/lib/differentials into the lazy cross-mode catalog would restore the ~1.2 MB snapshot while the data-equality gates stay green. Assert the trimmed JSON index import and the dynamic load site in cross-mode-links, and refresh the stale comment. Co-authored-by: BigSimmo --- .../clinical-dashboard/cross-mode-links.tsx | 4 ++-- tests/client-performance-boundaries.test.ts | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/components/clinical-dashboard/cross-mode-links.tsx b/src/components/clinical-dashboard/cross-mode-links.tsx index 9f1166e60d..2fdee7ffb8 100644 --- a/src/components/clinical-dashboard/cross-mode-links.tsx +++ b/src/components/clinical-dashboard/cross-mode-links.tsx @@ -143,8 +143,8 @@ export function CrossModeLinksSection({ const medications = useMedicationCatalog(undefined, { enabled, fields: "index" }); const [differentials, setDifferentials] = useState(null); useEffect(() => { - // Dynamic import keeps the 1.2 MB differentials snapshot out of the - // dashboard bundle; the catalog is loaded once per session. + // Dynamic import loads the ~53 KB precomputed cross-mode index + // (not the ~1.2 MB differentials snapshot) once per session. if (!enabled || differentials) return; let cancelled = false; import("@/lib/cross-mode-differentials").then((module) => { diff --git a/tests/client-performance-boundaries.test.ts b/tests/client-performance-boundaries.test.ts index f10e792d79..8770db6321 100644 --- a/tests/client-performance-boundaries.test.ts +++ b/tests/client-performance-boundaries.test.ts @@ -23,6 +23,25 @@ describe("fixture-free client performance boundaries", () => { expect(dashboard).not.toContain('from "@/lib/differentials"'); }); + it("keeps the lazy cross-mode differentials catalog on the trimmed precomputed index", () => { + // Re-importing @/lib/differentials (or the snapshot/fixtures) here would put the + // ~1.2 MB snapshot back into the dynamically loaded cross-mode chunk while the + // data-equality gates stay green. Lock the import graph, not just the catalog. + const catalogModule = source("src/lib/cross-mode-differentials.ts"); + const eagerDifferentialsImport = + /^[ \t]*import[ \t]+(?!type\b)(?:[^\r\n"']+[ \t]+from[ \t]+)?["']@\/lib\/differentials["'][ \t]*;?/m; + expect(catalogModule).toContain('from "@/data/cross-mode-differentials-index.json"'); + expect(catalogModule).not.toMatch(eagerDifferentialsImport); + expect(catalogModule).not.toMatch(/differential-fixtures|differentials-snapshot|loadDifferentialSnapshot/); + expect('import { differentialRecords } from "@/lib/differentials";').toMatch(eagerDifferentialsImport); + expect('import type { DifferentialRecord } from "@/lib/differentials";').not.toMatch(eagerDifferentialsImport); + + const links = source("src/components/clinical-dashboard/cross-mode-links.tsx"); + expect(links).toContain('import("@/lib/cross-mode-differentials")'); + expect(links).not.toContain('from "@/lib/cross-mode-differentials"'); + expect(links).not.toContain('from "@/lib/differentials"'); + }); + it("keeps initial dashboard rankers on fixture-free entry points", () => { const dashboard = source("src/components/ClinicalDashboard.tsx"); const deferredRegistrySearch = source("src/components/clinical-dashboard/use-deferred-registry-search.ts"); From 34e9fc602356408f5b92ca427068d0a8bebf0987 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 27 Jul 2026 22:57:03 +0000 Subject: [PATCH 07/19] docs(ledger): record Bugbot P2 fix for PR #1287 Append-only follow-up for the cross-mode differentials import-graph lock. Co-authored-by: BigSimmo --- docs/branch-review-ledger.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/branch-review-ledger.md b/docs/branch-review-ledger.md index eb9b0d3b5f..b86192aaf2 100644 --- a/docs/branch-review-ledger.md +++ b/docs/branch-review-ledger.md @@ -1146,3 +1146,4 @@ This file is append-only. Never rewrite or delete an existing review record; app | 2026-07-27 | PR #1280 / `claude/top-search-design-mockups-w53znc` | `93a9f90ff287` | Bugbot + CI debug | NOT READY until Production UI green. Product band rebuild looks sound; Advisory UI green. Hosted failure is Answer Suspense `Loading answer` strict-mode (2 nodes / one hidden) in ui-smoke — not caused by band diff. Optional P2: `useRailOverflow` can miss child-list changes. | Production UI log job 90037898852; unique diff vs main; focused band unit 9/9 on tip; no provider checks. | | 2026-07-27 | PR #1281 / `claude/safety-planning-tools-page-tsq4vs` | `a26e95fc9ac9` | Bugbot clinical review | APPROVE pending exact-head required CI + minor P2 polish. Incomplete plans get draft banner/clipboard marking; contact reach methods required for Ready/Finalise. P2: StepBuilderCard green check still uses entries.length; clipboard DRAFT text untested. No P0/P1. | unique diff review; GraphQL no cursor[bot] threads; no provider checks. | | 2026-07-27 | PR #1287 / `claude/site-audit-quick-wins-21v9gb` | `97ab067bfdca644e0750bfbc717da7d58ecd27ee` | Bugbot defect hunt (cursoragent request; no hosted cursor[bot] threads) | APPROVE pending exact-head required CI. No P0/P1. Projection ≡ live helpers (201 diagnoses / 31 presentations / 20 alias keys); `--check` compares parsed values (Prettier-safe); CI `static-pr` + `verify:cheap` wire `check:cross-mode-index`. Residual P2: re-importing `@/lib/differentials` into `cross-mode-differentials.ts` would restore the ~1.2 MB lazy-chunk weight while data gates stay green — no import-graph lock yet. P3: stale comment in `cross-mode-links.tsx`; scripts-index omits new generator. | `check:cross-mode-index` PASS; vitest `cross-mode-differentials-index` 2/2; gate-manifest PASS; drift/invalid-JSON proofs FAIL closed; import-graph grep clean today; no provider-backed checks. | +| 2026-07-27 | PR #1287 / `claude/site-audit-quick-wins-21v9gb` | `09dbd2dcc5126e4ae7d6f9e99e7325853047f749` | Bugbot P2 follow-up: import-graph lock for cross-mode differentials | FIXED. Added a client-performance-boundaries source assertion that `cross-mode-differentials.ts` stays on the trimmed JSON index (no value-import of `@/lib/differentials` / snapshot / fixtures) and that `cross-mode-links.tsx` keeps the dynamic import. Updated the stale 1.2 MB comment. Residual: dual projection logic still lives in the build script and `differentials.ts` (caught by the existing deep-equal test). | Focused `tests/client-performance-boundaries.test.ts` PASS (7/7); no provider-backed checks. | From d9284bd6d0604bd611d750eab72deb0d40fe9a8c Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 27 Jul 2026 22:57:57 +0000 Subject: [PATCH 08/19] docs(ledger): record reconciled Bugbot P2 fix for #1287 Co-authored-by: BigSimmo --- docs/branch-review-ledger.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/branch-review-ledger.md b/docs/branch-review-ledger.md index 26f38baf9e..bef5a2a1b7 100644 --- a/docs/branch-review-ledger.md +++ b/docs/branch-review-ledger.md @@ -1148,3 +1148,4 @@ This file is append-only. Never rewrite or delete an existing review record; app | 2026-07-27 | PR #1287 / `claude/site-audit-quick-wins-21v9gb` | `97ab067bfdca644e0750bfbc717da7d58ecd27ee` | Bugbot defect hunt (cursoragent request; no hosted cursor[bot] threads) | APPROVE pending exact-head required CI. No P0/P1. Projection ≡ live helpers (201 diagnoses / 31 presentations / 20 alias keys); `--check` compares parsed values (Prettier-safe); CI `static-pr` + `verify:cheap` wire `check:cross-mode-index`. Residual P2: re-importing `@/lib/differentials` into `cross-mode-differentials.ts` would restore the ~1.2 MB lazy-chunk weight while data gates stay green — no import-graph lock yet. P3: stale comment in `cross-mode-links.tsx`; scripts-index omits new generator. | `check:cross-mode-index` PASS; vitest `cross-mode-differentials-index` 2/2; gate-manifest PASS; drift/invalid-JSON proofs FAIL closed; import-graph grep clean today; no provider-backed checks. | | 2026-07-27 | PR #1287 / `claude/site-audit-quick-wins-21v9gb` | `09dbd2dcc5126e4ae7d6f9e99e7325853047f749` | Bugbot P2 follow-up: import-graph lock for cross-mode differentials | FIXED. Added a client-performance-boundaries source assertion that `cross-mode-differentials.ts` stays on the trimmed JSON index (no value-import of `@/lib/differentials` / snapshot / fixtures) and that `cross-mode-links.tsx` keeps the dynamic import. Updated the stale 1.2 MB comment. Residual: dual projection logic still lives in the build script and `differentials.ts` (caught by the existing deep-equal test). | Focused `tests/client-performance-boundaries.test.ts` PASS (7/7); no provider-backed checks. | | 2026-07-27 | PR #1287 / `claude/site-audit-quick-wins-21v9gb` | `18fcfae24b41cdd5caebd2780afa963a2a1335a5` | Follow-up — Bugbot/CodeRabbit residuals addressed (supersedes the 97ab067 Bugbot row) | Residual P2 closed: import-graph lock added — an allowlist test asserts `cross-mode-differentials.ts` may import ONLY the precomputed index + the (type-only) catalog type, catching direct, transitive-via-new-import, and dynamic `import()`/`require` reintroductions of `@/lib/differentials`. P3s closed: stale `cross-mode-links.tsx` comment fixed; `build-cross-mode-differentials-index.mjs` listed in `scripts-index.md`. CodeRabbit's two Minor nits (guard depth + this ledger refresh) addressed. | vitest `cross-mode-differentials-index` 3/3; typecheck + lint PASS; `docs:check-scripts` + `docs:check-links` PASS; `check:cross-mode-index` PASS; no provider-backed checks. | +| 2026-07-27 | PR #1287 / `claude/site-audit-quick-wins-21v9gb` | `82f73943a88fdccf8226344bbb5a0bf52f665ede` | Bugbot P2 reconcile after parallel remote fix | FIXED (reconciled). Remote already landed an allowlist import-graph guard in `tests/cross-mode-differentials-index.test.ts` plus scripts-index/comment refresh. Merged that work and retained a consumer-side lock: `cross-mode-links.tsx` must dynamically import the catalog module (not statically). | Focused vitest `client-performance-boundaries` + `cross-mode-differentials-index` PASS (10/10); no provider-backed checks. | From 5c2816d1dc2e7a99c37d27310b487bcac5232db3 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 27 Jul 2026 23:11:58 +0000 Subject: [PATCH 09/19] test(ui): settle differentials-home before strict redirect assert Production UI failed on a hydration overlap: getByTestId('differentials-home') resolved to two nodes after the mode=differentials redirect. Wait for a single settled owner (same helper as the mode-home loop) so transient duplicates do not trip Playwright strict mode while permanent doubles still fail. Co-authored-by: BigSimmo --- tests/ui-smoke.spec.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/ui-smoke.spec.ts b/tests/ui-smoke.spec.ts index 8188304672..e7ed76218d 100644 --- a/tests/ui-smoke.spec.ts +++ b/tests/ui-smoke.spec.ts @@ -8,6 +8,7 @@ import { readPrimaryScrollGeometry, scrollPrimarySurface, } from "./playwright-scroll"; +import { expectSingleSettledOwner } from "./playwright-settlement"; import { answerThreadStorageKey } from "../src/lib/answer-thread-storage"; import { documentSummaryQuestion } from "../src/lib/answer-contract"; import { demoAnswer, demoDocuments, demoSummary, getDemoDocument, getDemoDocumentPayload } from "../src/lib/demo-data"; @@ -2752,7 +2753,12 @@ test.describe("Clinical KB UI smoke coverage", () => { await gotoApp(page, "/?mode=differentials&q=acute+confusion&focus=1"); await expect(page).toHaveURL(/\/differentials\?q=acute\+confusion&focus=1$/); - await expect(page.getByTestId("differentials-home")).toBeVisible(); + // Production hydration can briefly overlap the outgoing server tree and the + // settled client tree on this redirect; wait for one owner before strict + // locators (same guard as the mode-home loop in ui-tools). + await expectSingleSettledOwner(page.getByTestId("differentials-home"), { + message: "differentials redirect home owner", + }); await expect(page.getByRole("heading", { level: 1, name: "Differentials" })).toBeVisible(); }); From f738f083f1c13203f36a12cccb77b4f79470b05c Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 27 Jul 2026 23:14:23 +0000 Subject: [PATCH 10/19] docs(ledger): record Production UI hydration fix for #1287 Co-authored-by: BigSimmo --- docs/branch-review-ledger.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/branch-review-ledger.md b/docs/branch-review-ledger.md index bef5a2a1b7..6277a09aac 100644 --- a/docs/branch-review-ledger.md +++ b/docs/branch-review-ledger.md @@ -1149,3 +1149,4 @@ This file is append-only. Never rewrite or delete an existing review record; app | 2026-07-27 | PR #1287 / `claude/site-audit-quick-wins-21v9gb` | `09dbd2dcc5126e4ae7d6f9e99e7325853047f749` | Bugbot P2 follow-up: import-graph lock for cross-mode differentials | FIXED. Added a client-performance-boundaries source assertion that `cross-mode-differentials.ts` stays on the trimmed JSON index (no value-import of `@/lib/differentials` / snapshot / fixtures) and that `cross-mode-links.tsx` keeps the dynamic import. Updated the stale 1.2 MB comment. Residual: dual projection logic still lives in the build script and `differentials.ts` (caught by the existing deep-equal test). | Focused `tests/client-performance-boundaries.test.ts` PASS (7/7); no provider-backed checks. | | 2026-07-27 | PR #1287 / `claude/site-audit-quick-wins-21v9gb` | `18fcfae24b41cdd5caebd2780afa963a2a1335a5` | Follow-up — Bugbot/CodeRabbit residuals addressed (supersedes the 97ab067 Bugbot row) | Residual P2 closed: import-graph lock added — an allowlist test asserts `cross-mode-differentials.ts` may import ONLY the precomputed index + the (type-only) catalog type, catching direct, transitive-via-new-import, and dynamic `import()`/`require` reintroductions of `@/lib/differentials`. P3s closed: stale `cross-mode-links.tsx` comment fixed; `build-cross-mode-differentials-index.mjs` listed in `scripts-index.md`. CodeRabbit's two Minor nits (guard depth + this ledger refresh) addressed. | vitest `cross-mode-differentials-index` 3/3; typecheck + lint PASS; `docs:check-scripts` + `docs:check-links` PASS; `check:cross-mode-index` PASS; no provider-backed checks. | | 2026-07-27 | PR #1287 / `claude/site-audit-quick-wins-21v9gb` | `82f73943a88fdccf8226344bbb5a0bf52f665ede` | Bugbot P2 reconcile after parallel remote fix | FIXED (reconciled). Remote already landed an allowlist import-graph guard in `tests/cross-mode-differentials-index.test.ts` plus scripts-index/comment refresh. Merged that work and retained a consumer-side lock: `cross-mode-links.tsx` must dynamically import the catalog module (not statically). | Focused vitest `client-performance-boundaries` + `cross-mode-differentials-index` PASS (10/10); no provider-backed checks. | +| 2026-07-27 | PR #1287 / `claude/site-audit-quick-wins-21v9gb` | `5c2816d1dc2e7a99c37d27310b487bcac5232db3` | Production UI CI fix: differentials-home hydration strict-mode | FIXED. Hosted Production UI failed solely on `dashboard differentials mode param redirects…`: `getByTestId(differentials-home)` hit 2 nodes (server+client overlap). Applied `expectSingleSettledOwner` before the visibility assert. No product change. | Focused production Playwright journey PASS 1/1 via `npm run test:e2e` (system Chrome); no provider-backed checks. | From 88d8638974075fa91334c2bb4a0e6b54fda00176 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 27 Jul 2026 23:35:59 +0000 Subject: [PATCH 11/19] test(perf): walk resolved cross-mode lazy-chunk import graph Strengthen the CodeRabbit follow-up: recursively resolve runtime imports from cross-mode-differentials.ts so a helper (or value-import of an allowlisted type module) cannot reintroduce @/lib/differentials or the snapshot while a surface regex stays green. Keep the entry allowlist as a fast fail. Co-authored-by: BigSimmo --- tests/cross-mode-differentials-index.test.ts | 148 ++++++++++++++++--- 1 file changed, 124 insertions(+), 24 deletions(-) diff --git a/tests/cross-mode-differentials-index.test.ts b/tests/cross-mode-differentials-index.test.ts index adc7ccfe50..3915fc40d2 100644 --- a/tests/cross-mode-differentials-index.test.ts +++ b/tests/cross-mode-differentials-index.test.ts @@ -1,4 +1,5 @@ -import { readFileSync } from "node:fs"; +import { existsSync, readFileSync, statSync } from "node:fs"; +import { dirname, join, relative, resolve } from "node:path"; import { fileURLToPath } from "node:url"; import { describe, expect, it } from "vitest"; @@ -34,33 +35,132 @@ describe("cross-mode differentials precomputed index", () => { expect(crossModeDifferentialCatalog()).toEqual(JSON.parse(JSON.stringify(live))); }); - it("only imports the precomputed index — no static, dynamic, or transitive path to the heavy snapshot", () => { + it("resolved lazy-chunk graph reaches the trimmed index and never the heavy snapshot", () => { // The value-equality test above stays green regardless of HOW the catalog is - // produced, so it cannot catch a regression that reintroduces the ~1.2 MB - // snapshot into the lazily-loaded cross-mode chunk. Guard the import graph at - // its entry point with an allowlist: cross-mode-differentials.ts may import ONLY - // the precomputed index and the (type-only, runtime-erased) catalog type. - // Anything else fails — a direct `@/lib/differentials` re-import, a NEW helper - // that transitively pulls it, or a dynamic `import()` / `require()` form — because - // any such regression must add a new import specifier to THIS file. - const source = readFileSync( + // produced. Walk the resolved runtime import graph from the lazy entry so a + // helper (or a value-import of an allowlisted type module) cannot reintroduce + // @/lib/differentials / the snapshot while a surface-level regex stays green. + const srcRoot = join(process.cwd(), "src"); + const entryPath = resolve(process.cwd(), "src/lib/cross-mode-differentials.ts"); + const indexPath = resolve(process.cwd(), "src/data/cross-mode-differentials-index.json"); + const forbiddenPaths = new Set( + [ + "lib/differentials.ts", + "lib/differentials.tsx", + "lib/differentials/index.ts", + "lib/differentials/index.tsx", + "lib/differential-fixtures.ts", + "lib/differential-fixtures.tsx", + ].map((candidate) => join(srcRoot, candidate)), + ); + const snapshotSuffix = join("data", "differentials-snapshot.json"); + + const sideEffectImport = /^import\s+["']([^"']+)["']/gm; + const dynamicImport = /\bimport\s*\(\s*(?:\/\*[\s\S]*?\*\/\s*)*["']([^"']+)["'][\s\S]*?\)/g; + const fromStatement = /^(import|export)\s+([\s\S]+?)\s+from\s+["']([^"']+)["']/gm; + + const hasRuntimeBindings = (kind: string, clause: string): boolean => { + const trimmed = clause.trim(); + if (/^type\b/.test(trimmed)) return false; + const named = trimmed.match(/^\{([\s\S]*)\}$/); + if (named) { + return named[1] + .split(",") + .map((specifier) => specifier.trim()) + .filter(Boolean) + .some((specifier) => !/^type\b/.test(specifier)); + } + return kind === "import" || kind === "export"; + }; + + const resolveImport = (specifier: string, fromFile: string): string | null => { + let base: string; + if (specifier.startsWith("@/")) base = join(srcRoot, specifier.slice(2)); + else if (specifier.startsWith(".")) base = resolve(dirname(fromFile), specifier); + else return null; + + for (const candidate of [ + base, + `${base}.ts`, + `${base}.tsx`, + `${base}.json`, + join(base, "index.ts"), + join(base, "index.tsx"), + ]) { + if (existsSync(candidate) && statSync(candidate).isFile()) return candidate; + } + return null; + }; + + const runtimeImports = (filePath: string): string[] => { + if (filePath.endsWith(".json")) return []; + const source = readFileSync(filePath, "utf8"); + const imports: string[] = []; + const record = (specifier: string) => { + const resolved = resolveImport(specifier, filePath); + if (resolved) imports.push(resolved); + }; + for (const match of source.matchAll(sideEffectImport)) record(match[1]); + for (const match of source.matchAll(dynamicImport)) record(match[1]); + for (const match of source.matchAll(fromStatement)) { + if (hasRuntimeBindings(match[1], match[2])) record(match[3]); + } + return imports; + }; + + const chainFor = (cameFrom: Map, hit: string): string => { + const chain: string[] = []; + for (let step: string | undefined = hit; step; step = cameFrom.get(step) || undefined) { + chain.unshift(relative(process.cwd(), step)); + } + return chain.join(" -> "); + }; + + const isForbidden = (path: string): boolean => + forbiddenPaths.has(path) || path.endsWith(snapshotSuffix); + + const cameFrom = new Map([[entryPath, ""]]); + const queue = [entryPath]; + const seen = new Set([entryPath]); + const offenders: string[] = []; + let reachedIndex = false; + + while (queue.length > 0) { + const currentPath = queue.shift() as string; + if (currentPath === indexPath) reachedIndex = true; + if (isForbidden(currentPath)) { + offenders.push(chainFor(cameFrom, currentPath)); + continue; + } + for (const next of runtimeImports(currentPath)) { + if (seen.has(next)) continue; + seen.add(next); + cameFrom.set(next, currentPath); + queue.push(next); + } + } + + expect( + offenders, + "Lazy cross-mode chunk must not resolve to @/lib/differentials or differentials-snapshot. " + + "Keep the catalog on the precomputed JSON index (or a helper that only imports that index).", + ).toEqual([]); + expect(reachedIndex, "lazy chunk must import the trimmed cross-mode index JSON").toBe(true); + + // Entry-file allowlist remains as a fast fail for accidental direct imports + // (including type-only neighbors that should stay type-only). + const entrySource = readFileSync( fileURLToPath(new URL("../src/lib/cross-mode-differentials.ts", import.meta.url)), "utf8", ); - const allowed = new Set(["@/data/cross-mode-differentials-index.json", "@/lib/cross-mode-links"]); - const specifiers = [ - /\bfrom\s*["']([^"']+)["']/g, // static: import … from "x" - /\bimport\s*\(\s*["']([^"']+)["']/g, // dynamic: import("x") - /\brequire\s*\(\s*["']([^"']+)["']/g, // cjs: require("x") - /(?:^|\n)\s*import\s+["']([^"']+)["']/g, // side-effect: import "x" - ].flatMap((pattern) => [...source.matchAll(pattern)].map((match) => match[1])); - - // Every import specifier must be in the allowlist — so a re-import of - // `@/lib/differentials` (or a helper/dynamic form pulling the snapshot) surfaces - // as a disallowed specifier and fails here. Comment mentions don't count: only - // real `from`/`import()`/`require` specifiers are collected. - expect(specifiers.filter((specifier) => !allowed.has(specifier))).toEqual([]); - expect(specifiers).toContain("@/data/cross-mode-differentials-index.json"); + const entrySpecifiers = [ + /\bfrom\s*["']([^"']+)["']/g, + /\bimport\s*\(\s*["']([^"']+)["']/g, + /\brequire\s*\(\s*["']([^"']+)["']/g, + /(?:^|\n)\s*import\s+["']([^"']+)["']/g, + ].flatMap((pattern) => [...entrySource.matchAll(pattern)].map((match) => match[1])); + const allowedEntry = new Set(["@/data/cross-mode-differentials-index.json", "@/lib/cross-mode-links"]); + expect(entrySpecifiers.filter((specifier) => !allowedEntry.has(specifier))).toEqual([]); }); it("carries the full catalogue and drops bare-number aliases", () => { From 5704a9ddf9af8da278714e2ba34d3124a490b17d Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 27 Jul 2026 23:35:59 +0000 Subject: [PATCH 12/19] docs(ledger): close out #1287 review/sync disposition Record main sync (ledger-only stale DIRTY), resolved-graph guard, and correct attribution for the consumer vs catalog import locks. Co-authored-by: BigSimmo --- docs/branch-review-ledger.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/branch-review-ledger.md b/docs/branch-review-ledger.md index 61776177f0..9136898790 100644 --- a/docs/branch-review-ledger.md +++ b/docs/branch-review-ledger.md @@ -1157,3 +1157,4 @@ This file is append-only. Never rewrite or delete an existing review record; app | 2026-07-27 | PR #1280 / `claude/top-search-design-mockups-w53znc` | `980b4298` | Implemented review follow-up | Synced main; rail overflow observes childList mutations. Temporarily disabled auto-merge to land polish without squash race. | Focused band Vitest 9/9; no provider checks. | | 2026-07-27 | PR #1281 / `claude/safety-planning-tools-page-tsq4vs` | `f7e616d1` | Implemented review polish | StepBuilderCard filled uses `isStepComplete`; clipboard DRAFT assertion added. Auto-merge temporarily disabled to land polish. | Focused safety-plan Vitest 3/3; no provider checks. | | 2026-07-27 | PR #1261/#1262/#1263 audit cluster | closed tips | Close without merge | Closed per review: unsafe lineage / tip markers / privacy+RAG P1s / parallel verify:cheap rewrite. Thin PDF exit-137 salvage opened separately. | Prior Bugbot + merge-tree evidence; no provider checks. | +| 2026-07-27 | PR #1287 / `claude/site-audit-quick-wins-21v9gb` | `88d8638974075fa91334c2bb4a0e6b54fda00176` | Review closeout: main sync + resolved-graph guard + ledger attribution | FIXED. Cause of GitHub CONFLICTING/DIRTY: both tips appended `docs/branch-review-ledger.md` (union); `git merge-tree` was clean — merged `origin/main` (#1284 ledger rows). CodeRabbit recursive import-graph ask: walk resolved runtime imports from `cross-mode-differentials.ts` (services/forms boundary pattern) + keep entry allowlist. Supersedes residual wording on rows 1148/1149: import-graph lock + scripts-index + comment already landed; `client-performance-boundaries` guards the consumer dynamic import, `cross-mode-differentials-index` guards the catalog module/graph. Hosted Production UI already green after hydration settle. | Focused vitest index+boundaries 10/10; `check:cross-mode-index` PASS; merge-tree CLEAN vs origin/main; prior Production UI PASS on `f738f083`; no provider-backed checks. | From 662a93f826ea6ba34df9d15677ef5ae2446a2e40 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 27 Jul 2026 23:45:15 +0000 Subject: [PATCH 13/19] style: prettier-format cross-mode differentials index test Static PR Format check failed solely on this file after the resolved-graph guard landed; no logic change. Co-authored-by: BigSimmo --- tests/cross-mode-differentials-index.test.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/cross-mode-differentials-index.test.ts b/tests/cross-mode-differentials-index.test.ts index 3915fc40d2..01ecc1c1b6 100644 --- a/tests/cross-mode-differentials-index.test.ts +++ b/tests/cross-mode-differentials-index.test.ts @@ -116,8 +116,7 @@ describe("cross-mode differentials precomputed index", () => { return chain.join(" -> "); }; - const isForbidden = (path: string): boolean => - forbiddenPaths.has(path) || path.endsWith(snapshotSuffix); + const isForbidden = (path: string): boolean => forbiddenPaths.has(path) || path.endsWith(snapshotSuffix); const cameFrom = new Map([[entryPath, ""]]); const queue = [entryPath]; From cec73fe45d831c10e495638ae516a32f8837ce75 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 27 Jul 2026 23:45:15 +0000 Subject: [PATCH 14/19] docs(ledger): record Static PR format fix for #1287 Co-authored-by: BigSimmo --- docs/branch-review-ledger.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/branch-review-ledger.md b/docs/branch-review-ledger.md index 9136898790..0ccae173bf 100644 --- a/docs/branch-review-ledger.md +++ b/docs/branch-review-ledger.md @@ -1158,3 +1158,4 @@ This file is append-only. Never rewrite or delete an existing review record; app | 2026-07-27 | PR #1281 / `claude/safety-planning-tools-page-tsq4vs` | `f7e616d1` | Implemented review polish | StepBuilderCard filled uses `isStepComplete`; clipboard DRAFT assertion added. Auto-merge temporarily disabled to land polish. | Focused safety-plan Vitest 3/3; no provider checks. | | 2026-07-27 | PR #1261/#1262/#1263 audit cluster | closed tips | Close without merge | Closed per review: unsafe lineage / tip markers / privacy+RAG P1s / parallel verify:cheap rewrite. Thin PDF exit-137 salvage opened separately. | Prior Bugbot + merge-tree evidence; no provider checks. | | 2026-07-27 | PR #1287 / `claude/site-audit-quick-wins-21v9gb` | `88d8638974075fa91334c2bb4a0e6b54fda00176` | Review closeout: main sync + resolved-graph guard + ledger attribution | FIXED. Cause of GitHub CONFLICTING/DIRTY: both tips appended `docs/branch-review-ledger.md` (union); `git merge-tree` was clean — merged `origin/main` (#1284 ledger rows). CodeRabbit recursive import-graph ask: walk resolved runtime imports from `cross-mode-differentials.ts` (services/forms boundary pattern) + keep entry allowlist. Supersedes residual wording on rows 1148/1149: import-graph lock + scripts-index + comment already landed; `client-performance-boundaries` guards the consumer dynamic import, `cross-mode-differentials-index` guards the catalog module/graph. Hosted Production UI already green after hydration settle. | Focused vitest index+boundaries 10/10; `check:cross-mode-index` PASS; merge-tree CLEAN vs origin/main; prior Production UI PASS on `f738f083`; no provider-backed checks. | +| 2026-07-27 | PR #1287 / `claude/site-audit-quick-wins-21v9gb` | `662a93f826ea6ba34df9d15677ef5ae2446a2e40` | Static PR Format check fix | FIXED. Hosted `static-pr` Format check failed on Prettier for `tests/cross-mode-differentials-index.test.ts` after the resolved-graph guard. Reformatted; no behaviour change. Mergeable vs main (merge-tree CLEAN, 0 behind). Prior review threads already dispositioned. | `prettier --check` local PASS for the file; vitest index test 3/3; no provider-backed checks. | From 1b31607b05f739d40004203c5b05a1fae1675ba4 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 28 Jul 2026 01:40:07 +0000 Subject: [PATCH 15/19] docs(ledger): record #1287 main-sync conflict repair Append-only closeout for the outstanding-issues merge resolution and re-verification after syncing current origin/main. Co-authored-by: BigSimmo --- docs/branch-review-ledger.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/branch-review-ledger.md b/docs/branch-review-ledger.md index c9cc162453..1f0f50384e 100644 --- a/docs/branch-review-ledger.md +++ b/docs/branch-review-ledger.md @@ -1190,3 +1190,4 @@ This file is append-only. Never rewrite or delete an existing review record; app | 2026-07-27 | PR #1275 / `codex/identify-and-fix-performance-issues-during-mode-switch` | `f4b55751` | CodeRabbit behavioral prefetch-test disposition | RESOLVED. Behavioral DOM coverage for menu-open prefetch paths (later adapted to per-option model). | focused vitest PASS; no provider checks. | | 2026-07-27 | PR #1275 / `codex/identify-and-fix-performance-issues-during-mode-switch` | `81005d18` | Codex mojibake-ledger disposition | RESOLVED. Historical rows restored byte-for-byte from origin/main; append-only thereafter. | exact prefix check; check:branch-review-ledger PASS; no provider checks. | | 2026-07-28 | PR #1275 / `codex/identify-and-fix-performance-issues-during-mode-switch` | `01469840` | CI/merge conflict closeout + Bugbot triage | RESOLVED merge conflict with origin/main (7ae4eb49 per-option prefetchModeHome). Dropped superseded bulk prefetchModeHomes; menu-open warms only highlighted option; focus/pointer scanning retained. CodeRabbit duplicate/malformed ledger threads dispositioned (main-owned). No open cursor[bot] Bugbot product defects. | merge-tree clean; focused vitest 12/12; ledger restored from main + appends; no provider checks. | +| 2026-07-28 | PR #1287 / `claude/site-audit-quick-wins-21v9gb` | `562bce2b3c90bf1790da9683077915cd3f8fdb17` | Main sync + conflict repair + Bugbot closeout | FIXED. Real CONFLICTING vs advanced main was `docs/outstanding-issues.md` only (ledger/ui-smoke auto-merged). Resolution keeps `#012` in Resolved with this PR's outcome while retaining main's newer open/archive rows. CI on prior tip was fully green (Static/Production UI/PR required); re-runs after sync. Review threads already dispositioned (resolved-graph guard, ledger residuals, attribution). | merge-tree CLEAN; focused vitest index+boundaries 10/10; `check:cross-mode-index` PASS; `prettier --check` on touched tests PASS; Bugbot pass pending agent; no provider-backed checks. | From 742b0d154f7058800c945b3ec6e720eef24ce4c0 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 28 Jul 2026 01:42:10 +0000 Subject: [PATCH 16/19] issues: complete #012 queue closeout after main sync Per /issues done: remove #012 from the recommended execution queue composite (#013/#016 remain) and drop the Before #012 sequencing hint on #017. #012 stays in Resolved/archive with the shipped outcome. Co-authored-by: BigSimmo --- docs/outstanding-issues.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/outstanding-issues.md b/docs/outstanding-issues.md index aac00b0e69..821d9161db 100644 --- a/docs/outstanding-issues.md +++ b/docs/outstanding-issues.md @@ -62,10 +62,10 @@ removed after current-main verification; it is not missing recommended work. | 13 | `#056` | A2 | Operator — Supabase/Railway + Specialist | Next approved staging schema window | 2–4 hours | Reconcile the existing healthy, empty staging tier's 23-migration history gap using the exact repository migration chain, then re-run indexing, health, identity and data-boundary proof. Never recreate it or copy production clinical documents. | | 14 | `#057` | A2 | High — release/SRE + Operator | After `#056` | 2–4 hours plus soak | Run documented staging soak and rollback against an exact candidate. Retain latency/error/rollback evidence; stop on unsafe data, identity mismatch, or unowned rollback. | | 16 | `#011` | A3 | Operator — Supabase capacity | Immediately before first compute scale-up | 30–60 min plus observation | Switch Auth to percentage allocation, record before/after, and run approved advisor/health checks. Stop if no scale-up is planned. | -| 17 | `#017` | A3 | High — performance/browser | Before `#012`/`#013`/`#016`; approved live-site window | 1–2 hours | Capture reproducible mobile/desktop Lighthouse/Web-Vitals evidence and decide whether payload work is justified. Stop if metrics are acceptable or evidence is too noisy. | +| 17 | `#017` | A3 | High — performance/browser | Before `#013`/`#016`; approved live-site window | 1–2 hours | Capture reproducible mobile/desktop Lighthouse/Web-Vitals evidence and decide whether payload work is justified. Stop if metrics are acceptable or evidence is too noisy. | | 18 | `#033` | A3 | Specialist — prompt/source governance | After `#022` and explicit evaluation approval | 1–2 days plus approved eval | Design unknown-vs-adverse metadata wording and prompt tests. Require no supported-grounding drop and zero citation failures; stop on broad over-caveating or degradation. | | 19 | `#037` | A3 | Operator — clinical/product + Standard | Next trust-policy review | 30–60 min; up to 0.5 day | Decide whether routine claims cap at medium trust. Record policy; if accepted, change only the flag/expectations and run focused tests. | -| 20 | `#012`, `#013`, `#016` | A3 | High — bundling/runtime performance | After `#017` or equivalent evidence | 0.5–2 days/route | Optimize only a production route with measured payload/render/motion harm. Require material gain plus focused, `verify:cheap`, and browser evidence; stop on small gain. | +| 20 | `#013`, `#016` | A3 | High — bundling/runtime performance | After `#017` or equivalent evidence | 0.5–2 days/route | Optimize only a production route with measured payload/render/motion harm. Require material gain plus focused, `verify:cheap`, and browser evidence; stop on small gain. | | 21 | `#035` | A3 | Specialist — evidence rules | After a demonstrated missed conflict | 0.5–1 day design; code separate | Define a clinically reviewed conflict class with positive and negative fixtures. Stop if no bounded class can be shown; behavior change requires protected review. | | 22 | `#027` | Optional | Operator — SRE/provider | When an owned external alert path is wanted | 1–2 hours | Decide vendor/cost/privacy/owner; if accepted, prove one non-PHI outage and recovery alert. Stop when no responder owns it. | | 23 | `#028` | Optional | Specialist privacy/observability + Operator | After privacy/ownership/cost approval | 1–3 days | Define vendor/region/retention/redaction/sampling/source-map envelope before SDK work. Prove no clinical text, identifiers, or secrets leave; stop if unacceptable. | From 860dd8098f396f76f650d222f6aa2eeb5fb5748b Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 28 Jul 2026 01:42:10 +0000 Subject: [PATCH 17/19] docs(ledger): record Bugbot #012 queue closeout for #1287 Co-authored-by: BigSimmo --- docs/branch-review-ledger.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/branch-review-ledger.md b/docs/branch-review-ledger.md index 1f0f50384e..b26c571d38 100644 --- a/docs/branch-review-ledger.md +++ b/docs/branch-review-ledger.md @@ -1191,3 +1191,4 @@ This file is append-only. Never rewrite or delete an existing review record; app | 2026-07-27 | PR #1275 / `codex/identify-and-fix-performance-issues-during-mode-switch` | `81005d18` | Codex mojibake-ledger disposition | RESOLVED. Historical rows restored byte-for-byte from origin/main; append-only thereafter. | exact prefix check; check:branch-review-ledger PASS; no provider checks. | | 2026-07-28 | PR #1275 / `codex/identify-and-fix-performance-issues-during-mode-switch` | `01469840` | CI/merge conflict closeout + Bugbot triage | RESOLVED merge conflict with origin/main (7ae4eb49 per-option prefetchModeHome). Dropped superseded bulk prefetchModeHomes; menu-open warms only highlighted option; focus/pointer scanning retained. CodeRabbit duplicate/malformed ledger threads dispositioned (main-owned). No open cursor[bot] Bugbot product defects. | merge-tree clean; focused vitest 12/12; ledger restored from main + appends; no provider checks. | | 2026-07-28 | PR #1287 / `claude/site-audit-quick-wins-21v9gb` | `562bce2b3c90bf1790da9683077915cd3f8fdb17` | Main sync + conflict repair + Bugbot closeout | FIXED. Real CONFLICTING vs advanced main was `docs/outstanding-issues.md` only (ledger/ui-smoke auto-merged). Resolution keeps `#012` in Resolved with this PR's outcome while retaining main's newer open/archive rows. CI on prior tip was fully green (Static/Production UI/PR required); re-runs after sync. Review threads already dispositioned (resolved-graph guard, ledger residuals, attribution). | merge-tree CLEAN; focused vitest index+boundaries 10/10; `check:cross-mode-index` PASS; `prettier --check` on touched tests PASS; Bugbot pass pending agent; no provider-backed checks. | +| 2026-07-28 | PR #1287 / `claude/site-audit-quick-wins-21v9gb` | `742b0d154f7058800c945b3ec6e720eef24ce4c0` | Bugbot P2: finish #012 recommended-queue closeout | FIXED. After main-sync conflict repair, `#012` was correctly Resolved/Open-clean but the Recommended execution queue still listed it (order 20 composite + #017 Before hint). Applied `/issues done` queue rewrite: order 20 is now `#013`, `#016`; #017 timing is Before `#013`/`#016`. | Bugbot review on `1b31607b`; queue/Open/Resolved audit; focused vitest previously green; no provider-backed checks. | From b90d659be12efedd339297daa2d289c2bd7ebb03 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 28 Jul 2026 01:51:21 +0000 Subject: [PATCH 18/19] style: prettier-format outstanding-issues after #012 queue closeout Static PR Format check failed solely on docs/outstanding-issues.md after the recommended-queue rewrite; no content change beyond formatting. Co-authored-by: BigSimmo --- docs/outstanding-issues.md | 232 ++++++++++++++++++------------------- 1 file changed, 115 insertions(+), 117 deletions(-) diff --git a/docs/outstanding-issues.md b/docs/outstanding-issues.md index 821d9161db..1b468e3d2e 100644 --- a/docs/outstanding-issues.md +++ b/docs/outstanding-issues.md @@ -48,32 +48,32 @@ removed after current-main verification; it is not missing recommended work. database/RAG/clinical/privacy expertise; Operator = named provider/product/legal authority. - **Estimate:** focused active time, excluding approval, hosted runtime, soak, and review waits. -| Order | ID(s) | Acuity | Capability | When | Estimate | Outcome, gate, verification, and stopping condition | -| ----: | ---------------------- | -------- | ------------------------------------------- | ------------------------------------------------------------------ | ------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| 1 | `#059` | A1 | Operator security + independent reviewer | Immediate approved security window | 1–3 hours plus verification | Verify every reported exposed credential (GitHub, OpenAI, Supabase service role/database, E2E) is retired; rotate anything still valid and update only intended secret stores. Never record values; stop before provider action without approval. | -| 2 | `#053` | A1 | Operator — legal/privacy | Start now; finish before real patient use/privacy-approved release | 4–8 hours internal; 1–6 weeks elapsed | Execute DPAs; decide ZDR/residency; obtain cache behavior in writing; review subprocessors; obtain APP 8 and APP 5/1 counsel sign-off. Do not change public copy before approval. | -| 5 | `#024` | A2 | High — browser/Next diagnostics | Provider-free macOS Safari host available | 1–2 hours | Reproduce document-source fallbacks in Safari/STP without Playwright interception; capture `_rsc` response evidence. Treat as an app defect only if native Safari reproduces; otherwise return to the harness. Never suppress `pageerror` or change CORS without proof. | -| 6 | `#022` | A2 | Operator — clinical governance + Specialist | Policy implemented locally; hosted apply and human review pending | 1–2 hours apply; 0.5–1 day first ten | The auditable BMJ `third_party_reference_attested` policy, migration and top-ten evidence manifest are prepared without changing `clinical_validation_status=unverified`. A qualified operator must review evidence, apply the migration deliberately, attest eligible records, review the ten visible local documents, then remeasure warnings. | -| 7 | `#023` | A2 | Specialist — RAG/browser diagnostics | After hosted dependency audit is green | 1–2 hours | Capture the skipped Firefox/WebKit scheduled datapoint and disposition the human irrelevant-at-10 labels. Retrieval and answer artifacts are already compared under resolved #051; do not spend on another RAG run. | -| 8 | `#018` | A2 | Specialist — clinical RAG/retrieval | Lithium closed; ADHD/metabolic evidence debt remains | Corpus/operator follow-up | Lithium's bounded subject/row-aware fix passed its targeted answer plus the full 36-case retrieval and 44-case answer canaries. ADHD's expected CAMHS document remains absent and the surfaced chart has no accessible table; metabolic schedule evidence remains unavailable and its standalone classifier candidate was reverted. | -| 10 | `#001` | A2 | Specialist — retrieval/ranking | After rollout approval | 0.5–1 day plus canary | Keep semantic reranking off unless an approved ambiguity comparison preserves 36/36, recall 1.0, zero per-case regressions, and shows measured gain; otherwise record keep-off and stop. | -| 11 | `#025` | A2 | Operator — Railway/GitHub/chat/Supabase | Next approved observability window | 1–3 hours/channel | Choose owned deployment, CI, ingestion, and SLO alerts; mock first, then one approved controlled provider event/channel. The merged Supabase trigger remains inert until its verified inputs are configured. Stop without an accountable responder. | -| 12 | `#055` | A2 | Specialist release owner + Operator | Before next full-confidence release/handoff | 2–4 hours plus runtime | On one exact SHA, run local/provider gates, Firefox/WebKit, required hosted CI, and close actionable GitHub threads. Stop at first failure and rerun only the repaired smallest gate. | -| 13 | `#056` | A2 | Operator — Supabase/Railway + Specialist | Next approved staging schema window | 2–4 hours | Reconcile the existing healthy, empty staging tier's 23-migration history gap using the exact repository migration chain, then re-run indexing, health, identity and data-boundary proof. Never recreate it or copy production clinical documents. | -| 14 | `#057` | A2 | High — release/SRE + Operator | After `#056` | 2–4 hours plus soak | Run documented staging soak and rollback against an exact candidate. Retain latency/error/rollback evidence; stop on unsafe data, identity mismatch, or unowned rollback. | -| 16 | `#011` | A3 | Operator — Supabase capacity | Immediately before first compute scale-up | 30–60 min plus observation | Switch Auth to percentage allocation, record before/after, and run approved advisor/health checks. Stop if no scale-up is planned. | -| 17 | `#017` | A3 | High — performance/browser | Before `#013`/`#016`; approved live-site window | 1–2 hours | Capture reproducible mobile/desktop Lighthouse/Web-Vitals evidence and decide whether payload work is justified. Stop if metrics are acceptable or evidence is too noisy. | -| 18 | `#033` | A3 | Specialist — prompt/source governance | After `#022` and explicit evaluation approval | 1–2 days plus approved eval | Design unknown-vs-adverse metadata wording and prompt tests. Require no supported-grounding drop and zero citation failures; stop on broad over-caveating or degradation. | -| 19 | `#037` | A3 | Operator — clinical/product + Standard | Next trust-policy review | 30–60 min; up to 0.5 day | Decide whether routine claims cap at medium trust. Record policy; if accepted, change only the flag/expectations and run focused tests. | +| Order | ID(s) | Acuity | Capability | When | Estimate | Outcome, gate, verification, and stopping condition | +| ----: | -------------- | -------- | ------------------------------------------- | ------------------------------------------------------------------ | ------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| 1 | `#059` | A1 | Operator security + independent reviewer | Immediate approved security window | 1–3 hours plus verification | Verify every reported exposed credential (GitHub, OpenAI, Supabase service role/database, E2E) is retired; rotate anything still valid and update only intended secret stores. Never record values; stop before provider action without approval. | +| 2 | `#053` | A1 | Operator — legal/privacy | Start now; finish before real patient use/privacy-approved release | 4–8 hours internal; 1–6 weeks elapsed | Execute DPAs; decide ZDR/residency; obtain cache behavior in writing; review subprocessors; obtain APP 8 and APP 5/1 counsel sign-off. Do not change public copy before approval. | +| 5 | `#024` | A2 | High — browser/Next diagnostics | Provider-free macOS Safari host available | 1–2 hours | Reproduce document-source fallbacks in Safari/STP without Playwright interception; capture `_rsc` response evidence. Treat as an app defect only if native Safari reproduces; otherwise return to the harness. Never suppress `pageerror` or change CORS without proof. | +| 6 | `#022` | A2 | Operator — clinical governance + Specialist | Policy implemented locally; hosted apply and human review pending | 1–2 hours apply; 0.5–1 day first ten | The auditable BMJ `third_party_reference_attested` policy, migration and top-ten evidence manifest are prepared without changing `clinical_validation_status=unverified`. A qualified operator must review evidence, apply the migration deliberately, attest eligible records, review the ten visible local documents, then remeasure warnings. | +| 7 | `#023` | A2 | Specialist — RAG/browser diagnostics | After hosted dependency audit is green | 1–2 hours | Capture the skipped Firefox/WebKit scheduled datapoint and disposition the human irrelevant-at-10 labels. Retrieval and answer artifacts are already compared under resolved #051; do not spend on another RAG run. | +| 8 | `#018` | A2 | Specialist — clinical RAG/retrieval | Lithium closed; ADHD/metabolic evidence debt remains | Corpus/operator follow-up | Lithium's bounded subject/row-aware fix passed its targeted answer plus the full 36-case retrieval and 44-case answer canaries. ADHD's expected CAMHS document remains absent and the surfaced chart has no accessible table; metabolic schedule evidence remains unavailable and its standalone classifier candidate was reverted. | +| 10 | `#001` | A2 | Specialist — retrieval/ranking | After rollout approval | 0.5–1 day plus canary | Keep semantic reranking off unless an approved ambiguity comparison preserves 36/36, recall 1.0, zero per-case regressions, and shows measured gain; otherwise record keep-off and stop. | +| 11 | `#025` | A2 | Operator — Railway/GitHub/chat/Supabase | Next approved observability window | 1–3 hours/channel | Choose owned deployment, CI, ingestion, and SLO alerts; mock first, then one approved controlled provider event/channel. The merged Supabase trigger remains inert until its verified inputs are configured. Stop without an accountable responder. | +| 12 | `#055` | A2 | Specialist release owner + Operator | Before next full-confidence release/handoff | 2–4 hours plus runtime | On one exact SHA, run local/provider gates, Firefox/WebKit, required hosted CI, and close actionable GitHub threads. Stop at first failure and rerun only the repaired smallest gate. | +| 13 | `#056` | A2 | Operator — Supabase/Railway + Specialist | Next approved staging schema window | 2–4 hours | Reconcile the existing healthy, empty staging tier's 23-migration history gap using the exact repository migration chain, then re-run indexing, health, identity and data-boundary proof. Never recreate it or copy production clinical documents. | +| 14 | `#057` | A2 | High — release/SRE + Operator | After `#056` | 2–4 hours plus soak | Run documented staging soak and rollback against an exact candidate. Retain latency/error/rollback evidence; stop on unsafe data, identity mismatch, or unowned rollback. | +| 16 | `#011` | A3 | Operator — Supabase capacity | Immediately before first compute scale-up | 30–60 min plus observation | Switch Auth to percentage allocation, record before/after, and run approved advisor/health checks. Stop if no scale-up is planned. | +| 17 | `#017` | A3 | High — performance/browser | Before `#013`/`#016`; approved live-site window | 1–2 hours | Capture reproducible mobile/desktop Lighthouse/Web-Vitals evidence and decide whether payload work is justified. Stop if metrics are acceptable or evidence is too noisy. | +| 18 | `#033` | A3 | Specialist — prompt/source governance | After `#022` and explicit evaluation approval | 1–2 days plus approved eval | Design unknown-vs-adverse metadata wording and prompt tests. Require no supported-grounding drop and zero citation failures; stop on broad over-caveating or degradation. | +| 19 | `#037` | A3 | Operator — clinical/product + Standard | Next trust-policy review | 30–60 min; up to 0.5 day | Decide whether routine claims cap at medium trust. Record policy; if accepted, change only the flag/expectations and run focused tests. | | 20 | `#013`, `#016` | A3 | High — bundling/runtime performance | After `#017` or equivalent evidence | 0.5–2 days/route | Optimize only a production route with measured payload/render/motion harm. Require material gain plus focused, `verify:cheap`, and browser evidence; stop on small gain. | -| 21 | `#035` | A3 | Specialist — evidence rules | After a demonstrated missed conflict | 0.5–1 day design; code separate | Define a clinically reviewed conflict class with positive and negative fixtures. Stop if no bounded class can be shown; behavior change requires protected review. | -| 22 | `#027` | Optional | Operator — SRE/provider | When an owned external alert path is wanted | 1–2 hours | Decide vendor/cost/privacy/owner; if accepted, prove one non-PHI outage and recovery alert. Stop when no responder owns it. | -| 23 | `#028` | Optional | Specialist privacy/observability + Operator | After privacy/ownership/cost approval | 1–3 days | Define vendor/region/retention/redaction/sampling/source-map envelope before SDK work. Prove no clinical text, identifiers, or secrets leave; stop if unacceptable. | -| 24 | `#038` | Optional | High — product/design architecture | When a new comparison surface is approved | 0.5–1 day | Define a shared interaction contract without flattening mode-specific content. Stop when no concrete new surface exists. | -| 25 | `#040` | Optional | High — visual QA/accessibility | When baseline owner/update workflow exists | 1–2 days | Establish a small stable desktop/mobile/accessibility baseline set. Do not make it blocking if flake or maintenance cost outweighs detection value. | -| 26 | `#039` | Optional | High — frontend architecture | During a concrete catalogue-toolbar project | 0.5–1 day inventory; 1–3 days code | Converge only repeated toolbar behavior without flattening search semantics. Stop when there is no bounded implementation target. | -| 27 | `#065` | A2 | High — document-viewer UI | Only when the user explicitly resumes the paused task | 0.5–1.5 days | Finish the compact source-text accordion, citation/search auto-open, print restoration, and 320/390/1280 px coverage. Keep the preserved branch untouched until explicit resume; no provider calls. | -| 28 | `#079` | Optional | High — repository hygiene | In explicitly scheduled batches | 30–60 minutes per batch | Disposition at most ten retained worktrees per pass using owner, PR, review-ledger, ancestry, and patch evidence. Preserve every dirty, active, secret-bearing, post-freeze, or ambiguous worktree and stop rather than broad-cleaning. | +| 21 | `#035` | A3 | Specialist — evidence rules | After a demonstrated missed conflict | 0.5–1 day design; code separate | Define a clinically reviewed conflict class with positive and negative fixtures. Stop if no bounded class can be shown; behavior change requires protected review. | +| 22 | `#027` | Optional | Operator — SRE/provider | When an owned external alert path is wanted | 1–2 hours | Decide vendor/cost/privacy/owner; if accepted, prove one non-PHI outage and recovery alert. Stop when no responder owns it. | +| 23 | `#028` | Optional | Specialist privacy/observability + Operator | After privacy/ownership/cost approval | 1–3 days | Define vendor/region/retention/redaction/sampling/source-map envelope before SDK work. Prove no clinical text, identifiers, or secrets leave; stop if unacceptable. | +| 24 | `#038` | Optional | High — product/design architecture | When a new comparison surface is approved | 0.5–1 day | Define a shared interaction contract without flattening mode-specific content. Stop when no concrete new surface exists. | +| 25 | `#040` | Optional | High — visual QA/accessibility | When baseline owner/update workflow exists | 1–2 days | Establish a small stable desktop/mobile/accessibility baseline set. Do not make it blocking if flake or maintenance cost outweighs detection value. | +| 26 | `#039` | Optional | High — frontend architecture | During a concrete catalogue-toolbar project | 0.5–1 day inventory; 1–3 days code | Converge only repeated toolbar behavior without flattening search semantics. Stop when there is no bounded implementation target. | +| 27 | `#065` | A2 | High — document-viewer UI | Only when the user explicitly resumes the paused task | 0.5–1.5 days | Finish the compact source-text accordion, citation/search auto-open, print restoration, and 320/390/1280 px coverage. Keep the preserved branch untouched until explicit resume; no provider calls. | +| 28 | `#079` | Optional | High — repository hygiene | In explicitly scheduled batches | 30–60 minutes per batch | Disposition at most ten retained worktrees per pass using owner, PR, review-ledger, ancestry, and patch evidence. Preserve every dirty, active, secret-bearing, post-freeze, or ambiguous worktree and stop rather than broad-cleaning. | @@ -89,100 +89,98 @@ removed after current-main verification; it is not missing recommended work. > > **Exact-head release confirmation (2026-07-28):** after final review narrowed expanded chunk selection to the measured clozapine blood-count action shape, `output/rag-retrieval-post-exact-head.json` repeated all 36 cases with document/content recall 1.0, MRR 0.8921, content MRR 0.9406, nDCG 0.9308, irrelevant-at-10 0.0917, zero failures and zero per-case document/content reciprocal-rank regressions versus `rag-retrieval-post-final.json`. Median latency rose 13,563 -> 19,729 ms while p90 improved 56,765 -> 55,660 ms; neither run had a latency-failed case, so no ranking or latency gate changed. Cache-bypassed exact-head answer probes for both admission/discharge cases and the clozapine threshold case were substantive, grounded, expected-source-backed and free of citation/numeric/route failures; all used zero provider requests and $0 generation cost. This was the protected behavior-change merge gate, not a rerun for #023. - -| ID | Pri | Type | Summary | Detail / next action | Source | Added | -| ---- | --- | ----- | --------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- | ---------- | -| #059 | P1 | task | Verify containment of every credential reported exposed in chat | **Outcome:** every reported exposed credential is rejected or retired. **Next:** in approved security windows, verify and revoke or rotate the GitHub token, OpenAI key, Supabase service-role JWT, database password, and E2E credential; create replacements only when required and update only intended secret stores. **Success:** provider evidence confirms the old credentials cannot authenticate, replacements are distinct and minimally scoped, presence/readiness checks pass, and secret scans remain clean. **Stop:** no provider or secret-store action without approval; never print or paste values into Git, logs, issues, or chat. | session 2026-07-24 security reconciliation; AI Agent Target Manifest | 2026-07-24 | -| #065 | P2 | task | Complete the paused compact document source-text accordion | **Outcome:** the document viewer uses compact nested disclosures while retaining complete text, citation/search navigation, print behavior, and composer clearance. **Next:** only when the user explicitly resumes, reconcile `codex/chat-document-text-accordion-7cb4` with current `main` and complete the focused 320/390/1280 px tests. **Success:** default disclosures are closed; deep links and search open only the active passage; printing expands/restores state; no overflow. **Verify:** focused document-viewer Playwright, `verify:cheap`, `verify:ui`, and static production-readiness. **Stop:** remain paused until explicit user return; no provider calls. | paused document-viewer task; `codex/chat-document-text-accordion-7cb4` | 2026-07-24 | -| #001 | P2 | task | Semantic reranking still gated off | `RAG_SEMANTIC_RERANK_ENABLED=false` from PR #901. Do not enable until the provider-backed 36/36 retrieval-quality gate **and** an ambiguity-focused canary are explicitly approved and recorded. | `docs/process-hardening.md` (Semantic reranking rollout debt); PR #901 | 2026-07-21 | -| #053 | P1 | task | Execute cross-border privacy/legal package | Execute OpenAI and Railway DPAs; decide ZDR and Australian data residency; obtain prompt-cache behavior in writing; review subprocessors; obtain APP 8 and APP 5/1 counsel sign-off. Do not represent the release as privacy-approved or alter final public privacy wording before sign-off. | `docs/openai-cross-border-basis.md`; `docs/privacy-impact-assessment.md` | 2026-07-24 | -| #055 | P2 | task | Run one exact-SHA full release and PR gate | Before the next full-confidence release/handoff, record the candidate/PR SHA and run the local/provider release gates, Firefox/WebKit, required hosted CI, and actionable GitHub review-thread closure once. Stop at the first actionable failure and rerun only the repaired smallest gate. | `docs/launch-operator-runbook.md`; `docs/codex-review-protocol.md` | 2026-07-24 | -| #056 | P2 | task | Reconcile the existing staging migration history | `Clinical KB Staging` already exists as a healthy, empty Supabase/Railway tier with distinct secrets and no production clinical data, but it is 23 repository migrations behind. In the next approved staging schema window, apply the exact missing migration chain, then re-run indexing, health, identity and data-boundary proof. Do not recreate the environment or copy production clinical documents. | current-main staging verification; `docs/staging-setup.md`; `docs/operator-backlog.md` | 2026-07-27 | -| #057 | P2 | task | Complete staging soak and rollback rehearsal | After #056, run the documented soak and rollback against an exact candidate; retain latency/error/rollback evidence. Stop on unsafe data, identity mismatch, or an unowned rollback decision. | `docs/launch-operator-runbook.md`; `docs/capacity-review.md` | 2026-07-24 | -| #005 | P3 | rec | `finalScore` saturates at clamp ceiling | Base + ~40 stacked boosts routinely exceed 1.0, so strong matches tie at 1.0 and order by an arbitrary `document_id` tiebreak. If ranking is ever revisited, break ties by the **pre-clamp** score rather than raising the `[0,1]` ceiling (downstream gates assume `[0,1]`). Ordering already sorts by the unbounded pre-clamp `rankScore` (`clinical-search.ts:1735,1927,1950-1955`), so the clamp confines only the reported confidence value, not result order. Not a defect on the current golden set; any change here is a protected RAG surface (canary required). | `docs/rag-hybrid-findings-and-todo.md` P1 item 4; `src/lib/clinical-search.ts:1735` | 2026-07-21 | -| #011 | P3 | task | Auth DB-connection allocation is operator-only | Supabase Auth (GoTrue) is capped at ~10 absolute DB connections (Supabase perf advisor). Switch to **percentage-based** allocation in the Supabase **dashboard** before the first compute scale-up — **not settable via SQL/MCP** (operator-owned). Verify via a staging soak + an approval-gated read-only advisor re-check. | `docs/auth-connection-cap-runbook.md`; `docs/process-hardening.md` (Known follow-up debts) | 2026-07-21 | -| #013 | P3 | rec | Route-chunk + mockup catalogue JSON weight | `build:analyze`: `/specifiers` ships `specifiers-search-index.json` (~180 KB parsed), `/forms` ships `forms-catalog.json` (~132 KB), `/formulation` ships `formulation-content.json` (~52 KB, client-side local search — needs index/full split or a search endpoint, architectural). All route-scoped (not initial bundle). Also `*-mockups.tsx` (~100 KB across chunks) build though `/mockups` 404s in prod — exclude from the prod artifact. | session 2026-07-21 (build:analyze) | 2026-07-21 | -| #016 | P3 | rec | "Big but not easy" structural + motion perf | Deferred larger levers: (a) nonce-CSP forces every product route to `╞Æ Dynamic` (zero static generation) — evaluate Partial Prerendering / static shells for the static clinical catalogues (DSM/differentials/therapy/specifiers/formulation); (b) sidebar expand/collapse animates `grid-template-columns` (biggest smoothness cost, motion-gated — needs a transform-overlay rethink); (c) Therapy Compass fetches 692 KB / 2.5 MB JSON client-side (defer until interaction + confirm brotli); (d) settings/setup/admin dialogs static-imported into the home chunk (`next/dynamic` them). | session 2026-07-21 (build route table + design audit) | 2026-07-21 | -| #017 | P3 | task | Field Web-Vitals baseline via live Lighthouse | In-sandbox runtime vitals were blocked (prod server hard-requires Supabase secrets; dev-mode CLS measured excellent at 0.00–0.04, content-first pages 0.000). Run Lighthouse against `psychiatry.tools` for real LCP/INP/CLS to prioritize #012–#016 by measured impact rather than reasoning. | session 2026-07-21 (measurement pass) | 2026-07-21 | -| #018 | P2 | task | Split the lithium, ADHD and metabolic residuals by mechanism | Current evidence keeps the mechanisms separate. **Lithium — closed within this item:** the row/atom-aware subject guard, foreign-parameter rejection and query-specific range promotion returned `0.5–1.0 mmol/L` with correct targeting/citation; the full retrieval canary remained 36/36 with recall 1.0 and zero per-case RR regressions, and the full answer canary passed every blocking gate. **ADHD — open corpus debt:** `CG.MHSP.ADHD.pdf` is absent from the hosted corpus and the retrieved chart exposes `accessible_table_count=0`; repair corpus/fixture or ingestion evidence rather than weakening extractive budgets. **Metabolic — open structured-evidence debt:** the standalone plural classifier worsened the live answer and was reverted; obtain auditable schedule text/table evidence before another candidate. | targeted live lithium/ADHD/metabolic evidence 2026-07-27; `docs/evidence/rag-reliability-evidence-2026-07-27.md`; refuted approaches | 2026-07-21 | -| #021 | P3 | rec | E-3d H2 residual: strong/comparison generation discards | approx. 6 generation attempts per full 44-case run still fail the final quality gate and fall to extractive on strong-route comparison/complex shapes (the designed-conservative outcome). PARKED: weakest cost/benefit on the queue — a wave (approx. $2-4 pair + reviewer cycle) to shave seconds off a few hard cases. Revisit only if latency/waste complaints or a cheaper lever appears. | E-3c design record; runs #59-#61 diagnostics | 2026-07-21 | -| #022 | P2 | task | Source-governance metadata refresh (operator) | The selected policy is now encoded locally as auditable `third_party_reference_attested` evidence with policy version, reviewer qualification, evidence references and append-only review history. It deliberately preserves `clinical_validation_status=unverified`; malformed, stale or non-BMJ evidence remains review debt. Migration `20260727010000_bmj_third_party_source_attestation.sql` is prepared but was **not applied**. The ten most visible local-document candidates are captured in `docs/evidence/rag-top-local-review-manifest-2026-07-26.json` with `attestation_applied=false`; qualified human review, deliberate hosted apply/attestation, and warning-rate remeasurement remain operator work. | governance worklist; local policy/migration tests; top-ten evidence manifest | 2026-07-21 | -| #023 | P2 | task | Complete scheduled browser and labeling disposition | The 2026-07-26 retrieval and answer artifacts are read and compared under resolved #051. Scheduled CI run `30216361999` failed its existing production dependency audit before Firefox/WebKit, while production Chromium passed. After that audit is green, capture one scheduled/manual browser-matrix datapoint; separately record the human decision for the stable irrelevant-at-10 set. #084 now makes each top-10 grade and matched signal reproducible, but it does not substitute for the human disposition. Do not rerun or spend on RAG for this item. | runs `30216191889`/`30216361999`; per-rank diagnostics #084; session 2026-07-27 | 2026-07-21 | -| #024 | P2 | issue | WebKit e2e `_rsc` prefetch access-control errors | PR #1205 narrowed catch-all interception and duplicate navigation, but Next 16.2.11 still raises `_rsc` access-control `pageerror`s after document-source fallbacks: `/documents/source?id=&page=2&chunk=safety%20plan` → `/documents/?page=2&chunk=safety+plan`; `/documents/source/evidence?id=not-a-uuid&page=2` → `/documents/search`. The invalid-id failure survived removing every Playwright route; Chromium passed both. **Next:** on a provider-free macOS host, run both URLs in stable Safari and Safari Technology Preview without interception, capture console text plus `_rsc` status/access-control headers, and compare Playwright WebKit with routing on/off. Treat as an app defect only if native Safari reproduces; otherwise return to the harness. Never suppress `pageerror` or change CORS without native evidence. | PRs #1179/#1205; current-main local WebKit evidence; session 2026-07-28 | 2026-07-28 | -| #025 | P2 | task | Activate the three webhooks (operator secrets) | Merged (#968/#1100) + deployed but inert — verified live: `POST /api/webhooks/railway` returns `503 webhook_not_configured`; the Supabase document-change trigger exists but lacks both activation inputs. To turn on: (1) Railway ΓåÆ set `RAILWAY_WEBHOOK_SECRET` + add the `?token=…` webhook URL; (2) set `SLACK_WEBHOOK_URL`/`DISCORD_WEBHOOK_URL` in BOTH the Railway **app/server env** and **GitHub repo secrets**; (3) set one matching document-change secret in the Railway app env as `SUPABASE_INGESTION_WEBHOOK_SECRET` and in Supabase Vault as `ingestion_webhook_secret`, then set the per-environment database GUC `app.ingestion_webhook_base_url` to the deployed app origin. Each path fails closed until fully configured, so this is pure ops. See `docs/webhooks.md` for verification and rotation. | sessions 2026-07-22/24; PRs #968/#1100; docs/webhooks.md | 2026-07-22 | -| #027 | P3 | rec | External uptime monitor independent of GitHub/Railway | `live-domain-monitor.yml` runs on GitHub's cron, so it won't run in exactly the outage it should catch (Actions or the deploy itself down). Add an off-platform synthetic monitor (UptimeRobot / Better Stack / Checkly) hitting `/api/health` with a webhook alert. Provider setup, not code. | session 2026-07-22 webhook review | 2026-07-22 | -| #028 | P3 | rec | Runtime error tracking (Sentry or similar) | No error tracking in the repo — production exceptions on `psychiatry.tools`, including how often `RAG_PROVIDER_MODE=auto` silently degrades to source-only, are invisible. Weigh adding `@sentry/nextjs` (dependency + DSN secret + instrumentation) vs cost; alert ΓåÆ chat/issue. Provider-backed; needs explicit sign-off before adding the dependency. | session 2026-07-22 webhook review | 2026-07-22 | -| #033 | P3 | rec | Source governance metadata absent from the LLM prompt | `buildRagSourceBlock` omits `document_status`, `clinical_validation_status`, and `extraction_quality`, so the model cannot self-caveat during generation and governance is enforced only post-hoc. Generation-surface change: needs `eval:rag` plus `eval:quality --rag-only` (grounded-supported must not drop, citation-failure 0) and explicit approval. Carries the same "unknown Γëá bad" hazard as #032 — on a partially-enriched corpus the model would likely over-caveat correct sources, so design the prompt wording before spending an eval. | `src/lib/rag/rag-source-block.ts:126-198`; PR #1051 audit item 8 | 2026-07-22 | -| #035 | P3 | rec | Threshold-conflict detection covers only 3 params | `detectThresholdDisagreements` checks only ANC, WBC, and platelets paired with withholding verbs, so cross-source conflicts on medication doses, lithium/thyroid levels, or vital signs go undetected. Deliberately narrow (see the comment at `:469-474`). Broadening changes when an answer is classified `conflicting` and adds warnings — real false-positive risk. Needs new fixtures plus a behaviour review before any change. | `src/lib/evidence.ts:469-574`; PR #1051 audit item 7 | 2026-07-22 | -| #036 | P3 | rec | No explicit `is_public` visibility flag on documents | Public-corpus visibility is implicit: `owner_id IS NULL` on an `indexed` document (`resolveSearchScope`). The `metadata.public_corpus` marker is written by the promotion migrations but never used as a retrieval filter. Promotion is unconditional on `clinical_validation_status`, so unverified documents are publicly searchable — compensated by keeping `unverified_source` in the frontend-visible warning set. A hard schema flag touches RLS and the clinical-risk-gated retrieval RPCs; weigh against the existing compensating control before acting. | `supabase/schema.sql:61-108`; `src/lib/search-scope.ts:181-236`; PR #1051 audit item 3 | 2026-07-22 | -| #037 | P3 | rec | D5 trust-cap-all-claims flag parked OFF | `NEXT_PUBLIC_RAG_TRUST_CAP_ALL_CLAIMS` extends authority gating from high-risk claims to **all** supported claims (`deriveTrust`). Ships OFF by design; flipping it caps trust to `medium` for routine claims across the board — a product/clinical-UX decision, not a defect. Both states are test-pinned. Next action: product decision, then flip and re-baseline the UI expectations. | `src/lib/answer-render-policy.ts:159-177`; PR #1051 audit item 11 | 2026-07-22 | -| #038 | P3 | rec | Consolidate shared comparison behavior | Several clinical modes expose comparison workflows with similar selection, empty-state and mobile-dock needs. Define one shared behavioral contract before another comparison surface is added; keep mode-specific clinical content separate. This is a design-system recommendation, not a current defect. | design audit reconciliation; session 2026-07-22 | 2026-07-22 | -| #039 | P3 | rec | Consolidate catalogue toolbar patterns | Catalogue/search pages have independently evolved filter, sort, result-count and mobile toolbar behavior. Inventory the existing implementations and converge only the repeated interaction contract; do not flatten mode-specific search semantics. | design audit reconciliation; session 2026-07-22 | 2026-07-22 | -| #040 | P3 | rec | Add targeted visual-regression baselines | Keep a small approved baseline set for high-value desktop/mobile surfaces and accessibility modes instead of screenshotting every route. Start with account/settings, document viewer, mode homes and bottom-composer interactions; define an intentional-update workflow before enabling blocking comparisons. | design audit reconciliation; session 2026-07-22 | 2026-07-22 | -| #079 | P3 | task | Disposition retained worktrees in bounded cleanup batches | **Outcome:** the retained reconciliation tail is gradually classified without another disruptive all-worktree sweep. **Next:** process no more than ten worktrees per explicitly scheduled pass using current owner/process metadata, open-PR state, exact review-ledger coverage, ancestry, and cherry-pick-aware content proof. **Success:** remove only clean, inactive, bundled worktrees whose content is merged or explicitly rejected; record every disposition and retain recovery evidence. **Stop:** preserve dirty, active, secret-bearing, post-freeze, paused, or ambiguous work and never use reset, force deletion, broad clean, or process killing. | final reconciliation inventory retained 104 independent worktrees; session 2026-07-24 | 2026-07-24 | - +| ID | Pri | Type | Summary | Detail / next action | Source | Added | +| ---- | --- | ----- | --------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | ---------- | +| #059 | P1 | task | Verify containment of every credential reported exposed in chat | **Outcome:** every reported exposed credential is rejected or retired. **Next:** in approved security windows, verify and revoke or rotate the GitHub token, OpenAI key, Supabase service-role JWT, database password, and E2E credential; create replacements only when required and update only intended secret stores. **Success:** provider evidence confirms the old credentials cannot authenticate, replacements are distinct and minimally scoped, presence/readiness checks pass, and secret scans remain clean. **Stop:** no provider or secret-store action without approval; never print or paste values into Git, logs, issues, or chat. | session 2026-07-24 security reconciliation; AI Agent Target Manifest | 2026-07-24 | +| #065 | P2 | task | Complete the paused compact document source-text accordion | **Outcome:** the document viewer uses compact nested disclosures while retaining complete text, citation/search navigation, print behavior, and composer clearance. **Next:** only when the user explicitly resumes, reconcile `codex/chat-document-text-accordion-7cb4` with current `main` and complete the focused 320/390/1280 px tests. **Success:** default disclosures are closed; deep links and search open only the active passage; printing expands/restores state; no overflow. **Verify:** focused document-viewer Playwright, `verify:cheap`, `verify:ui`, and static production-readiness. **Stop:** remain paused until explicit user return; no provider calls. | paused document-viewer task; `codex/chat-document-text-accordion-7cb4` | 2026-07-24 | +| #001 | P2 | task | Semantic reranking still gated off | `RAG_SEMANTIC_RERANK_ENABLED=false` from PR #901. Do not enable until the provider-backed 36/36 retrieval-quality gate **and** an ambiguity-focused canary are explicitly approved and recorded. | `docs/process-hardening.md` (Semantic reranking rollout debt); PR #901 | 2026-07-21 | +| #053 | P1 | task | Execute cross-border privacy/legal package | Execute OpenAI and Railway DPAs; decide ZDR and Australian data residency; obtain prompt-cache behavior in writing; review subprocessors; obtain APP 8 and APP 5/1 counsel sign-off. Do not represent the release as privacy-approved or alter final public privacy wording before sign-off. | `docs/openai-cross-border-basis.md`; `docs/privacy-impact-assessment.md` | 2026-07-24 | +| #055 | P2 | task | Run one exact-SHA full release and PR gate | Before the next full-confidence release/handoff, record the candidate/PR SHA and run the local/provider release gates, Firefox/WebKit, required hosted CI, and actionable GitHub review-thread closure once. Stop at the first actionable failure and rerun only the repaired smallest gate. | `docs/launch-operator-runbook.md`; `docs/codex-review-protocol.md` | 2026-07-24 | +| #056 | P2 | task | Reconcile the existing staging migration history | `Clinical KB Staging` already exists as a healthy, empty Supabase/Railway tier with distinct secrets and no production clinical data, but it is 23 repository migrations behind. In the next approved staging schema window, apply the exact missing migration chain, then re-run indexing, health, identity and data-boundary proof. Do not recreate the environment or copy production clinical documents. | current-main staging verification; `docs/staging-setup.md`; `docs/operator-backlog.md` | 2026-07-27 | +| #057 | P2 | task | Complete staging soak and rollback rehearsal | After #056, run the documented soak and rollback against an exact candidate; retain latency/error/rollback evidence. Stop on unsafe data, identity mismatch, or an unowned rollback decision. | `docs/launch-operator-runbook.md`; `docs/capacity-review.md` | 2026-07-24 | +| #005 | P3 | rec | `finalScore` saturates at clamp ceiling | Base + ~40 stacked boosts routinely exceed 1.0, so strong matches tie at 1.0 and order by an arbitrary `document_id` tiebreak. If ranking is ever revisited, break ties by the **pre-clamp** score rather than raising the `[0,1]` ceiling (downstream gates assume `[0,1]`). Ordering already sorts by the unbounded pre-clamp `rankScore` (`clinical-search.ts:1735,1927,1950-1955`), so the clamp confines only the reported confidence value, not result order. Not a defect on the current golden set; any change here is a protected RAG surface (canary required). | `docs/rag-hybrid-findings-and-todo.md` P1 item 4; `src/lib/clinical-search.ts:1735` | 2026-07-21 | +| #011 | P3 | task | Auth DB-connection allocation is operator-only | Supabase Auth (GoTrue) is capped at ~10 absolute DB connections (Supabase perf advisor). Switch to **percentage-based** allocation in the Supabase **dashboard** before the first compute scale-up — **not settable via SQL/MCP** (operator-owned). Verify via a staging soak + an approval-gated read-only advisor re-check. | `docs/auth-connection-cap-runbook.md`; `docs/process-hardening.md` (Known follow-up debts) | 2026-07-21 | +| #013 | P3 | rec | Route-chunk + mockup catalogue JSON weight | `build:analyze`: `/specifiers` ships `specifiers-search-index.json` (~180 KB parsed), `/forms` ships `forms-catalog.json` (~132 KB), `/formulation` ships `formulation-content.json` (~52 KB, client-side local search — needs index/full split or a search endpoint, architectural). All route-scoped (not initial bundle). Also `*-mockups.tsx` (~100 KB across chunks) build though `/mockups` 404s in prod — exclude from the prod artifact. | session 2026-07-21 (build:analyze) | 2026-07-21 | +| #016 | P3 | rec | "Big but not easy" structural + motion perf | Deferred larger levers: (a) nonce-CSP forces every product route to `╞Æ Dynamic` (zero static generation) — evaluate Partial Prerendering / static shells for the static clinical catalogues (DSM/differentials/therapy/specifiers/formulation); (b) sidebar expand/collapse animates `grid-template-columns` (biggest smoothness cost, motion-gated — needs a transform-overlay rethink); (c) Therapy Compass fetches 692 KB / 2.5 MB JSON client-side (defer until interaction + confirm brotli); (d) settings/setup/admin dialogs static-imported into the home chunk (`next/dynamic` them). | session 2026-07-21 (build route table + design audit) | 2026-07-21 | +| #017 | P3 | task | Field Web-Vitals baseline via live Lighthouse | In-sandbox runtime vitals were blocked (prod server hard-requires Supabase secrets; dev-mode CLS measured excellent at 0.00–0.04, content-first pages 0.000). Run Lighthouse against `psychiatry.tools` for real LCP/INP/CLS to prioritize #012–#016 by measured impact rather than reasoning. | session 2026-07-21 (measurement pass) | 2026-07-21 | +| #018 | P2 | task | Split the lithium, ADHD and metabolic residuals by mechanism | Current evidence keeps the mechanisms separate. **Lithium — closed within this item:** the row/atom-aware subject guard, foreign-parameter rejection and query-specific range promotion returned `0.5–1.0 mmol/L` with correct targeting/citation; the full retrieval canary remained 36/36 with recall 1.0 and zero per-case RR regressions, and the full answer canary passed every blocking gate. **ADHD — open corpus debt:** `CG.MHSP.ADHD.pdf` is absent from the hosted corpus and the retrieved chart exposes `accessible_table_count=0`; repair corpus/fixture or ingestion evidence rather than weakening extractive budgets. **Metabolic — open structured-evidence debt:** the standalone plural classifier worsened the live answer and was reverted; obtain auditable schedule text/table evidence before another candidate. | targeted live lithium/ADHD/metabolic evidence 2026-07-27; `docs/evidence/rag-reliability-evidence-2026-07-27.md`; refuted approaches | 2026-07-21 | +| #021 | P3 | rec | E-3d H2 residual: strong/comparison generation discards | approx. 6 generation attempts per full 44-case run still fail the final quality gate and fall to extractive on strong-route comparison/complex shapes (the designed-conservative outcome). PARKED: weakest cost/benefit on the queue — a wave (approx. $2-4 pair + reviewer cycle) to shave seconds off a few hard cases. Revisit only if latency/waste complaints or a cheaper lever appears. | E-3c design record; runs #59-#61 diagnostics | 2026-07-21 | +| #022 | P2 | task | Source-governance metadata refresh (operator) | The selected policy is now encoded locally as auditable `third_party_reference_attested` evidence with policy version, reviewer qualification, evidence references and append-only review history. It deliberately preserves `clinical_validation_status=unverified`; malformed, stale or non-BMJ evidence remains review debt. Migration `20260727010000_bmj_third_party_source_attestation.sql` is prepared but was **not applied**. The ten most visible local-document candidates are captured in `docs/evidence/rag-top-local-review-manifest-2026-07-26.json` with `attestation_applied=false`; qualified human review, deliberate hosted apply/attestation, and warning-rate remeasurement remain operator work. | governance worklist; local policy/migration tests; top-ten evidence manifest | 2026-07-21 | +| #023 | P2 | task | Complete scheduled browser and labeling disposition | The 2026-07-26 retrieval and answer artifacts are read and compared under resolved #051. Scheduled CI run `30216361999` failed its existing production dependency audit before Firefox/WebKit, while production Chromium passed. After that audit is green, capture one scheduled/manual browser-matrix datapoint; separately record the human decision for the stable irrelevant-at-10 set. #084 now makes each top-10 grade and matched signal reproducible, but it does not substitute for the human disposition. Do not rerun or spend on RAG for this item. | runs `30216191889`/`30216361999`; per-rank diagnostics #084; session 2026-07-27 | 2026-07-21 | +| #024 | P2 | issue | WebKit e2e `_rsc` prefetch access-control errors | PR #1205 narrowed catch-all interception and duplicate navigation, but Next 16.2.11 still raises `_rsc` access-control `pageerror`s after document-source fallbacks: `/documents/source?id=&page=2&chunk=safety%20plan` → `/documents/?page=2&chunk=safety+plan`; `/documents/source/evidence?id=not-a-uuid&page=2` → `/documents/search`. The invalid-id failure survived removing every Playwright route; Chromium passed both. **Next:** on a provider-free macOS host, run both URLs in stable Safari and Safari Technology Preview without interception, capture console text plus `_rsc` status/access-control headers, and compare Playwright WebKit with routing on/off. Treat as an app defect only if native Safari reproduces; otherwise return to the harness. Never suppress `pageerror` or change CORS without native evidence. | PRs #1179/#1205; current-main local WebKit evidence; session 2026-07-28 | 2026-07-28 | +| #025 | P2 | task | Activate the three webhooks (operator secrets) | Merged (#968/#1100) + deployed but inert — verified live: `POST /api/webhooks/railway` returns `503 webhook_not_configured`; the Supabase document-change trigger exists but lacks both activation inputs. To turn on: (1) Railway ΓåÆ set `RAILWAY_WEBHOOK_SECRET` + add the `?token=…` webhook URL; (2) set `SLACK_WEBHOOK_URL`/`DISCORD_WEBHOOK_URL` in BOTH the Railway **app/server env** and **GitHub repo secrets**; (3) set one matching document-change secret in the Railway app env as `SUPABASE_INGESTION_WEBHOOK_SECRET` and in Supabase Vault as `ingestion_webhook_secret`, then set the per-environment database GUC `app.ingestion_webhook_base_url` to the deployed app origin. Each path fails closed until fully configured, so this is pure ops. See `docs/webhooks.md` for verification and rotation. | sessions 2026-07-22/24; PRs #968/#1100; docs/webhooks.md | 2026-07-22 | +| #027 | P3 | rec | External uptime monitor independent of GitHub/Railway | `live-domain-monitor.yml` runs on GitHub's cron, so it won't run in exactly the outage it should catch (Actions or the deploy itself down). Add an off-platform synthetic monitor (UptimeRobot / Better Stack / Checkly) hitting `/api/health` with a webhook alert. Provider setup, not code. | session 2026-07-22 webhook review | 2026-07-22 | +| #028 | P3 | rec | Runtime error tracking (Sentry or similar) | No error tracking in the repo — production exceptions on `psychiatry.tools`, including how often `RAG_PROVIDER_MODE=auto` silently degrades to source-only, are invisible. Weigh adding `@sentry/nextjs` (dependency + DSN secret + instrumentation) vs cost; alert ΓåÆ chat/issue. Provider-backed; needs explicit sign-off before adding the dependency. | session 2026-07-22 webhook review | 2026-07-22 | +| #033 | P3 | rec | Source governance metadata absent from the LLM prompt | `buildRagSourceBlock` omits `document_status`, `clinical_validation_status`, and `extraction_quality`, so the model cannot self-caveat during generation and governance is enforced only post-hoc. Generation-surface change: needs `eval:rag` plus `eval:quality --rag-only` (grounded-supported must not drop, citation-failure 0) and explicit approval. Carries the same "unknown Γëá bad" hazard as #032 — on a partially-enriched corpus the model would likely over-caveat correct sources, so design the prompt wording before spending an eval. | `src/lib/rag/rag-source-block.ts:126-198`; PR #1051 audit item 8 | 2026-07-22 | +| #035 | P3 | rec | Threshold-conflict detection covers only 3 params | `detectThresholdDisagreements` checks only ANC, WBC, and platelets paired with withholding verbs, so cross-source conflicts on medication doses, lithium/thyroid levels, or vital signs go undetected. Deliberately narrow (see the comment at `:469-474`). Broadening changes when an answer is classified `conflicting` and adds warnings — real false-positive risk. Needs new fixtures plus a behaviour review before any change. | `src/lib/evidence.ts:469-574`; PR #1051 audit item 7 | 2026-07-22 | +| #036 | P3 | rec | No explicit `is_public` visibility flag on documents | Public-corpus visibility is implicit: `owner_id IS NULL` on an `indexed` document (`resolveSearchScope`). The `metadata.public_corpus` marker is written by the promotion migrations but never used as a retrieval filter. Promotion is unconditional on `clinical_validation_status`, so unverified documents are publicly searchable — compensated by keeping `unverified_source` in the frontend-visible warning set. A hard schema flag touches RLS and the clinical-risk-gated retrieval RPCs; weigh against the existing compensating control before acting. | `supabase/schema.sql:61-108`; `src/lib/search-scope.ts:181-236`; PR #1051 audit item 3 | 2026-07-22 | +| #037 | P3 | rec | D5 trust-cap-all-claims flag parked OFF | `NEXT_PUBLIC_RAG_TRUST_CAP_ALL_CLAIMS` extends authority gating from high-risk claims to **all** supported claims (`deriveTrust`). Ships OFF by design; flipping it caps trust to `medium` for routine claims across the board — a product/clinical-UX decision, not a defect. Both states are test-pinned. Next action: product decision, then flip and re-baseline the UI expectations. | `src/lib/answer-render-policy.ts:159-177`; PR #1051 audit item 11 | 2026-07-22 | +| #038 | P3 | rec | Consolidate shared comparison behavior | Several clinical modes expose comparison workflows with similar selection, empty-state and mobile-dock needs. Define one shared behavioral contract before another comparison surface is added; keep mode-specific clinical content separate. This is a design-system recommendation, not a current defect. | design audit reconciliation; session 2026-07-22 | 2026-07-22 | +| #039 | P3 | rec | Consolidate catalogue toolbar patterns | Catalogue/search pages have independently evolved filter, sort, result-count and mobile toolbar behavior. Inventory the existing implementations and converge only the repeated interaction contract; do not flatten mode-specific search semantics. | design audit reconciliation; session 2026-07-22 | 2026-07-22 | +| #040 | P3 | rec | Add targeted visual-regression baselines | Keep a small approved baseline set for high-value desktop/mobile surfaces and accessibility modes instead of screenshotting every route. Start with account/settings, document viewer, mode homes and bottom-composer interactions; define an intentional-update workflow before enabling blocking comparisons. | design audit reconciliation; session 2026-07-22 | 2026-07-22 | +| #079 | P3 | task | Disposition retained worktrees in bounded cleanup batches | **Outcome:** the retained reconciliation tail is gradually classified without another disruptive all-worktree sweep. **Next:** process no more than ten worktrees per explicitly scheduled pass using current owner/process metadata, open-PR state, exact review-ledger coverage, ancestry, and cherry-pick-aware content proof. **Success:** remove only clean, inactive, bundled worktrees whose content is merged or explicitly rejected; record every disposition and retain recovery evidence. **Stop:** preserve dirty, active, secret-bearing, post-freeze, paused, or ambiguous work and never use reset, force deletion, broad clean, or process killing. | final reconciliation inventory retained 104 independent worktrees; session 2026-07-24 | 2026-07-24 | ## Resolved / archive Move resolved rows here with the resolution date and a one-line outcome. Keep them — do not delete. -| ID | Type | Summary | Outcome | Resolved | -| ---- | ----- | ---------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- | - -| #012 | rec | Slim the lazy cross-mode differentials chunk | Precomputed a trimmed index (`src/data/cross-mode-differentials-index.json` via `scripts/build-cross-mode-differentials-index.mjs`) so the lazily-loaded cross-mode chunk imports a ~53 KB catalog instead of statically pulling the ~1.2 MB differentials snapshot (only that dynamic path reached it). A drift test plus `check:cross-mode-index` (in verify:cheap) lock the index to the live projection. | 2026-07-27 | -| #029 | issue | Residual answer-quality fallback stubs | Closed after fixing each causal cluster independently. Active-community ED, community-home-visit, clozapine blood-threshold/typo, discharge source-gap recovery, and Best Practice Prescription now use narrowly validated, source-bound answers or auditable recovery; cited provider refusal prose can no longer masquerade as grounded, and terminal gaps retain no claim citations. The final 44-case gate reported 30/30 substantive grounded supported answers, 14/14 unsupported correct, zero review fallbacks, zero citation/numeric failures, and zero route-ceiling failures. Measurement still reports review fallback separately and denies targeting credit for echoed boilerplate. | 2026-07-27 | -| #019 | issue | Preserve admission/discharge sources through comparison fallback | The actual fallback path now selects source-bound facts, preserves one admission and one discharge citation from distinct documents, and terminates at an evidence gap for qualified, negated, unrelated, title-only, single-sided, or same-document traps. Both exact live cases complete in about one second with zero provider calls; the final 44-case canary passed them with two citations each, while the 36-case retrieval canary held recall 1.0 and zero RR regressions. Retrieval scores, aliases, clamps, and comparator ordering were unchanged. | 2026-07-27 | -| #084 | task | Persist per-result irrelevant-at-10 grading evidence | `eval-retrieval` now persists each top result's `relevanceGrade` and `matchedDeclaredSignals`; focused fixtures cover ideal and zero-grade rows. The final golden artifact contains 338 graded top rows, including 33 grade-zero rows. This closes the reproducibility gap only: fixture labels, ranking, thresholds, and provider behavior were not changed, and human disposition remains #023. | 2026-07-27 | -| #080 | rec | Re-test the removed admission-to-discharge alias widening | Restored the two approved NMHS Admission-to-Discharge titles only on the eval-expectation surface. Canonical document-identity dedupe plus maximum bipartite matching prevents one dual-listed physical document from satisfying both comparison slots. Focused matching tests, both targeted admission cases, the final 36-case golden retrieval run, and the 44-case answer run passed; runtime retrieval/ranking behavior was not changed. | 2026-07-27 | -| #083 | issue | Documents-only universal search timed out on staging tenancy | A current staging nightly reproducer showed the documents-only search losing its synthetic fixture after the federated typeahead timeout was reduced to 750 ms. Current main retains 750 ms for multi-domain requests and uses the established 6,000 ms budget only when documents are the sole requested domain; fake-timer coverage proves both paths. RAG impact: no retrieval, ranking, ordering, alias, score, or result-selection change—only availability of the explicitly focused request. | 2026-07-27 | -| #082 | issue | Bot branch-sync heads leave required checks unapproved | Retired the automatic `GITHUB_TOKEN` PR branch-update workflow instead of weakening required-check approvals or introducing a privileged automation token. The existing helper remains dry-run by default, verifies its apply identity, and refuses missing or bot identities. The fast GitHub Actions policy check rejects both direct workflow `update-branch` calls and indirect apply-helper invocation. | 2026-07-27 | - -| #058 | task | Verify production content before any seed write | Read-only production counts on project `sjrfecxgysukkwxsowpy` found 276 clinical registry, 328 medication, and 232 differential records. The required tables are non-empty, so no seed or production write was needed. | 2026-07-27 | -| #069 | task | Validate hosted table-facts RPC latency | Read-only profiling on the correct hosted project separated sample 1 (`first_unprimed`) from five `warm_repeat` samples; managed Supabase buffers were not flushed, so no true-cold claim is made. First-unprimed client/DB execution was 662.916/141.537 ms (clozapine), 277.661/96.229 ms (lithium), and 322.598/148.378 ms (metabolic). Warm client median/p90 was 187.029/198.907, 167.803/174.391, and 189.065/243.324 ms; warm DB execution median/p90 was 88.292/89.355, 64.342/65.566, and 107.448/148.417 ms. Earlier exact clinical probes were lower again. Plans are not the multi-second tail; no hosted migration, ranking, or provider configuration changed. | 2026-07-27 | -| #051 | task | Stabilise the live answer-quality canary before more RAG tuning | Closed after the scheduled structured report supplied a comparable second 36-retrieval/44-answer datapoint. Content gates stayed stable, the prior citation failure cleared, and #019 repeated with an identical diagnostic signature. Retrieval latency was investigated separately: #069 subsequently found acceptable table-facts database plans, so the broad scheduled tail was not treated as ranking debt. The report/trend tooling is now sufficient to compare future approved runs; no scheduled rerun or tuning was dispatched. | 2026-07-27 | -| #054 | task | Reconcile local and hosted secrets/config | Completed production names-only reconciliation on 2026-07-27. The correctly identified primary checkout received distinct gitignored local safety/query-hash/deep-probe values. A hardened checker now pins GitHub to `BigSimmo/Database` and Railway to the live production project/environment plus `Database`/`worker`, catches multiline schema and `.env.example` drift, and verifies GitHub secrets/variables and per-service Railway contracts without emitting provider values. All required names passed; the Ops Digest workflow is active with a successful scheduled run; both Railway services have later successful deployments; Supabase names-only proof found the expected cron/Vault configuration. Value equality remains deliberately unobservable, staging stays #056, webhook activation stays #025, and legal/ZDR work stays #053. | 2026-07-27 | -| #064 | task | Reconcile the preserved browser and contrast patch | Landed via PR #1250 squash `b91b4600171be08198e92bcf19b7d67e8207cb2f`. Opacity-free disabled Previous/Continue styling plus native-disabled/focus/axe Playwright coverage is on `main`. Historical `agent/formulation-disabled-contrast` remained unrecovered; conflicted PRs #1219/#1223/#1226/#1231/#1249 were closed without merge. | 2026-07-26 | -| #081 | issue | Open PR #1196 would undo the #030 alias tightening | Closed as no longer live: PR #1196 was closed 2026-07-25 as superseded by #913/current `main` (~680 commits behind, conflicting), and its successor #1198 does not touch `src/lib/eval-document-matching.ts`. The generalized alias-disjointness and single-document contracts landed in PR #1215 fail closed if any later branch re-adds the dual-listed admission aliases, so the regression route is guarded rather than watched. | 2026-07-25 | -| #077 | issue | Concurrent tasks can re-dirty the canonical primary checkout | Added cooperative primary-checkout write lease with dirty/operation fail-closed checks, stale-owner recovery, and lifecycle start/cleanup wiring; focused concurrency tests refuse a second primary writer while read-only/feature worktrees stay unblocked. | 2026-07-25 | -| #078 | task | Generate a deterministic reconciliation evidence pack | Added report-only atomic evidence pack with dispositions, markers, archive refs, bundle verify/hash, worktree counts, and local/base equality; fixture tests prove determinism/redaction and no false completion record on interrupt. | 2026-07-25 | -| #066 | task | Land and prove the streamlined six-item sidebar | Proven on `origin/main` via PR #1174 (`4dc76306 Land streamlined six-item sidebar`). Six-item rail shipped; open ledger row was stale post-merge. | 2026-07-25 | -| #067 | issue | Reconciliation preflight test times out under full-suite load | Fixed in PR #1191 (`e2488dbb`) by calling `collectReconciliationState()` in-process; PR #1203 further injects a fixture `repositoryRoot` so the contract no longer scales with the live worktree farm. No global timeout raise or heavy-test lock bypass. | 2026-07-25 | -| #007 | rec | `/tools` vs `/?mode=tools` parallel Tools entry points | Resolved as `/tools` canonical (PT-11 already documented on `/applications` redirect). Sidebar, appModeHomeHref, universal-search, prefetch, sitemap, and reachability now use `/tools`; `/?mode=tools` remains a dashboard-mode alias. Reachability allowlist entry removed. | 2026-07-24 | -| #030 | issue | Wide-tier alias lets one doc satisfy both comparison slots | Fixed on `cursor/search-correctness-030-075-6273`: removed dual-listed Admission-to-Discharge titles from AdmissionCommunityPts so one retrieved source cannot make allHit true for both comparison slots; fail-closed contracts in `tests/eval-document-matching.test.ts`. RAG impact: no retrieval behaviour change — eval matching only. Hardened after merge: coverage dedupes by document identity and assigns by maximum matching (#080). | 2026-07-24 | -| #075 | issue | Search-scope label enumeration can truncate after 1,000 rows | Fixed on `cursor/search-correctness-030-075-6273`: `loadScopeLabels` pages document_labels with deterministic order/batching past the Supabase 1k cap; multi-page >1000 contracts in `tests/search-scope.test.ts`. Isolated from mixed PR #1132. RAG impact: no retrieval behaviour change — label pagination only. | 2026-07-24 | -| #009 | rec | Confirm `/api/jobs` is intentionally server/ops-only | Kept as deliberate administrator/ops listing: no client `fetch("/api/jobs")` (UI uses `/api/ingestion/jobs`); documented in `docs/api-jobs-ops-surface.md` plus wiring/codebase-index/site-map notes. Not abandoned — do not remove without updating API contract tests. | 2026-07-24 | -| #010 | task | Un-built "Coming soon" controls across forms/favourites | Audited forms/favourites/presentation placeholders: all use honest `disabled` or `aria-disabled` + coming-soon copy (or presentational `ToggleSwitch` without `onToggle`). No fake-interactive controls; leave unwired until features land. Recorded in `docs/wiring-conventions.md`. | 2026-07-24 | -| #032 | rec | Governance ranking weighting: REFUTED, not debt | Reinforced as guardrail only in `docs/rag-behaviour/refuted-approaches.md` (Refutation 3), README, and safeguards — do **not** implement `review_due`/unknownCurrentness ranking penalties or boosts. No retrieval/ranking code changed. RC8 filter path remains the only revisit route behind canary gates. | 2026-07-24 | -| #041 | rec | Extend the existing Factsheets reading model | Brief recorded in `docs/factsheets-reading-model-brief.md`: extend Easy Read/Standard on existing Factsheets routes; reject a second patient-facing Factsheets mode unless concrete need + source-governance plan exist. | 2026-07-24 | -| #063 | rec | Define “Current Clinical Work” before implementation | Product/privacy/persistence brief recorded in `docs/current-clinical-work-brief.md`. Default v0 = no new storage (tab/URL resume); Class C free text needs privacy clearance. Stop without demand evidence. No UI/schema implemented. | 2026-07-24 | -| #076 | task | Reproduce malformed fallback PDF image/table crops | Reproduced truncated page-edge `table_crop`s on current-main with `worker/python/fixtures/malformed-table-crop-page-edge.pdf`. Root cause: `pymupdf_find_tables` stops at the last fully detected row; fix extends the candidate from contiguous cell drawings, recovers the on-page score-5 remnant, and emits `table_crop_edge_incomplete` / `crop_completeness=0.9` when content continues past the page. PR #1176. Broad PR #1129 retention/padding changes not merged. | 2026-07-24 | -| #070 | issue | Presentation mobile tabs misroute Overview/Map/Related | Fixed in PR #1135: Overview/Map/Related deep-link to diagnosis `?tab=` sections; Compare stays on the presentation page. Regression in `tests/mobile-interaction-regressions.test.ts`. (Provisional PR-branch IDs `#068`–`#072` were renumbered after `main` claimed `#068`/`#069`.) | 2026-07-24 | -| #071 | issue | Evidence/Clinical Notes Add fakes success without persistence | Fixed in PR #1135: sticky Add controls use the focusable coming-soon placeholder pattern instead of optimistic `setAdded(true)`. | 2026-07-24 | -| #072 | issue | Tools hub exposes false Sort/More affordances | Fixed in PR #1135: Sort is a status label, More filter targets coordination/saved without a fake menu chevron, and the favourites shortcut is labelled Saved/Favourites. | 2026-07-24 | -| #073 | issue | Presentation compare dock CTA is a self-link no-op | Fixed in PR #1135: dock shows non-link "Comparing (N)" status while already comparing. | 2026-07-24 | -| #074 | issue | Mode-action popup hard-reloads internal clinical routes | Fixed in PR #1135: `master-search-header` uses `router.push` for DSM/Specifiers/Formulation actions and mode href fallback. | 2026-07-24 | -| #068 | task | Regenerate full drift-manifest snapshot after schema hygiene | Full Docker `npm run drift:manifest` replay succeeded on a Docker-capable host; `supabase/drift-manifest.json` now carries live `def_hash` values for the plpgsql table-facts body (offline generator_note removed). | 2026-07-24 | -| #052 | issue | Reindex can overlap a fresh agent-enrichment pass | PR #1143 retained the friendly full/retry preflight and closed its check-then-enqueue race with an owner-scoped transactional RPC. Reindex enqueue and the agent claim path serialize on the document row; disposable PostgreSQL proved both interleavings, and exact-head migration replay/unit/build/Chromium/policy/security checks passed. | 2026-07-24 | -| #062 | issue | Upload crash can strand a queued document without a job | Aged owner-scoped `queued`-without-open-job rows are detected by `reindex:health`; the six-hour autopilot raises a durable alert, and guarded recovery uses PR #1143's transactional RPC so enqueue is owner-scoped, idempotent and atomic. `recover:ingestion --include-stranded-queued` remains dry-run/confirmation-first; scheduled production mutation is not enabled. | 2026-07-24 | -| #060 | issue | Safety Plan Generator contradicted the privacy contract | PR #1119 removed patient identifier entry, leaves the post-export name line blank, and aligned tool, privacy and PIA copy. DOM/privacy tests and Chromium copy/print/network coverage prove working content remains in React memory with no fetch/XHR; hosted Production UI, build, unit, policy, safety, static-analysis and secret checks passed. Support-contact details remain classified as sensitive local-only working content. | 2026-07-24 | -| #061 | issue | Missing answer relevance metadata was treated as source-backed | PR #1125 now requires explicit source-backed relevance for trusted/grounded presentation and prevents visual tables, clinical-note sections and quotes, and comparison metadata from bypassing the render model. Three actionable P2 review paths were fixed; focused policy/DOM tests, offline RAG, production-readiness, build, unit, static, security, and Production UI gates passed. No retrieval, ranking, generation, provider, or data behavior changed. | 2026-07-24 | -| #034 | issue | Answer cache can serve stale governance metadata | Current-source verification found direct route coverage already asserts RAG-cache invalidation on document PATCH, source review, label, bulk, and reindex mutation paths. The residual test recommendation is already met; changing the protected cache key is unnecessary. | 2026-07-24 | -| #014 | rec | Realize the `next/image` win on signed previews | Superseded: `SignedImage` uses `next/image` for layout and sizing but deliberately sets `unoptimized`, preventing bearer signed URLs from entering the unauthenticated optimizer cache where cached content could outlive the token. No optimization task remains unless private-image delivery changes. | 2026-07-24 | -| #026 | task | Wire the Supabase document-change trigger | PR #1100 merged after disposable PostgreSQL replay and hosted migration replay. Production migration history and read-only catalog proof confirm the enabled metadata trigger, security-definer function, pinned search path and denied anonymous/authenticated execution; `npm run check:drift` reports no unexpected live drift. Delivery remains intentionally inert until the operator inputs tracked in #025 are configured. | 2026-07-24 | -| #031 | issue | Populate canary Source Governance table | The answer-quality step now consumes the preceding `golden-retrieval.json` only for source-governance reporting. Offline replay of run `30018289898` populated 338 top results, including 202 review-required entries, while retaining zero retrieval cases and no additional threshold failures. Retrieval and ranking behavior are unchanged. | 2026-07-24 | -| #020 | task | Validate eval:quality cost readout post-fix | Confirmed on merged-main canary run `30018289898`: Answer Metrics reported 9 nonzero-cost cases and an estimated answer cost of `$0.234736`; the structured report retained the same value. The PR #1050 estimator fix is operationally proven. | 2026-07-23 | -| #003 | task | Staging tenancy release evidence outstanding | Ran GitHub Action and validated isolation | 2026-07-21 | -| #002 | task | Process-ownership fix not yet isolated on `main` | Fixed process isolation using child.pid termination | 2026-07-21 | -| #008 | rec | Dead href builders in `document-flow-routes.ts` | Not dead code (false positive): `documentReaderHref`/`documentEvidenceHref` are live via the mock wrappers in `src/components/document-search-mockups.tsx` + `src/components/master-document-flow-mockups.tsx` (rendered under `src/app/mockups/document-search/`) and covered by `tests/document-flow-routes.test.ts`; removing breaks the build. Only the production non-mock hrefs are unlinked from prod UI — a wiring gap, not dead code. | 2026-07-22 | -| #015 | task | Content-first fallback regression tests | Added `tests/registry-record-loader.dom.test.tsx` (8) + `tests/medication-record-page.dom.test.tsx` (6) covering content-first fallback paint, live swap-in, spinner/skeleton, error + not-found/unauthorized states, and the invariant that no authoritative verification badge shows before live governance reconciles (registry fixture-flag neutralization + medication governance-drop-on-error). | 2026-07-22 | -| #004 | rec | Rescope provider-gated RAG safety ideas | Closed obsolete — rescue source (754-line RAG-safety worktree) unrecoverable/pruned across all refs; answer-quality thresholds + deep-health already shipped on `main` (#585/#587); only cost-cap preflight was genuinely missing and, per session decision, dropped rather than re-filed. | 2026-07-22 | -| #006 | issue | Globe "Language & region" button had no handler | Resolved on main with the repository's disabled "Coming soon" placeholder convention and button-wiring coverage. Future language/region work remains a feature request, not an inert-control defect. | 2026-07-22 | -| #042 | issue | Invalid optional credentials fell into anonymous access | PRs #1078/#1079 introduced `absent \| valid \| invalid`, return 401 for presented invalid credentials, preserve authoritative header precedence and prefer the current-project session cookie. The archived anonymous-upload metadata patch was rejected as stale because uploads are already administrator-only before duplicate lookup. | 2026-07-22 | -| #043 | issue | Readiness could report healthy or throw on Supabase errors | PR #1080 now fails readiness closed for returned and thrown dependency failures, preserves recognized actionable messages, and prevents raw dependency-error disclosure. | 2026-07-22 | -| #044 | issue | Publication approval was not bound to immutable reviewed state | PR #1081 added a canonical reviewed-state digest, row locks, active-job rejection and a new forward migration with replay/schema/type/drift evidence. | 2026-07-22 | -| #045 | issue | Bulk reindex discarded partial-success results | PR #1084 reserves preflight conflicts for non-2xx responses; completed mixed batches return per-item success/failure/missing results, and the UI refreshes successful work. | 2026-07-22 | -| #046 | issue | DOCX extraction lacked explicit resource budgets | PR #1085 added pre-inflate declared-size checks and post-read fail-safes for artifact count, per-artifact bytes, aggregate media, Word XML and extracted UTF-8 text. | 2026-07-22 | -| #047 | issue | XLSX extraction could construct unbounded results | PR #1086 bounds worksheets, non-empty rows, rendered cells and UTF-8 output while preserving sparse-column rendering. | 2026-07-22 | -| #048 | issue | Account copy overstated sync/privacy and enabled unavailable SSO | PR #1087 now maps copy to actual favourites/preferences persistence, identifies browser-session recents, removes the contradictory "never shared" claim and clearly disables unavailable providers using the accessible placeholder contract. | 2026-07-22 | -| #049 | issue | Process diagnostic exposed a Cursor worker API key | The exact worker was stopped, the key was revoked server-side, both local encrypted worker-secret records were removed, and authorized repository/backup scans found no plaintext copy. Follow-up guardrails now prevent repository process inventory from serializing command lines and redact heavyweight-lock command text before persistence or errors. | 2026-07-23 | -| #050 | issue | Next.js 16.2.10 remained in a high-severity security range | Upgraded `next` and `@next/env` to 16.2.11, regenerated the npm lockfile, confirmed the production dependency audit is clean, and passed focused framework checks, `verify:cheap`, and the full Chromium UI gate. | 2026-07-23 | +| ID | Type | Summary | Outcome | Resolved | +| --- | ---- | ------- | ------- | -------- | + +| #012 | rec | Slim the lazy cross-mode differentials chunk | Precomputed a trimmed index (`src/data/cross-mode-differentials-index.json` via `scripts/build-cross-mode-differentials-index.mjs`) so the lazily-loaded cross-mode chunk imports a ~53 KB catalog instead of statically pulling the ~1.2 MB differentials snapshot (only that dynamic path reached it). A drift test plus `check:cross-mode-index` (in verify:cheap) lock the index to the live projection. | 2026-07-27 | +| #029 | issue | Residual answer-quality fallback stubs | Closed after fixing each causal cluster independently. Active-community ED, community-home-visit, clozapine blood-threshold/typo, discharge source-gap recovery, and Best Practice Prescription now use narrowly validated, source-bound answers or auditable recovery; cited provider refusal prose can no longer masquerade as grounded, and terminal gaps retain no claim citations. The final 44-case gate reported 30/30 substantive grounded supported answers, 14/14 unsupported correct, zero review fallbacks, zero citation/numeric failures, and zero route-ceiling failures. Measurement still reports review fallback separately and denies targeting credit for echoed boilerplate. | 2026-07-27 | +| #019 | issue | Preserve admission/discharge sources through comparison fallback | The actual fallback path now selects source-bound facts, preserves one admission and one discharge citation from distinct documents, and terminates at an evidence gap for qualified, negated, unrelated, title-only, single-sided, or same-document traps. Both exact live cases complete in about one second with zero provider calls; the final 44-case canary passed them with two citations each, while the 36-case retrieval canary held recall 1.0 and zero RR regressions. Retrieval scores, aliases, clamps, and comparator ordering were unchanged. | 2026-07-27 | +| #084 | task | Persist per-result irrelevant-at-10 grading evidence | `eval-retrieval` now persists each top result's `relevanceGrade` and `matchedDeclaredSignals`; focused fixtures cover ideal and zero-grade rows. The final golden artifact contains 338 graded top rows, including 33 grade-zero rows. This closes the reproducibility gap only: fixture labels, ranking, thresholds, and provider behavior were not changed, and human disposition remains #023. | 2026-07-27 | +| #080 | rec | Re-test the removed admission-to-discharge alias widening | Restored the two approved NMHS Admission-to-Discharge titles only on the eval-expectation surface. Canonical document-identity dedupe plus maximum bipartite matching prevents one dual-listed physical document from satisfying both comparison slots. Focused matching tests, both targeted admission cases, the final 36-case golden retrieval run, and the 44-case answer run passed; runtime retrieval/ranking behavior was not changed. | 2026-07-27 | +| #083 | issue | Documents-only universal search timed out on staging tenancy | A current staging nightly reproducer showed the documents-only search losing its synthetic fixture after the federated typeahead timeout was reduced to 750 ms. Current main retains 750 ms for multi-domain requests and uses the established 6,000 ms budget only when documents are the sole requested domain; fake-timer coverage proves both paths. RAG impact: no retrieval, ranking, ordering, alias, score, or result-selection change—only availability of the explicitly focused request. | 2026-07-27 | +| #082 | issue | Bot branch-sync heads leave required checks unapproved | Retired the automatic `GITHUB_TOKEN` PR branch-update workflow instead of weakening required-check approvals or introducing a privileged automation token. The existing helper remains dry-run by default, verifies its apply identity, and refuses missing or bot identities. The fast GitHub Actions policy check rejects both direct workflow `update-branch` calls and indirect apply-helper invocation. | 2026-07-27 | + +| #058 | task | Verify production content before any seed write | Read-only production counts on project `sjrfecxgysukkwxsowpy` found 276 clinical registry, 328 medication, and 232 differential records. The required tables are non-empty, so no seed or production write was needed. | 2026-07-27 | +| #069 | task | Validate hosted table-facts RPC latency | Read-only profiling on the correct hosted project separated sample 1 (`first_unprimed`) from five `warm_repeat` samples; managed Supabase buffers were not flushed, so no true-cold claim is made. First-unprimed client/DB execution was 662.916/141.537 ms (clozapine), 277.661/96.229 ms (lithium), and 322.598/148.378 ms (metabolic). Warm client median/p90 was 187.029/198.907, 167.803/174.391, and 189.065/243.324 ms; warm DB execution median/p90 was 88.292/89.355, 64.342/65.566, and 107.448/148.417 ms. Earlier exact clinical probes were lower again. Plans are not the multi-second tail; no hosted migration, ranking, or provider configuration changed. | 2026-07-27 | +| #051 | task | Stabilise the live answer-quality canary before more RAG tuning | Closed after the scheduled structured report supplied a comparable second 36-retrieval/44-answer datapoint. Content gates stayed stable, the prior citation failure cleared, and #019 repeated with an identical diagnostic signature. Retrieval latency was investigated separately: #069 subsequently found acceptable table-facts database plans, so the broad scheduled tail was not treated as ranking debt. The report/trend tooling is now sufficient to compare future approved runs; no scheduled rerun or tuning was dispatched. | 2026-07-27 | +| #054 | task | Reconcile local and hosted secrets/config | Completed production names-only reconciliation on 2026-07-27. The correctly identified primary checkout received distinct gitignored local safety/query-hash/deep-probe values. A hardened checker now pins GitHub to `BigSimmo/Database` and Railway to the live production project/environment plus `Database`/`worker`, catches multiline schema and `.env.example` drift, and verifies GitHub secrets/variables and per-service Railway contracts without emitting provider values. All required names passed; the Ops Digest workflow is active with a successful scheduled run; both Railway services have later successful deployments; Supabase names-only proof found the expected cron/Vault configuration. Value equality remains deliberately unobservable, staging stays #056, webhook activation stays #025, and legal/ZDR work stays #053. | 2026-07-27 | +| #064 | task | Reconcile the preserved browser and contrast patch | Landed via PR #1250 squash `b91b4600171be08198e92bcf19b7d67e8207cb2f`. Opacity-free disabled Previous/Continue styling plus native-disabled/focus/axe Playwright coverage is on `main`. Historical `agent/formulation-disabled-contrast` remained unrecovered; conflicted PRs #1219/#1223/#1226/#1231/#1249 were closed without merge. | 2026-07-26 | +| #081 | issue | Open PR #1196 would undo the #030 alias tightening | Closed as no longer live: PR #1196 was closed 2026-07-25 as superseded by #913/current `main` (~680 commits behind, conflicting), and its successor #1198 does not touch `src/lib/eval-document-matching.ts`. The generalized alias-disjointness and single-document contracts landed in PR #1215 fail closed if any later branch re-adds the dual-listed admission aliases, so the regression route is guarded rather than watched. | 2026-07-25 | +| #077 | issue | Concurrent tasks can re-dirty the canonical primary checkout | Added cooperative primary-checkout write lease with dirty/operation fail-closed checks, stale-owner recovery, and lifecycle start/cleanup wiring; focused concurrency tests refuse a second primary writer while read-only/feature worktrees stay unblocked. | 2026-07-25 | +| #078 | task | Generate a deterministic reconciliation evidence pack | Added report-only atomic evidence pack with dispositions, markers, archive refs, bundle verify/hash, worktree counts, and local/base equality; fixture tests prove determinism/redaction and no false completion record on interrupt. | 2026-07-25 | +| #066 | task | Land and prove the streamlined six-item sidebar | Proven on `origin/main` via PR #1174 (`4dc76306 Land streamlined six-item sidebar`). Six-item rail shipped; open ledger row was stale post-merge. | 2026-07-25 | +| #067 | issue | Reconciliation preflight test times out under full-suite load | Fixed in PR #1191 (`e2488dbb`) by calling `collectReconciliationState()` in-process; PR #1203 further injects a fixture `repositoryRoot` so the contract no longer scales with the live worktree farm. No global timeout raise or heavy-test lock bypass. | 2026-07-25 | +| #007 | rec | `/tools` vs `/?mode=tools` parallel Tools entry points | Resolved as `/tools` canonical (PT-11 already documented on `/applications` redirect). Sidebar, appModeHomeHref, universal-search, prefetch, sitemap, and reachability now use `/tools`; `/?mode=tools` remains a dashboard-mode alias. Reachability allowlist entry removed. | 2026-07-24 | +| #030 | issue | Wide-tier alias lets one doc satisfy both comparison slots | Fixed on `cursor/search-correctness-030-075-6273`: removed dual-listed Admission-to-Discharge titles from AdmissionCommunityPts so one retrieved source cannot make allHit true for both comparison slots; fail-closed contracts in `tests/eval-document-matching.test.ts`. RAG impact: no retrieval behaviour change — eval matching only. Hardened after merge: coverage dedupes by document identity and assigns by maximum matching (#080). | 2026-07-24 | +| #075 | issue | Search-scope label enumeration can truncate after 1,000 rows | Fixed on `cursor/search-correctness-030-075-6273`: `loadScopeLabels` pages document_labels with deterministic order/batching past the Supabase 1k cap; multi-page >1000 contracts in `tests/search-scope.test.ts`. Isolated from mixed PR #1132. RAG impact: no retrieval behaviour change — label pagination only. | 2026-07-24 | +| #009 | rec | Confirm `/api/jobs` is intentionally server/ops-only | Kept as deliberate administrator/ops listing: no client `fetch("/api/jobs")` (UI uses `/api/ingestion/jobs`); documented in `docs/api-jobs-ops-surface.md` plus wiring/codebase-index/site-map notes. Not abandoned — do not remove without updating API contract tests. | 2026-07-24 | +| #010 | task | Un-built "Coming soon" controls across forms/favourites | Audited forms/favourites/presentation placeholders: all use honest `disabled` or `aria-disabled` + coming-soon copy (or presentational `ToggleSwitch` without `onToggle`). No fake-interactive controls; leave unwired until features land. Recorded in `docs/wiring-conventions.md`. | 2026-07-24 | +| #032 | rec | Governance ranking weighting: REFUTED, not debt | Reinforced as guardrail only in `docs/rag-behaviour/refuted-approaches.md` (Refutation 3), README, and safeguards — do **not** implement `review_due`/unknownCurrentness ranking penalties or boosts. No retrieval/ranking code changed. RC8 filter path remains the only revisit route behind canary gates. | 2026-07-24 | +| #041 | rec | Extend the existing Factsheets reading model | Brief recorded in `docs/factsheets-reading-model-brief.md`: extend Easy Read/Standard on existing Factsheets routes; reject a second patient-facing Factsheets mode unless concrete need + source-governance plan exist. | 2026-07-24 | +| #063 | rec | Define “Current Clinical Work” before implementation | Product/privacy/persistence brief recorded in `docs/current-clinical-work-brief.md`. Default v0 = no new storage (tab/URL resume); Class C free text needs privacy clearance. Stop without demand evidence. No UI/schema implemented. | 2026-07-24 | +| #076 | task | Reproduce malformed fallback PDF image/table crops | Reproduced truncated page-edge `table_crop`s on current-main with `worker/python/fixtures/malformed-table-crop-page-edge.pdf`. Root cause: `pymupdf_find_tables` stops at the last fully detected row; fix extends the candidate from contiguous cell drawings, recovers the on-page score-5 remnant, and emits `table_crop_edge_incomplete` / `crop_completeness=0.9` when content continues past the page. PR #1176. Broad PR #1129 retention/padding changes not merged. | 2026-07-24 | +| #070 | issue | Presentation mobile tabs misroute Overview/Map/Related | Fixed in PR #1135: Overview/Map/Related deep-link to diagnosis `?tab=` sections; Compare stays on the presentation page. Regression in `tests/mobile-interaction-regressions.test.ts`. (Provisional PR-branch IDs `#068`–`#072` were renumbered after `main` claimed `#068`/`#069`.) | 2026-07-24 | +| #071 | issue | Evidence/Clinical Notes Add fakes success without persistence | Fixed in PR #1135: sticky Add controls use the focusable coming-soon placeholder pattern instead of optimistic `setAdded(true)`. | 2026-07-24 | +| #072 | issue | Tools hub exposes false Sort/More affordances | Fixed in PR #1135: Sort is a status label, More filter targets coordination/saved without a fake menu chevron, and the favourites shortcut is labelled Saved/Favourites. | 2026-07-24 | +| #073 | issue | Presentation compare dock CTA is a self-link no-op | Fixed in PR #1135: dock shows non-link "Comparing (N)" status while already comparing. | 2026-07-24 | +| #074 | issue | Mode-action popup hard-reloads internal clinical routes | Fixed in PR #1135: `master-search-header` uses `router.push` for DSM/Specifiers/Formulation actions and mode href fallback. | 2026-07-24 | +| #068 | task | Regenerate full drift-manifest snapshot after schema hygiene | Full Docker `npm run drift:manifest` replay succeeded on a Docker-capable host; `supabase/drift-manifest.json` now carries live `def_hash` values for the plpgsql table-facts body (offline generator_note removed). | 2026-07-24 | +| #052 | issue | Reindex can overlap a fresh agent-enrichment pass | PR #1143 retained the friendly full/retry preflight and closed its check-then-enqueue race with an owner-scoped transactional RPC. Reindex enqueue and the agent claim path serialize on the document row; disposable PostgreSQL proved both interleavings, and exact-head migration replay/unit/build/Chromium/policy/security checks passed. | 2026-07-24 | +| #062 | issue | Upload crash can strand a queued document without a job | Aged owner-scoped `queued`-without-open-job rows are detected by `reindex:health`; the six-hour autopilot raises a durable alert, and guarded recovery uses PR #1143's transactional RPC so enqueue is owner-scoped, idempotent and atomic. `recover:ingestion --include-stranded-queued` remains dry-run/confirmation-first; scheduled production mutation is not enabled. | 2026-07-24 | +| #060 | issue | Safety Plan Generator contradicted the privacy contract | PR #1119 removed patient identifier entry, leaves the post-export name line blank, and aligned tool, privacy and PIA copy. DOM/privacy tests and Chromium copy/print/network coverage prove working content remains in React memory with no fetch/XHR; hosted Production UI, build, unit, policy, safety, static-analysis and secret checks passed. Support-contact details remain classified as sensitive local-only working content. | 2026-07-24 | +| #061 | issue | Missing answer relevance metadata was treated as source-backed | PR #1125 now requires explicit source-backed relevance for trusted/grounded presentation and prevents visual tables, clinical-note sections and quotes, and comparison metadata from bypassing the render model. Three actionable P2 review paths were fixed; focused policy/DOM tests, offline RAG, production-readiness, build, unit, static, security, and Production UI gates passed. No retrieval, ranking, generation, provider, or data behavior changed. | 2026-07-24 | +| #034 | issue | Answer cache can serve stale governance metadata | Current-source verification found direct route coverage already asserts RAG-cache invalidation on document PATCH, source review, label, bulk, and reindex mutation paths. The residual test recommendation is already met; changing the protected cache key is unnecessary. | 2026-07-24 | +| #014 | rec | Realize the `next/image` win on signed previews | Superseded: `SignedImage` uses `next/image` for layout and sizing but deliberately sets `unoptimized`, preventing bearer signed URLs from entering the unauthenticated optimizer cache where cached content could outlive the token. No optimization task remains unless private-image delivery changes. | 2026-07-24 | +| #026 | task | Wire the Supabase document-change trigger | PR #1100 merged after disposable PostgreSQL replay and hosted migration replay. Production migration history and read-only catalog proof confirm the enabled metadata trigger, security-definer function, pinned search path and denied anonymous/authenticated execution; `npm run check:drift` reports no unexpected live drift. Delivery remains intentionally inert until the operator inputs tracked in #025 are configured. | 2026-07-24 | +| #031 | issue | Populate canary Source Governance table | The answer-quality step now consumes the preceding `golden-retrieval.json` only for source-governance reporting. Offline replay of run `30018289898` populated 338 top results, including 202 review-required entries, while retaining zero retrieval cases and no additional threshold failures. Retrieval and ranking behavior are unchanged. | 2026-07-24 | +| #020 | task | Validate eval:quality cost readout post-fix | Confirmed on merged-main canary run `30018289898`: Answer Metrics reported 9 nonzero-cost cases and an estimated answer cost of `$0.234736`; the structured report retained the same value. The PR #1050 estimator fix is operationally proven. | 2026-07-23 | +| #003 | task | Staging tenancy release evidence outstanding | Ran GitHub Action and validated isolation | 2026-07-21 | +| #002 | task | Process-ownership fix not yet isolated on `main` | Fixed process isolation using child.pid termination | 2026-07-21 | +| #008 | rec | Dead href builders in `document-flow-routes.ts` | Not dead code (false positive): `documentReaderHref`/`documentEvidenceHref` are live via the mock wrappers in `src/components/document-search-mockups.tsx` + `src/components/master-document-flow-mockups.tsx` (rendered under `src/app/mockups/document-search/`) and covered by `tests/document-flow-routes.test.ts`; removing breaks the build. Only the production non-mock hrefs are unlinked from prod UI — a wiring gap, not dead code. | 2026-07-22 | +| #015 | task | Content-first fallback regression tests | Added `tests/registry-record-loader.dom.test.tsx` (8) + `tests/medication-record-page.dom.test.tsx` (6) covering content-first fallback paint, live swap-in, spinner/skeleton, error + not-found/unauthorized states, and the invariant that no authoritative verification badge shows before live governance reconciles (registry fixture-flag neutralization + medication governance-drop-on-error). | 2026-07-22 | +| #004 | rec | Rescope provider-gated RAG safety ideas | Closed obsolete — rescue source (754-line RAG-safety worktree) unrecoverable/pruned across all refs; answer-quality thresholds + deep-health already shipped on `main` (#585/#587); only cost-cap preflight was genuinely missing and, per session decision, dropped rather than re-filed. | 2026-07-22 | +| #006 | issue | Globe "Language & region" button had no handler | Resolved on main with the repository's disabled "Coming soon" placeholder convention and button-wiring coverage. Future language/region work remains a feature request, not an inert-control defect. | 2026-07-22 | +| #042 | issue | Invalid optional credentials fell into anonymous access | PRs #1078/#1079 introduced `absent \| valid \| invalid`, return 401 for presented invalid credentials, preserve authoritative header precedence and prefer the current-project session cookie. The archived anonymous-upload metadata patch was rejected as stale because uploads are already administrator-only before duplicate lookup. | 2026-07-22 | +| #043 | issue | Readiness could report healthy or throw on Supabase errors | PR #1080 now fails readiness closed for returned and thrown dependency failures, preserves recognized actionable messages, and prevents raw dependency-error disclosure. | 2026-07-22 | +| #044 | issue | Publication approval was not bound to immutable reviewed state | PR #1081 added a canonical reviewed-state digest, row locks, active-job rejection and a new forward migration with replay/schema/type/drift evidence. | 2026-07-22 | +| #045 | issue | Bulk reindex discarded partial-success results | PR #1084 reserves preflight conflicts for non-2xx responses; completed mixed batches return per-item success/failure/missing results, and the UI refreshes successful work. | 2026-07-22 | +| #046 | issue | DOCX extraction lacked explicit resource budgets | PR #1085 added pre-inflate declared-size checks and post-read fail-safes for artifact count, per-artifact bytes, aggregate media, Word XML and extracted UTF-8 text. | 2026-07-22 | +| #047 | issue | XLSX extraction could construct unbounded results | PR #1086 bounds worksheets, non-empty rows, rendered cells and UTF-8 output while preserving sparse-column rendering. | 2026-07-22 | +| #048 | issue | Account copy overstated sync/privacy and enabled unavailable SSO | PR #1087 now maps copy to actual favourites/preferences persistence, identifies browser-session recents, removes the contradictory "never shared" claim and clearly disables unavailable providers using the accessible placeholder contract. | 2026-07-22 | +| #049 | issue | Process diagnostic exposed a Cursor worker API key | The exact worker was stopped, the key was revoked server-side, both local encrypted worker-secret records were removed, and authorized repository/backup scans found no plaintext copy. Follow-up guardrails now prevent repository process inventory from serializing command lines and redact heavyweight-lock command text before persistence or errors. | 2026-07-23 | +| #050 | issue | Next.js 16.2.10 remained in a high-severity security range | Upgraded `next` and `@next/env` to 16.2.11, regenerated the npm lockfile, confirmed the production dependency audit is clean, and passed focused framework checks, `verify:cheap`, and the full Chromium UI gate. | 2026-07-23 | From 78897c17ee2baa772377c364e55ee3fd03d5b767 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 28 Jul 2026 01:51:21 +0000 Subject: [PATCH 19/19] docs(ledger): record #1287 format+main-sync fix Co-authored-by: BigSimmo --- docs/branch-review-ledger.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/branch-review-ledger.md b/docs/branch-review-ledger.md index 044381220e..8ea909279d 100644 --- a/docs/branch-review-ledger.md +++ b/docs/branch-review-ledger.md @@ -1198,3 +1198,4 @@ This file is append-only. Never rewrite or delete an existing review record; app | 2026-07-28 | PR #1287 / `claude/site-audit-quick-wins-21v9gb` | `562bce2b3c90bf1790da9683077915cd3f8fdb17` | Main sync + conflict repair + Bugbot closeout | FIXED. Real CONFLICTING vs advanced main was `docs/outstanding-issues.md` only (ledger/ui-smoke auto-merged). Resolution keeps `#012` in Resolved with this PR's outcome while retaining main's newer open/archive rows. CI on prior tip was fully green (Static/Production UI/PR required); re-runs after sync. Review threads already dispositioned (resolved-graph guard, ledger residuals, attribution). | merge-tree CLEAN; focused vitest index+boundaries 10/10; `check:cross-mode-index` PASS; `prettier --check` on touched tests PASS; Bugbot pass pending agent; no provider-backed checks. | | 2026-07-28 | PR #1287 / `claude/site-audit-quick-wins-21v9gb` | `742b0d154f7058800c945b3ec6e720eef24ce4c0` | Bugbot P2: finish #012 recommended-queue closeout | FIXED. After main-sync conflict repair, `#012` was correctly Resolved/Open-clean but the Recommended execution queue still listed it (order 20 composite + #017 Before hint). Applied `/issues done` queue rewrite: order 20 is now `#013`, `#016`; #017 timing is Before `#013`/`#016`. | Bugbot review on `1b31607b`; queue/Open/Resolved audit; focused vitest previously green; no provider-backed checks. | | 2026-07-28 | PR #1292 / `codex/chat-clinical-grounding-cap-bbc4` | ff40a2b945caf657b45cec0d662317057c63abe3 | CI babysit + main sync + Bugbot | GitHub DIRTY/CONFLICTING with clean `merge-tree` (2 behind main). Merged `origin/main` with no content conflicts. CI already green on prior tip; no product test failures. Bugbot: reviewThreads=0; product fail-closed scan clean. No comments to resolve. | Local overflow Vitest 1/1; ledger guard pass; awaiting exact-head hosted CI. No providers. | +| 2026-07-28 | PR #1287 / `claude/site-audit-quick-wins-21v9gb` | `b90d659be12efedd339297daa2d289c2bd7ebb03` | Sync main + Format check on outstanding-issues | FIXED. Cause of GitHub CONFLICTING/DIRTY: branch 1 behind main (`11a4ed74` numeric claim truncation); `git merge-tree` CLEAN — ledger union auto-merge. Cause of Static PR red: Prettier on `docs/outstanding-issues.md` after queue closeout rewrite. Merged main; reformatted file; `#012` remains Resolved and out of the recommended queue. | merge-tree CLEAN; `prettier --check` PASS; `check:branch-review-ledger` PASS; no provider-backed checks. |