Skip to content

Age medication source status on the read path and make unknown visible - #2536

Merged
BigSimmo merged 13 commits into
mainfrom
claude/medication-status
Sep 3, 2026
Merged

Age medication source status on the read path and make unknown visible#2536
BigSimmo merged 13 commits into
mainfrom
claude/medication-status

Conversation

@BigSimmo

@BigSimmo BigSimmo commented Sep 2, 2026

Copy link
Copy Markdown
Owner

Summary

  • Source freshness never aged on the live path. It was computed once by recordToRow at insert time, frozen into the source_status column, and read straight back by rowGovernance. A row written while its sources were fresh claimed current indefinitely. Only the snapshot/demo path re-derived per request, so ageing worked in the one environment that has no patients and never worked against the database. rowGovernance now re-derives from the row's own sections at read time.
  • A future date read as freshly checked forever. It produced a large negative age that passed the interval test, so a "2126" typo would never age out. It now degrades to unknown beyond one day of timezone slack.
  • A block with several dates reported the first match; it now reports the oldest, so one recently re-checked line cannot vouch for every other source beside it.
  • unknown was silent. Only review_due and outdated were badged, so a record whose source date could not be read looked identical to one checked last month. Three records (alimemazine, edoxaban, levomepromazine) carry no src section at all and sat in exactly that state.
  • Age is deliberately not used to derive outdated. Supersession is a recorded clinical judgement that age can neither establish nor refute, so that branch stays dormant and a stored outdated survives re-derivation — it can only ever over-warn.

