Skip to content

Surface clinical answer failures the task ledger already records - #2498

Merged
BigSimmo merged 12 commits into
mainfrom
claude/dev-hub-clinical-answer-failures
Sep 1, 2026
Merged

Surface clinical answer failures the task ledger already records#2498
BigSimmo merged 12 commits into
mainfrom
claude/dev-hub-clinical-answer-failures

Conversation

@BigSimmo

@BigSimmo BigSimmo commented Sep 1, 2026

Copy link
Copy Markdown
Owner

Summary

  • Surface the clinical answer failures the task ledger already records. The ledger names which clinical questions the answer engine gets wrong; today that is three open items naming five questions between them, including antipsychotic metabolic monitoring returning a source-backed stub instead of a written answer. Those facts sit unread among eighty-one open items. A new panel at /mockups/development/clinical-answer-failures lists them, and a band on the hub links straight to it so the count is visible on every visit.
  • resolveClinicalAnswerFailures intersects two records the repository already maintains — open ledger items, and the eval case list that names each clinical question. It is derived rather than curated deliberately: a hand-kept list of ledger ids is exact the day it is written and wrong a month later, with nothing to catch the drift.

Verification

  • npm run verify:pr-local

Run in full on the pre-merge tree and green end to end: all fourteen static and documentation checks, lint, typecheck, then Test Files 936 passed | 3 skipped (939) / Tests 11737 passed | 75 skipped (11812), then npm run build exit 0 (Compiled successfully in 3.1min, Client bundle secret surface check passed), then check:rag:fixtures, check:medication-interactions and check:medication-lexicon-report, all exit 0.

Reported precisely: an earlier attempt of the same gate stopped at build with BUILD_REFUSED_DEV_SERVER, because a PsychSift dev server was still running from the browser check. That is a refusal rather than a failure; the server was stopped and the build then passed.

