Skip to content

fix(medications): restore Dosulepin to the TCA class and repair the missed-member guard (#318) - #2015

Merged
BigSimmo merged 10 commits into
mainfrom
claude/p1-318-lexicon-slug-and-guards
Aug 17, 2026
Merged

fix(medications): restore Dosulepin to the TCA class and repair the missed-member guard (#318)#2015
BigSimmo merged 10 commits into
mainfrom
claude/p1-318-lexicon-slug-and-guards

Conversation

@BigSimmo

Copy link
Copy Markdown
Owner

Summary

Two mechanical fixes to the medication interaction lexicon. Neither changes a mapping that needed a clinical answer, and the sign-off block in docs/medication-interaction-lexicon-review.md is untouched — the sheet is still UNREVIEWED, which remains the only thing that closes #318.

  • Dosulepin fired none of the 20 severe TCA rows. The tcas selector listed slug dothiepin; the catalogue keys the drug as dosulepin — the same drug under its current INN. A search of data/medications-snapshot.json for "dothiepin" returns zero records, so the slug matched nothing and a TCA whose own record flags Toxicity in OD: FATAL and Anticholinergic: HIGH was absent from every one of that term's CRITICAL/HIGH rows.

    This restores the author's evident intent rather than making a new clinical determination: they wrote dothiepin, and the catalogue already files the drug as subclass: TCA with an overview reading "Tricyclic Antidepressant (also known as Dothiepin)". Only the spelling had drifted.

  • The guard that should have caught it could not run.missedClassMembers() in scripts/build-medication-lexicon-report.ts skipped any surface whose singular stem was shorter than four characters, which disabled the check entirely for tcas and arbs (ppis was rescued by its long surface proton pump inhibitors), and it read only class and subclass, never tag. The sheet's printed "Checks that ran and found nothing" line was therefore false for two terms. A printed clean result that could not have found anything is worse than no line at all, because it retires the question.

Measured clinical effect

After regenerating data/medication-interaction-index.json:

beforeafter
rows naming dosulepin as a counterparty0 via tcas22
of which CRITICAL/HIGH020
tcas resolves to5 drugs6 drugs
sheet flags12

Aggregate resolution is unchanged — 523 rows, 362 resolved, 161 unresolved, 423 with a catalogue target — because those rows already resolved through other TCAs. So this widens the counterparty set inside already-resolved rows rather than resolving new ones. The whole index diff is 30 insertions and 1 deletion, every one a dosulepin addition; the single deletion is prettier expanding a compact array to fit the insertion.

The sheet now also raises the Celecoxib/Parecoxib coxib gap itself, where previously it could not.

Two durable guards, so neither failure recurs

  • The coverage test now fails on any selector slug or denySlug that resolves to no catalogue record. The pre-existing test only required a term to resolve to some drug, so tcas stayed green on five of its six slugs — which is exactly how this shipped.
  • missedClassMembers() is now exported and directly tested, matching its sibling substringTraps. It was neither before, which is why its blind spots were invisible.

One thing I got wrong first, recorded because it matters

The acronym fix originally matched short stems as whole tokens (\btca\b). Mutation testing showed that branch did no protective work — the leading \b already stops arb reaching inside Carbapenem, the trap substringTraps exists for — while it would newly miss a subclass spelled TCAs, a regression in the dangerous direction. It is a plain prefix match with the floor lowered to 3, the shortest stem any real surface produces, pinned by a pluralised-subclass test.

Also queues one immutable inbox request (db498cc1) updating #318. It is cumulative over the still-pending 46750cbf from #1991, since update --detail replaces the cell rather than appending.

Verification

  • npm run verify:pr-local — completed 18 checks:
PR-local verification summary:
- completed: check:runtime, check:installed-lock-parity, format:changed, sitemap:check,
docs:check-index, docs:check-inventory, docs:check-scripts, docs:check-links,
check:branch-review-ledger, check:outstanding-issues, check:ledger-write-discipline,
lint, typecheck, test, build, check:rag:fixtures, check:medication-interactions,
check:medication-lexicon-report
- failed: (none)
- not reached: (none)
  • Focused suite tests/medication-interaction-lexicon-coverage.test.ts: 33 passed (was 30).
  • npm run check:production-readiness — run because this is clinical scope: 2 PASS, 5 WARN, 2 FAIL. Both FAILs are the documented offline provider-capability gap (absent NEXT_PUBLIC_SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY, OPENAI_API_KEY in this container), not regressions from this diff.
  • Mutation-verified four ways, so no new guard is decorative: reverting the slug fails 2 tests; restoring the < 4 stem floor fails 2; dropping tag from the haystack fails 1; anchoring the stem tail fails 1.

UI verification not run: no UI, routing, or styling changed. Independently, this container cannot run Chromium gates at all — Playwright ships chromium-1194 against a pinned 1234 (#255/#312), so check:playwright-browser-revision fails closed and verify:ui / verify:phone-chrome are unavailable. No browser coverage is implied.

No live Supabase or OpenAI call was made, and no retrieval or ranking surface was touched.

Risk and rollout

  • Risk: medium. This widens which drugs a CRITICAL/HIGH interaction phrase resolves to, so it is a real change to alert behaviour and is treated as clinical-risk rather than a chore. It moves in the conservative direction: 20 severe alerts that previously could not fire for a Dosulepin patient now fire, and nothing was removed from any term. No interaction wording changed — the prose a clinician reads is still verbatim catalogue text, and only the drug set a phrase resolves to moved. The residual risk is that a row now reaching Dosulepin is clinically wrong for it; restoring the mapping does not review the 20 rows it switched on, and the worklist calls that out as still owed.
  • Rollback: git revert the fix commit, then re-run npm run medications:interactions and npm run medications:lexicon-report to regenerate both artefacts and commit them. The two commits are separately revertible while this PR is open; after squash merge, revert the relevant hunks of the squash commit instead.
  • Provider or production effects: None. All work was local file reads and generator runs; no Supabase, OpenAI, deployment, or environment change.

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 on the items that actually bite here. Source verification and wording: unchanged — the alert text is verbatim catalogue prose in both cases, and this PR only alters which catalogue drugs a phrase resolves to. Conservative failure behaviour: preserved and improved — unresolved rows still render grey rather than green, the #318 sheet still declares itself UNREVIEWED, and the repaired guard removes a printed "checks ran clean" line that was false for two terms, which was the least conservative thing in the file. SaMD impact: considered. This changes interaction-alert reach, which is decision-support behaviour, so it is not exempt; the change restores a mapping the catalogue metadata already asserted rather than introducing new clinical logic, and it adds no new clinical claim, so the existing classification in docs/samd-classification-medication-considerations.md is unaffected. Privacy, service-role keys, demo separation: not touched by any file in this diff.

Notes

  • #318 stays open. Five clinical questions remain, mappings deliberately unchanged: the coxibs excluded from nsaids (38 severe rows, now auto-flagged); Moclobemide excluded from maois (17 severe rows) — which the sheet structurally cannot surface, because Moclobemide's tag is also RIMA, and RIMA/MAOI are synonyms in pharmacology but unrelated as strings; Loperamide included in opioids (35 severe rows, false-alert direction); acei/arbs resolving to one drug each, which I verified is catalogue coverage rather than a narrow selector; and three antiplatelets inside anticoagulants while Aspirin is deliberately excluded on identical class metadata.
  • A classifier gap worth its own row:src/lib/medication-interaction-lexicon.ts on its own classifies clinicalRisk: false. Only the generated data/medication-interaction-index.json makes a lexicon PR clinical-risk. So a lexicon edit that changes which drugs a CRITICAL phrase resolves to would skip this preflight entirely if the index were not regenerated in the same PR.
  • Run npm run issues:reconcile on its own serialized branch after this lands. There are now pending requests from docs(medications): clinician reading worklist for the lexicon review (#318) #1991, fix(audit): repair the merge-loss reconciliation exemption and classify how each loss happened (#324) #2014 and this PR.

🤖 Generated with Claude Code

https://claude.ai/code/session_01PZL59KVSBWURMKvfc9mbJr


Generated by Claude Code

…issed-member guard (#318)
Two mechanical fixes. Neither changes a mapping that needed a clinical answer,
and the sign-off block in docs/medication-interaction-lexicon-review.md is
untouched — the sheet is still UNREVIEWED, which is the only thing that closes
`#318`.
**Dosulepin fired none of the 20 severe TCA rows.** The `tcas` selector listed
slug `dothiepin`; the catalogue keys the drug as `dosulepin` — the same drug
under its current INN. A search of data/medications-snapshot.json for
"dothiepin" returns zero records, so the slug matched nothing and a TCA whose
own record flags `Toxicity in OD: FATAL` was absent from every one of that
term's CRITICAL/HIGH rows.
This restores the author's evident intent rather than making a new clinical
determination: they wrote `dothiepin`, and the catalogue already files the drug
as `subclass: TCA`. Measured effect after regenerating the interaction index —
22 rows now name `dosulepin` as a counterparty, 20 of them CRITICAL/HIGH, up
from 0 via this term. Aggregate resolution is unchanged (523 rows, 362
resolved, 161 unresolved, 423 with a catalogue target) because those rows
already resolved through other TCAs, so this widens counterparties inside
already-resolved rows rather than resolving new ones.
Durable guard: the coverage test now fails on ANY selector slug or denySlug
that resolves to no catalogue record. The pre-existing test only required a
TERM to resolve to *some* drug, so `tcas` stayed green on five of its six
slugs — which is exactly how this shipped.
**The guard that should have caught it could not run.** `missedClassMembers()`
skipped any surface stem shorter than four characters, disabling the check
entirely for `tcas` and `arbs` (`ppis` was rescued by its long surface), and it
read only `class`/`subclass`, never `tag`. The sheet's printed "Checks that ran
and found nothing" line was therefore false for two terms. A printed clean
result that could not have found anything is worse than no line at all, because
it retires the question.
Both closed: the floor is 3, the shortest stem any real surface produces, and
the haystack includes `tag`. The sheet now raises the Celecoxib/Parecoxib coxib
gap itself — 2 flagged, up from 1.
Worth recording because the first attempt was wrong: the acronym fix originally
matched short stems as whole tokens. Mutation testing showed that branch did no
protective work — the leading `\b` already stops `arb` reaching inside
`Carbapenem` — while it would newly MISS a subclass spelled `TCAs`, a
regression in the dangerous direction. It is a plain prefix match, pinned by a
pluralised-subclass test.
Gate: `npm run verify:pr-local` completed 18 checks with "failed: (none)",
including lint, typecheck, the full unit suite, build, check:medication-interactions
and check:medication-lexicon-report. Focused suite 33 passed (was 30).
Mutation-verified four ways: reverting the slug fails 2 tests, restoring the
`< 4` floor fails 2, dropping `tag` from the haystack fails 1, and anchoring
the stem tail fails 1.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PZL59KVSBWURMKvfc9mbJr
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@supabase

supabaseBot commented Aug 17, 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

coderabbitaiBot commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in:49 minutes

Limit details: You’ve used all 1 included review currently available under your plan. You completed 95 included PR reviews in the past 7 days; at that activity level, included reviews refill at 1 review per hour.

Your organization has reached its usage spending cap. Adjust your spending cap in the billing tab.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 6630d82e-fdc2-4552-8bec-695f60d01913

📥 Commits

Reviewing files that changed from the base of the PR and between a75d226 and 122961f.

📒 Files selected for processing (9)
  • data/medication-interaction-index.json
  • docs/branch-review-records/691b5961fbbe31dbb74413feee9a1471c1303aad0af3e55e97664038a28937de.record.md
  • docs/medication-interaction-lexicon-review.md
  • docs/medication-lexicon-review-worklist.md
  • docs/outstanding-issues-inbox/370d002c-5a65-4c99-bf5b-4146c59d5dc8.json
  • docs/outstanding-issues-inbox/db498cc1-c516-4141-9837-15fc9ef30684.json
  • scripts/build-medication-lexicon-report.ts
  • src/lib/medication-interaction-lexicon.ts
  • tests/medication-interaction-lexicon-coverage.test.ts

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

@github-actions

github-actionsBot commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

CI triage

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

  • Static PR checksneeds investigation: inspect the failing step and uploaded diagnostics; rerun only after classifying the cause.
  • PR requiredneeds investigation: inspect the failing step and uploaded diagnostics; rerun only after classifying the cause.

Compared with main CI run #11311 (cancelled).

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

BigSimmoand others added 8 commits August 17, 2026 16:38
Two pending outstanding-issues update requests targeted #318:
46750cbf (2026-08-15, from PR #1991) and this PR's own db498cc1
(2026-08-17), which restates and extends the former with the fixes
landed here. docs:check-links' mutationConflicts guard correctly
refused to pick a winner between two pending mutations on the same
row, so queue the explicit cancellation the guard's own error message
asks for.
…uards' into claude/p1-318-lexicon-slug-and-guards
@BigSimmo
BigSimmo enabled auto-merge (squash) August 17, 2026 12:23
@BigSimmo
BigSimmo merged commit b5d2109 into mainAug 17, 2026
26 checks passed
@BigSimmo
BigSimmo deleted the claude/p1-318-lexicon-slug-and-guards branch August 17, 2026 12:31
Sign up for freeto 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

@BigSimmo@claude