No migration. supabase/** is untouched and the applied source_status CHECK constraint is unchanged.

Governance review changed the design before push

A clinical governance review measured a regression the first implementation would have shipped, and it was not in the status logic. Badging every status meant an always-present neutral chip, and the detail-page hero renders its cluster at limit={5} while all 330 records already produce ≥5 badges. So the new chip had to evict one: 201 of 330 records lost a badge — the Poisons Schedule chip on 122 and the TGA-approved-indication tag on 77 — and the overflow +N chip is non-interactive, so they were unreachable. A chip showing an identical string on 327 of 330 records was displacing prescribing-relevant information.

The fix is to badge nothing when sources are current. That keeps the entire safety goal — an unreadable source date no longer looks like one checked last month — at zero informational cost. Post-fix eviction is 0 for every record that derives current, asserted both over the badge arrays and by rendering the real BadgeCluster and diffing visible chips. The three records with no sources do spend a slot, on a warning, which is the fix working rather than residue.

Two further review findings are fixed here: the unknown label is split so a record with no src section reads "No sources recorded" rather than the weaker "Source date unknown"; and the list endpoints no longer Zod-parse the same sections twice per row (~9 ms of synchronous event-loop time per owner list request at the 500-record cap).

docs/clinical-badge-system-guide.md specified "Current source → Success" in two places. Both are corrected — a green chip would be exactly the content-correctness endorsement this change avoids, and leaving the table would have invited the next agent to reintroduce it.

Known and accepted

326 of 327 dated records carry a 2026-05 date, so the catalogue flips to "Source check due" on roughly one day in May 2027. The label is deliberately descriptive rather than alarming, and no logic hides or staggers it — a catalogue-wide flip is a signal to re-export the snapshot.

Verification

  • npm run test (full unit suite) — Test Files 948 passed (948) / Tests 12083 passed | 1 skipped (12084)
  • npm run typecheck — exit 0
  • npm run format (whole tree) — no files changed afterwards

Negative control. Restoring the always-on current badge and changing nothing else fails five assertions across two files: Test Files 2 failed (2) / Tests 5 failed | 33 passed (38), the DOM failure reading expected [ 'MELATONERGIC', 'Valdoxan', …(4) ] to include 'TGA' and the corpus test naming all 327 displaced slugs. The guard is real, not vacuous. Restored and re-verified green.

A second negative control covers the cache: reverting the UTC-day key makes its guard test fail (Tests 1 failed | 14 passed).

npm run test:focused refuses by design here ("Focused test selection is unsafe: test or configuration paths changed") and instructs the full suite, so the full suite is what ran.

Two failures seen on an earlier run of this branch (tests/clinical-hazard-controls.test.ts, tests/rag-plan-package-parity.test.ts) were a shallow-clone artefact — the container held 98 commits and both tests name commits absent from it. After git fetch --deepen=2000 the suite is green as quoted above, with no code change.

Verification not run: npm run check:production-readiness cannot pass in this repository. check:privacy-readiness:release blocks on six release-blocking legal/provider items (OpenAI ZDR, OpenAI and Railway DPAs, APP 8 cross-border basis, APP 1/APP 5 notice, PHI minimisation), and the readiness script additionally reports absent provider credentials in this offline container. The same command on unmodified main at 45a3dca, with no changes, produces identical output — repository state, not a consequence of this diff, and none of those items is touched here.

Verification not run: npm run verify:pr-local was not run; its risk-routed selection adds no failure class this diff can reach beyond the full unit suite already run above.

UI verification not run: npm run verify:ui was not run. The rendered change is badge presence and label text, now pinned by a DOM test that renders the real BadgeCluster at the hero's limit={5} and diffs visible chips including the overflow label — which is the assertion that was missing when the eviction regression went unnoticed.

Risk and rollout

  • Risk: medium — this changes a clinician-facing governance badge. The change only ever makes freshness state more visible (a record with no recorded sources now renders a warning where it previously rendered nothing) and never relabels an unchecked source as checked.
  • Rollback: revert this single commit. No schema, migration, or stored state changes; source status is derived at read time, so a revert restores previous behaviour immediately with no data repair.
  • Provider or production effects: None
  • RAG impact: none

Clinical Governance Preflight

  • Source-backed claims still require linked source verification before clinical use
  • No patient-identifiable document workflow was introduced or expanded without explicit governance approval
  • Supabase target remains Clinical KB Database (sjrfecxgysukkwxsowpy)
  • Service-role keys and private document access remain server-only
  • Demo/synthetic content remains clearly separated from real clinical sources
  • Source metadata, review status, and outdated/unknown-source behavior remain conservative
  • Deployment classification/TGA SaMD impact was checked when clinical decision-support behavior changed

Notes

Out of scope but worth recording: src/lib/registry-records.ts and src/lib/differential-records.ts return the frozen source_status column verbatim, exactly as this module did, and both feed clinician-facing governance on the services and differentials surfaces. differential-records.ts additionally hardcodes validation_status: "locally_reviewed" over a snapshot whose own governance.reviewStatus reads "Pending review" — and deriveTrust accepts locally_reviewed as satisfying the authority gate for high-risk claims. Same defect class, unfixed, deliberately left for its own change.

🤖 Generated with Claude Code

https://claude.ai/code/session_0183EiexuZe6uKqoACXGuANL


Generated by Claude Code


Note

Medium Risk
Changes clinician-facing medication governance badges and API-derived freshness; behaviour is conservative (more visible warnings, no “current” endorsement chips) but affects prescribing UI at scale.

Overview
Medication source freshness now ages on the live read path instead of trusting the frozen source_status column written at insert. rowGovernance / API payloads re-derive from each record’s src section (with stored outdated preserved), expose sourceCheckedAt and sourcesRecorded, and avoid double-parsing sections on list routes. Parsing is stricter: oldest date in a multi-citation block, invalid dates fail the whole block, and future check dates degrade to unknown.

Identity badges surface source problems only — no chip when sources are current, so the hero BadgeCluster (limit 5) does not evict schedule/TGA chips. Deficiencies get explicit warnings (Source check due…, Source date unknown, No sources recorded, Source superseded). Public governance memoization is keyed by UTC day so cached maps can flip when records age out.

docs/clinical-badge-system-guide.md now documents that a simply current source gets no badge. Tests cover derivation, route cache behaviour, badge logic, and visible chips via BadgeCluster.

Reviewed by Cursor Bugbot for commit 8de4df5. Configure here.

Source freshness was computed once, by `recordToRow` at insert time, frozen
into the `source_status` column, and read straight back by `rowGovernance`.
A row written while its sources were fresh therefore claimed `current`
indefinitely. Only the snapshot/demo path re-derived per request, so ageing
worked in the one environment that has no patients and never worked against
the live database.

`rowGovernance` now re-derives freshness from the row's own `sections` at read
time. The column stays (it is applied migration history and still what the
write path stores); it simply stops being the answer. A stored `outdated`
survives re-derivation, because supersession is a recorded clinical judgement
that age can neither establish nor refute.

Two further conservative corrections in the same derivation:

- A future date produced a large negative age, passed the interval test, and
  read as freshly checked forever — a "2126" typo would never age out. It now
  degrades to `unknown` beyond one day of timezone slack.
- A source block carrying several dates reported the first match. It now
  reports the OLDEST, so one recently re-checked line cannot vouch for every
  other source beside it.

`unknown` also stops being silent. Only `review_due` and `outdated` were
badged, so a record whose source date could not be read looked identical to
one checked last month; three snapshot records (edoxaban, alimemazine,
levomepromazine) carry no source section and sat in exactly that state. Those
two deficiencies are now told apart rather than merged: "No sources recorded"
where nothing was ever cited, "Source date unknown" where a date is present
but unreadable. Both stay warning tone — three records with no recorded
sources in a prescribing tool is a legitimate caution, not alarm fatigue.

`current`, by contrast, gets no badge at all. The freshness chip is a
DEFICIENCY signal, not a status readout. Every one of the 330 snapshot records
already produces at least five identity badges and the detail hero renders the
cluster at `limit={5}`, so an always-on chip cannot add a row — it evicts one.
Sorted by tone, a neutral "Source checked …" chip outranks the Poisons
Schedule and the TGA/OFF indication tag, both `info`: measured at a 2026-09-02
reference date it displaced a schedule chip on 122 records and a TGA tag on
77, 201 of 330 in total, while carrying text that was identical for 327 of
them. A record showing neither TGA nor OFF reads as unknown regulatory status
rather than approved, and the overflow "+N" chip is plain, non-interactive
text with no tooltip, so what it swallows is genuinely unreachable. Re-toning
does not help; the cluster is saturated either way. A healthy record's
last-checked date still reads as text in its own Sources section, which is
where a date belongs. Nothing asserted what survives that cluster before,
which is why this was invisible; a new DOM test
(`tests/medication-identity-badge-cluster.dom.test.tsx`) now pins it through
the real component.

The `review_due` wording moves with it: "sources last checked <month>" is
unambiguously a recency statement, where a bare "checked" reads as a claim
that someone validated the entry — the same conflation `isReviewed` above it
already had to be rescued from.

The list route also stops parsing every row twice. It mapped rows to records
and then derived governance straight from the rows, Zod-parsing the identical
`sections` payload a second time — about 6 ms per 330 rows on top of the 7 ms
the first pass costs, roughly 9 ms of synchronous event-loop time per owner
request at the `MEDICATION_MAX_RECORDS` cap. `rowGovernanceForRecord` derives
from the record already in hand; `rowGovernance` keeps its fail-closed
row-only behaviour for callers that hold nothing but a row.

Age is deliberately NOT used to derive `outdated`. That branch stays dormant
until a recorded-supersession flow exists.

The public governance map was cached for the process lifetime, which would
have re-frozen exactly what this change unfreezes; it is now keyed by UTC day,
which is the finest granularity the status can change at. That is the tightest
link, not the only one: anonymous responses go out `public, max-age=300,
s-maxage=3600, stale-while-revalidate=86400`, so a CDN may serve a day-old
governance map for about 25 h past the flip. Immaterial against a 365-day
interval, and now noted beside the day key so nobody over-trusts it.

`docs/clinical-badge-system-guide.md` loses its "current source → Success" row
in both tables and gains the reasoning, because that table is not gate-enforced
and the claim would otherwise be restored as a fix.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0183EiexuZe6uKqoACXGuANL
@supabase

supabase Bot commented Sep 2, 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 ↗︎.

@coderabbitai

coderabbitai Bot commented Sep 2, 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: 9f42dfc4-fb63-499f-8620-94abb36ac725


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.

@BigSimmo
BigSimmo marked this pull request as ready for review September 2, 2026 05:55
@cursor

cursor Bot commented Sep 2, 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_5d6a49e3-b186-48a8-bda6-5712678ae274)

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 2, 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-02T06:00:51.200901Z e24e0ee Draft marked ready
ℹ️ 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.

@cursor

cursor Bot commented Sep 2, 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_d85becd6-6219-4276-a501-7c324b314c3f)

@cursor

cursor Bot commented Sep 2, 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_5a30d839-2b24-488f-9301-fddda006e8de)

BigSimmo pushed a commit that referenced this pull request Sep 2, 2026
main's #2536 appended tests/bundle-budget-refresh-workflow.test.ts to
test:ci-workflows on the same line this branch adds
tests/branch-review-index.test.ts, so package.json conflicted there for
the second time. Same resolution as before, and for the same reason: both
suites load a committed workflow file, ci-cache-safety.test.ts requires
every such suite to be listed, so the union is correct and dropping
either would silently stop that suite running.

Verified rather than assumed: npx vitest run tests/ci-cache-safety.test.ts
tests/branch-review-index.test.ts tests/bundle-budget-refresh-workflow.test.ts
tests/browser-test-plan.test.ts -> 151 tests passed.

docs/scripts-index.md, docs/branch-review-index.md and
data/repo-awareness-snapshot.json regenerated with their own generators.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AxPVZhAvzE2YupMiDz8sqF
@cursor

cursor Bot commented Sep 2, 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_d954c126-12a2-4c2c-9c06-b8004a0d9efb)

@github-actions

github-actions Bot commented Sep 2, 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 #15160 (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.

@cursor

cursor Bot commented Sep 2, 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_1a63b18b-ae1f-4348-9447-342abcae1e71)

@BigSimmo
BigSimmo enabled auto-merge (squash) September 2, 2026 19:32
@cursor

cursor Bot commented Sep 2, 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_279a6064-a333-4dbe-90a3-33d90a6d9b92)

BigSimmo commented Sep 2, 2026

Copy link
Copy Markdown
Owner Author

Lighthouse budget is red, and it is not this PR's — but it is not a flake either

Correcting an earlier reading. This check failed on 30afc398, passed on a re-run of that same commit, and has now failed again on cda377fb. Two confirmed failures against one pass means the pass was the outlier. I am not spending a second re-run: one was already spent on this exact check on this PR, and by the repo's own rule a second failure is real.

The measurements

The gate takes three samples per cell and calls a regression only at 2/3. It reached that on mobile /documents/search both times:

Head LCP Baseline Over
30afc398 2748 ms 2282 ms +466 ms (+20.4%)
cda377fb 2753 ms 2282 ms +471 ms (+20.6%)

Tolerance is +20% and +100 ms. Five milliseconds apart across two independent runs is not noise. In the second run mobile / also breached once and was correctly classified transient first measurement; 1/3 samples breached — the harness distinguishes the two, and it called this one confirmed twice.

Why the diff cannot reach that page

This PR changes six source files, all medication ones: medications/[slug]/page.tsx, the two api/medications routes, use-medication-catalog.ts, medication-badges.ts, medication-records.ts.

src/app/(search-app)/documents/search/page.tsx imports exactly one symbol — Metadata from next. Nothing outside medication-named files calls /api/medications. The one apparent link, src/lib/document-summary-badges.ts, names medication-badges only in a comment saying it mirrors that file's shape, and it is reached from DocumentViewer / DocumentTagCloud / document-rail-panels, not from the search route.

What I think is actually happening, stated as a hypothesis

lighthouse-budget.json was last refreshed at b22639b5 on 2026-08-27. main has taken heavy change in the six days since, ~40 merges today alone. This branch has merged main in repeatedly today, so it carries all of that accumulated growth; #2531 and #2538 passed this same check earlier today against earlier, lighter main heads.

That is the failure #QSHHGK already records for the bundle budget — "Nothing schedules a baseline refresh, so accumulated growth fails whichever unrelated PR lands last."

I have not proven it. I did not measure main itself against the baseline, and the check is path-scoped so it seldom runs on main pushes. It is the best-supported explanation available, not a demonstrated cause.

No fix ported, and none exists to port. There is no open PR fixing this, so there is nothing to bring in. Refreshing the baseline inside this PR would clear the red by moving the goalposts on a failure nobody has explained; hunting the slowdown here would widen a clinical-wording change into a performance investigation. Both are the wrong shape for this PR.

Either /documents/search is genuinely ~0.5 s slower to paint on mobile than it was six days ago and someone should find out why, or the page has legitimately grown and the baseline is overdue. The gate as configured cannot tell those apart, and blames whichever PR is newest. That is an owner decision, and it is being recorded on #2544 rather than resolved here.


Generated by Claude Code

BigSimmo pushed a commit that referenced this pull request Sep 2, 2026
Two confirmed measurements on different heads of PR #2536 — +466ms (+20.4%)
and +471ms (+20.6%) against a +20%/+100ms tolerance, five milliseconds apart.
That is reproducible, not noise, and the passing re-run between them was the
outlier.

Ruled out as PR #2536's doing: its diff is six medication files, and
documents/search/page.tsx imports one symbol (Metadata from next). Nothing
outside medication-named files calls /api/medications.

Recorded as a hypothesis, not a finding: lighthouse-budget.json was last
refreshed 2026-08-27 and main has taken heavy change since, so the PR carrying
the newest main absorbs the blame. Same disease as #QSHHGK for the bundle
budget. Main itself was never measured, and the record says so.

Needs an owner decision — find the regression, or refresh the baseline on a
schedule rather than reactively to clear a red PR.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0183EiexuZe6uKqoACXGuANL
@cursor

cursor Bot commented Sep 2, 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_ad9704c1-67bb-4792-a607-c4ca607b0284)

@cursor

cursor Bot commented Sep 2, 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_8a301c7b-08a0-43d5-8d99-0bae34fe237a)

BigSimmo pushed a commit that referenced this pull request Sep 2, 2026
…violation

Observed once on PR #2536 (CI run 33690849576 attempt 1, Production UI shard 2)
and cleared by a single re-run of the same commit:

  strict mode violation: getByTestId("caring-contacts-guidance")
  resolved to 2 elements

The source renders that test id in exactly one place, and both the page and the
shell interpolate it once, so the duplicate is not a second render site. The
likely window is React relocating out-of-order streamed content from the page's
next/dynamic shell, which the test's waitUntil:"load" does not wait past — but
that is a hypothesis and the record says so; the retained trace should confirm
it before anyone edits the test.

Not PR #2536's: its diff is medication and documentation files only, and the
same shard passed on its previous head.

The record asks for the three unscoped locators to be scoped to the main
landmark, and explicitly rules out quarantining — one reproduction is below the
repository's three-on-the-same-SHA bar, and a locator fix is not a suppression.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0183EiexuZe6uKqoACXGuANL
@cursor

cursor Bot commented Sep 2, 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_96e8a3b4-492f-49a6-bb15-de561e14fc8a)

@BigSimmo
BigSimmo merged commit 0953822 into main Sep 3, 2026
33 checks passed
@BigSimmo
BigSimmo deleted the claude/medication-status branch September 3, 2026 00:03
BigSimmo added a commit that referenced this pull request Sep 3, 2026
…rward this time

⚠️ THE PREVIOUS PUSH WOULD HAVE BEEN A FORCE-UPDATE OVER AN ARMED AUTO-MERGE,
and the guard refused it with no override. It was right to. Rebuilt on the
current remote tip so this is a fast-forward: GitHub re-validates required
checks against the new head before it will merge, which an additive push cannot
slip past.

⚠️ AND THE FORCE-UPDATE WOULD HAVE DISCARDED NOTHING ONLY BY LUCK. An earlier
session had fixed the same Tier B refusal the other way, by restoring the route
as a compatibility redirect (`4473ebd9e`). That fix is already gone from the
remote tip — a later publication commit rebuilt the whole tree from the working
branch and silently overwrote it. Checked by tree rather than by commit message:
`git ls-tree` shows the route present at `4473ebd9e` and absent at the tip. So
this publication mechanism overwrites remote-side fixes wholesale, which is
worth knowing before anyone fixes anything on the branch itself again.

The competing resolutions were genuinely different, and the owner chose. The
redirect keeps an address that looked a MOVEMENT up by id while calling it a
patient; its inbound links passed movement ids through a variable named
`patient`, so forwarding them into the person screen would render the wrong
person's record. This branch retires the address instead, with the owner's
decision recorded in the register the gate now reads.

Diffed against the remote tip before pushing: 14 files, every one of them
tonight's work, no remote content dropped.

typecheck exit 0 over 6,770 files after merging main's #2540 and #2536.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
BigSimmo added a commit that referenced this pull request Sep 4, 2026
…r review (#2544)

* issues: queue seven verified follow-ups from PRs #2538, #2536 and #2531

Seven immutable inbox requests, no canonical ledger edit. Every claim was
checked against the code before it was written: three against origin/main,
four against the PR heads the follow-up belongs to (#2538 ef7c55a,
#2536 e24e0ee, #2531 60f5e8e), since none of the three has merged yet.

- P2 issue: differential-records.ts asserts validation_status
  "locally_reviewed" from a literal over a snapshot that says
  "Pending review" — the sibling of the fix already in medication-records.ts.
- P2 issue: registry-records.ts and differential-records.ts both return the
  frozen source_status column verbatim, and derive it with a substring test
  that also matches "not checked"/"unchecked".
- P3 issue: a stored source_status of "outdated" can never be cleared, since
  nothing writes that column back on any of the three record tables.
- P3 rec: patient-alert rows with action "info" reach neither unassessed
  tier, leaving a green all-clear for nystatin, levetiracetam and lorazepam.
- P3 issue: isProfileEmpty treats a recorded hepatic "none" as no
  information, disagreeing with the engine, which treats it as an answer.
- P3 task: the considerations panel's two not-assessed sentences format
  their input lists differently.
- P3 rec: the forms PDF manifest records only passwordProtected, though the
  committed bytes also say modification, text extraction and assembly are
  blocked while printing and form-filling are permitted.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0183EiexuZe6uKqoACXGuANL

* issues: queue the recurring docs/scripts-index.md counts collision (#Y090R5 sibling)

The generated counts sentence at the top of docs/scripts-index.md conflicts on
every concurrent PR — measured five times on PR #2531 in about three hours, at
five different main heads, each time as the sole conflicting line.

Same root cause as #Y090R5 for data/outstanding-issues-snapshot.json: a
single-line generated artefact every PR must regenerate. PR #2530 fixed the
snapshot half by moving regeneration into the serialised reconcile step; this
half is untouched.

Records the two traps found while resolving it: taking main's whole file
silently drops the branch's own new script entry, and a bundled
verify:cheap:internal union merge desynchronises the gate counts that
check:gate-manifest reads.

Append-only inbox request; no canonical ledger edit.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0183EiexuZe6uKqoACXGuANL

* issues: cancel the OCR-routing claim and replace it with a corrected record

Codex review finding on this PR, verified against the code it cited rather than
taken on trust. The finding is correct.

Request e3133c1b asserted that disabled text extraction is "exactly the
condition that sends the worker down the OCR fallback path". It is not.
should_ocr_page() in worker/python/extract_pdf_assets.py (line 186) decides on
extracted text length and image coverage ratio only, and never reads the /P
permission bits. The extractor has no needs_pass or authenticate handling at
all, so a user-password PDF fails before any OCR decision is reached.

A wrong causal mechanism in a durable record is worse than no record: the next
person plans ingestion work from it. Cancelled rather than edited, because
inbox requests are immutable by design and the cancel action exists for exactly
this — the audit trail keeps the wrong claim, its refutation, and the
correction.

The permission-bits finding itself stands unchanged and is restated in the
replacement request, which makes no ingestion claim and says plainly that what
happens to these files on ingestion is untested.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0183EiexuZe6uKqoACXGuANL

* issues: record the reproducible mobile /documents/search LCP breach

Two confirmed measurements on different heads of PR #2536 — +466ms (+20.4%)
and +471ms (+20.6%) against a +20%/+100ms tolerance, five milliseconds apart.
That is reproducible, not noise, and the passing re-run between them was the
outlier.

Ruled out as PR #2536's doing: its diff is six medication files, and
documents/search/page.tsx imports one symbol (Metadata from next). Nothing
outside medication-named files calls /api/medications.

Recorded as a hypothesis, not a finding: lighthouse-budget.json was last
refreshed 2026-08-27 and main has taken heavy change since, so the PR carrying
the newest main absorbs the blame. Same disease as #QSHHGK for the bundle
budget. Main itself was never measured, and the record says so.

Needs an owner decision — find the regression, or refresh the baseline on a
schedule rather than reactively to clear a red PR.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0183EiexuZe6uKqoACXGuANL

* issues: cancel the Lighthouse regression claim and replace it with the measured variance

A third graded run refutes the "reproducible regression" reading in request
d1b3491f. CI run 33684986161 (head 957a038) measured mobile /documents/search
at 2323ms against the 2282ms baseline — +41ms — and reported "Every graded
route is within tolerance of the committed baseline."

The cell has now produced two breaches near 2750ms and at least two passes near
2320ms on the same pinned Chromium and the same route. That is a bimodal
measurement whose two modes straddle the +20%/+100ms tolerance, not a page that
became half a second slower. The gate's 2-of-3 sampling already tolerates noise
within a run; this split is between runs, which that design does not cover.

Cancelled rather than edited, because inbox requests are immutable and the
cancel action exists for this. The replacement states all four outcomes and asks
for the variance to be characterised — repeat the dispatch-only baseline-refresh
job against main and compare the spread — rather than for a regression hunt or a
reactive baseline refresh.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0183EiexuZe6uKqoACXGuANL

* issues: record the measured encryption state of the committed WA MHA form PDFs

A concurrent review comment on this PR asserted that PyMuPDF does not enforce
/P permission bits and that these files therefore "open normally and yield
their text layer". Measured against the committed bytes with PyMuPDF 1.28.0,
the library the worker actually uses, that is false for 50 of the 51 files.

  needs_pass=1, is_encrypted=True, page_count=0
  doc.authenticate("") -> 0        (the empty user password is rejected)
  load_page(0) -> ValueError('document closed or encrypted')

form-12a.pdf is the sole exception: no /Encrypt, opens, 3274 characters of
first-page text — which is exactly the file PR #2531 corrected.

The distinction the comment missed is that /P alone would not block opening;
these files also carry a non-empty /U, and that does. The practical
consequence is that extraction fails at the open call, before
should_ocr_page() is ever reached, so the OCR fallback cannot rescue them.
The JavaScript fallback was not measured and is not claimed either way.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0183EiexuZe6uKqoACXGuANL

* issues: record the intermittent caring-contacts-guidance strict-mode violation

Observed once on PR #2536 (CI run 33690849576 attempt 1, Production UI shard 2)
and cleared by a single re-run of the same commit:

  strict mode violation: getByTestId("caring-contacts-guidance")
  resolved to 2 elements

The source renders that test id in exactly one place, and both the page and the
shell interpolate it once, so the duplicate is not a second render site. The
likely window is React relocating out-of-order streamed content from the page's
next/dynamic shell, which the test's waitUntil:"load" does not wait past — but
that is a hypothesis and the record says so; the retained trace should confirm
it before anyone edits the test.

Not PR #2536's: its diff is medication and documentation files only, and the
same shard passed on its previous head.

The record asks for the three unscoped locators to be scoped to the main
landmark, and explicitly rules out quarantining — one reproduction is below the
repository's three-on-the-same-SHA bar, and a locator fix is not a suppression.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0183EiexuZe6uKqoACXGuANL

* docs(ledger): record Run PR sweep review for PR #2544

Merged origin/main into claude/issues-followups (clean, no conflicts)
and verified the narrow gates for this append-only inbox PR; both
review threads were already resolved from a prior pass.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0137etKe6X2p9HkXsXoYY8TA

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: BigSimmo <87357024+BigSimmo@users.noreply.github.com>
BigSimmo added a commit that referenced this pull request Sep 4, 2026
…cation (#2613)

* docs: queue outstanding-issue for recurring shared-shell testid duplication

Playwright strict-mode "resolved to 2 elements" failures on
service-actions-trigger (PR #2536) and sources-topics-main (PR #2591,
tests/ui-sources.spec.ts:88) share the same shape: one element nested
inside GlobalSearchShell's mobile-composer-reserve-pad wrapper, one
outside it. Confirmed unrelated to either PR's own diff. Queuing for a
focused investigation/fix rather than patching a "one owner" shared
shell component inside an unrelated PR.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015uRvqm44emrLDMyPF7Pame

* docs: log a third occurrence of the shared-shell testid duplication bug

Same strict-mode "resolved to 2 elements" shape seen on PR #2613's
queued issue, now also on caring-contacts-phone-dock (PR #2600).
Three unrelated PRs, three different testids — updates the priority
signal for the existing queued investigation without duplicating it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015uRvqm44emrLDMyPF7Pame

* docs: correct an unverified detail in the third-occurrence ledger entry

The earlier note claimed the duplicate caring-contacts-phone-dock
element was nested inside mobile-composer-reserve-pad by analogy with
the first two occurrences. The actual job log for this instance only
shows two sibling matches for the same testid; the nesting detail was
not verified and is removed. Cancels and replaces the prior entry.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015uRvqm44emrLDMyPF7Pame

---------

Co-authored-by: Claude <noreply@anthropic.com>
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.

2 participants