What has not been re-run since. origin/main moved after that gate (PR #2495 merged), and merging it in produced two content conflicts, in src/app/mockups/development/page.tsx and tests/developer-hub-page.dom.test.tsx — the expected consequence of this branch's base landing on main as a single squashed commit. On the merged tree I have run the seven affected test files (Tests 108 passed) and a Prettier check, not the whole gate, so CI is the first full verification of the merged tree. The conflict resolution discarded zero non-empty lines from main's side, asserted mechanically during the resolution rather than judged by eye, and git diff --stat origin/main HEAD -- src tests is 573 insertions and 0 deletions.

Three things were mutation-proven rather than assumed:

  • Reverting the whole-token matcher to a plain includes turns both the synthetic nesting test and the real-data property test red.
  • The real-data test could not fail before this branch fixed it. vi.resetModules() clears the module cache but not the mock registry, so it silently ran against a previous test's fixture; it now calls vi.doUnmock first, and the mutation above is what exposed it.
  • Every mutated file was restored and confirmed byte-identical by SHA-256.

UI verification not run: npm run verify:ui was not run because no shared UI foundation changed — this adds one page and one band, with no layout, chrome, composer or routing-shell change. The changed surfaces were proven directly instead: npm run ensure, /api/local-project-id confirmed as this project, and both /mockups/development and the new page rendered and inspected in a browser. The panel showed 3 open problems and 5 clinical questions with their real question text; the band rendered in warning colours beneath the existing blocking-work band.

  • npm run verify:release not run: not a release or handoff-confidence claim.
  • npm run eval:retrieval:quality not applicable: no retrieval, ranking, selection, chunking or scoring behaviour changed. See the RAG note below.
  • npm run check:production-readiness not run: no clinical workflow, privacy, environment, Supabase, source-governance or deployment behaviour changed. This page reads two committed files and renders text.

Risk and rollout

  • Risk: Low in mechanism, but the content is clinical-facing, so the failure that matters is not a crash — it is the panel asserting something untrue about a clinical question. Both directions are guarded. Case ids nest in this repository (discharge-documentation inside quality-discharge-documentation; patient-safety-plan inside quality-patient-safety-plan-documents), so a plain substring match reported five questions where three were real, putting two clinical questions on screen that nothing had reported as broken. Matching is therefore whole-token, and the reverse case — a short id genuinely named in its own right — is pinned by its own test, so the fix cannot be traded for a false negative.
  • Rollback: Revert the single feature commit. The panel is additive: a new route, a new lib module, one new panel entry, and one conditional band. No migration, data or configuration state is left behind.
  • Provider or production effects: None. Both inputs are files already committed to the repository (data/outstanding-issues-snapshot.json and the eval case list). No network call, no Supabase read, no write path.
  • RAG impact: none. src/lib/rag/rag-eval-cases.ts is imported read-only for case ids and question text; no file under src/lib/rag/** is modified by this branch (git diff --name-only origin/main HEAD matching src/lib/rag/ returns nothing), and classifyPullRequestFiles reports ragRanking: false. Declared explicitly rather than left implicit, because this repository asks for retrieval-surface contact to be flagged even when it is only a read.

Clinical Governance Preflight

classifyPullRequestFiles reports clinicalRisk: true for this diff.

  • Source-backed claims still require linked source verification before clinical use
    Unchanged. This page makes no clinical claim of its own: every line it shows is quoted from a committed ledger item or from the eval case list, each shown with its ledger id and case id so a reader can check it against the source record.
  • No patient-identifiable document workflow was introduced or expanded without explicit governance approval
    No patient data of any kind is read or rendered. The inputs are repository metadata about test questions and outstanding work.
  • Supabase target remains Clinical KB Database (sjrfecxgysukkwxsowpy)
    No environment or project configuration was touched, and this page performs no database access.
  • Service-role keys and private document access remain server-only
    No Supabase client of any kind is constructed by this change.
  • Demo/synthetic content remains clearly separated from real clinical sources
    Unchanged, and the page inherits the hub's standing "synthetic data only" warning. The questions it names are evaluation cases, labelled as such.
  • Source metadata, review status, and outdated/unknown-source behavior remain conservative
    This is where most of the care went. The panel is named for its evidence ("Open problems recorded against a named clinical question"), states above the list that a recorded problem which does not name a case is not shown, and renders an empty result in words saying that a quiet ledger is not a well answer engine. On a clinical surface the reassuring-looking empty state is the dangerous one, so both the caveat and the empty wording are pinned by tests rather than left to editorial goodwill.
  • Deployment classification/TGA SaMD impact was checked when clinical decision-support behavior changed
    No clinical decision-support behaviour changed. This is developer-facing reporting about test questions, on a page that 404s for everyone but a signed-in administrator.

Notes

Stacked on #2495 while that was open, and brought onto main by merge once it landed. The conflicts were the ordinary consequence of a squash merge: main carries the base work as one commit, so git cannot match it against this branch's originals.

data/repo-awareness-snapshot.json, docs/site-map.md and data/outstanding-issues-snapshot.json are generated. The conflict in the first was resolved by regenerating, never by hand, as this repository requires.

BigSimmo and others added 9 commits September 1, 2026 17:53
…rant timeout

Both shell out to a Node child process. Measured on this worktree: the child
finishes in ~7 s standalone and the tests take ~9 s and ~10 s, so the global
30 s budget looks generous in isolation. It is not under the full run — the node
suite is CPU-bound on two workers across 900+ files, and on 2026-09-01 the
parity test exceeded 30 000 ms inside `npm run test` and failed the gate for a
diff that touched nothing under `.design-sync/**`.

Deliberately not quarantined: it did not fail an assertion, and a wrong time
budget is a different defect from an unstable test. `tests/flake-ledger.json` is
untouched, and the repo's three-reproductions rule does not apply here.

Proven rather than assumed: setting the same option to 1 ms makes the test fail
with "Test timed out in 1ms", so the option is read rather than silently ignored.

Committed with SKIP_DOCS_SYNC_HOOK=1 because the pre-commit generator refuses a
partially staged tree; the sibling commit that follows runs it in full on the
final tree, which is what gets pushed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…acts

The strip shipped in Phase 1 with `demoMode`, `documentCount` and `email` all
`null`, rendering "environment unknown · document count unavailable · account
unknown". Phase 2's design deferred them to whichever phase owned each value's
data source and reserved any Supabase read from the hub for its own approval;
that approval was given on 2026-09-01.

`resolveHubEnvironmentFacts` gathers all three in one Supabase round trip. Two
of its rules are load-bearing, and each is pinned by a test proven to fail
without it:

- The count goes through the cookie-bound user client, so row-level security
  (`documents owner read`, `owner_id = auth.uid()`) scopes it to the caller's
  own documents. The service-role admin client bypasses RLS and would report
  every owner's total to whoever was signed in; a source assertion fails if that
  import ever appears.
- Every failure path returns `null`, never `0`, and an unauthenticated request
  skips the query rather than reporting the zero rows RLS would correctly hand
  it. Zero is a true answer here, so a failed read must not be able to
  impersonate an empty corpus.

Reading data makes the page an async Server Component, so the dom tests and the
shared in-page-nav route table now await their fixtures. `next build` classifies
`/mockups/development` as dynamic, which it already was via the layout's
`headers()` call, so nothing regressed there.

The Phase 2 handoff document is corrected here rather than left claiming the
strip is unwired.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…d rejects

Raised in review of #2495. The helper handled a returned `{ error }` but not a
rejected promise, and the installed client rejects rather than resolves when a
request is aborted or exhausts its network retries. An unhandled rejection there
fails the whole developer hub page instead of degrading one line of it — during
exactly the Supabase outage that makes the page worth opening, and contrary to
the module's own stated contract.

Both awaits are now inside one guard, not just the count: an auth read can fail
the same way. `demoMode` is resolved before either of them and survives both,
because it never depended on the network, so an outage cannot make the strip
claim it is unable to tell demo data from live.

Proven, not assumed: two tests whose mocked `select` and `getUser` reject both
fail without the guard and pass with it, and they assert the whole returned
shape rather than merely that nothing threw.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ady records

The task ledger already records which clinical questions the answer engine gets
wrong -- today, three open items naming five questions between them, including
antipsychotic metabolic monitoring returning a placeholder instead of an answer.
Those facts sit unread among eighty-one open items. This puts them on the hub.

`resolveClinicalAnswerFailures` intersects two records the repository already
maintains: open ledger items, and the eval case list that names each clinical
question. Derived rather than curated on purpose -- a hand-kept list of ledger
ids is exact the day it is written and wrong a month later, with nothing to
catch the drift.

Matching is whole-token, and that is the load-bearing part rather than a detail.
Case ids nest: `discharge-documentation` and `patient-safety-plan` are both real
cases AND substrings of other real ones, so a plain `includes` reported five
questions where three were real -- two clinical questions on screen that nothing
had reported as broken. Proven by mutation: reverting the matcher to `includes`
turns both the synthetic nesting test and the real-data property test red.

The panel names itself for its evidence ("Open problems recorded against a named
clinical question"), and states above the list that a recorded problem which
does not name a case is not shown. On a clinical surface the reassuring-looking
empty state is the dangerous one, so the empty branch says in words that a quiet
ledger is not a well answer engine. Both are pinned by tests.

A hub band links straight to the panel, in warning rather than danger colours:
these are recorded problems, not blocking work, and painting them the same red
as the blocking band would teach the reader to discount both.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…linical-answer-failures

# Conflicts:
#	data/repo-awareness-snapshot.json
…al-answer-failures

# Conflicts:
#	src/app/mockups/development/page.tsx
#	tests/developer-hub-page.dom.test.tsx
@coderabbitai

coderabbitai Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Team

Run ID: 9c3efdec-b1b4-4539-ba56-e1543e34e967


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@cursor

cursor Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_282c8ff9-4f8b-4d4e-a019-55b9cad8a1d9)

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 1, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-01T11:16:57.132375Z 10c79bd PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@supabase

supabase Bot commented Sep 1, 2026

Copy link
Copy Markdown

This pull request has been ignored for the connected project sjrfecxgysukkwxsowpy because there are no changes detected in supabase directory. You can change this behaviour in Project Integrations Settings ↗︎.


Preview Branches by Supabase.
Learn more about Supabase Branching ↗︎.

@BigSimmo
BigSimmo enabled auto-merge (squash) September 1, 2026 11:14

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 10c79bdd0f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/lib/developer-area/clinical-answer-failures.ts
BigSimmo and others added 3 commits September 1, 2026 19:24
…n that case

Raised in review of #2498, and confirmed against the real ledger text rather than
taken on trust. `#J8SJQ9` is about `quality-antipsychotic-metabolic-monitoring`
and names `quality-discharge-documentation` only as the CONTRAST -- the case that
"deliberately drops mustContainAny" because a source pointer is a legitimate
answer there. The panel listed it among the questions and counted it, stating on
a clinical surface the opposite of what the ledger says.

Two tempting fixes were tried against the real data and rejected:

- Matching only the `source` field fixes `#J8SJQ9` and HIDES both genuinely
  broken questions in `#S4R2W3`, which names them only in its detail prose. On
  this panel a false negative is worse than a loose one.
- Guessing intent from surrounding words is the fragile heuristic the panel
  exists to avoid; it would be wrong quietly, later.

So the assertion moves to the level the data supports. The item-level claim was
always sound -- these are recorded problems about clinical answers -- and the
named cases are now presented as references: the section reads "Questions this
item names", the count is "questions referenced" (`affectedQuestionCount` becomes
`referencedQuestionCount`), and a second caveat says in words that an item may
name a question as the contrast that is behaving correctly.

Tests: the reviewer's requested fixture reproduces the real `#J8SJQ9` detail and
pins that BOTH cases come back as references, with a comment saying a later
change may only narrow that via an explicit per-case association in the ledger,
never a heuristic. A DOM test pins the wording, and fails when the caveat is
removed -- proven by mutation, then restored byte-identical.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ailures' into claude/dev-hub-clinical-answer-failures
@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

CI triage

CI failed on this PR. Automated classification of the 2 failed job(s):

  • Lighthouse budgetnot baselined: this job did NOT run on the main comparison below (path-scoped skip), so that run says nothing about it either way. Treat the comparison as absent, not green, and inspect the failing step.
  • PR requiredneeds investigation: inspect the failing step and uploaded diagnostics; rerun only after classifying the cause.

Compared with main CI run #14683 (success). That run's conclusion is an aggregate and did not exercise Lighthouse budget.

Classification is evidence routing, not permission to ignore a failure. Exact quarantined Playwright identities remain governed by the flake ledger.

@BigSimmo
BigSimmo merged commit 33bfca8 into main Sep 1, 2026
55 of 57 checks passed
@BigSimmo
BigSimmo deleted the claude/dev-hub-clinical-answer-failures branch September 1, 2026 11:53
BigSimmo added a commit that referenced this pull request Sep 1, 2026
Resolve dirty/behind after #2498: keep main regenerated snapshots and deps;
overlay PR intentional clinical copy + token-divergence gate hardening.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant