Generate DREAM's drug data from a pinned SCRATCH snapshot - #5
Merged
Conversation
DREAM's 116 protocol records were hand-typed copies of doses also authored in SCRATCH. The two silently diverged and nothing detected it. SCRATCH is now the source of truth. drugMasterlist.ts becomes a thin module merging drugMasterlist.generated.ts (built from protocols.snapshot.json) with dreamOnlyProtocols.ts, which still holds every drug SCRATCH has not migrated yet. Drugs move from hand- maintained to generated one tranche at a time; today 6 of 96 are generated. Its public exports are unchanged, so all ~7 consumers needed no edits. The snapshot is committed and never fetched at runtime or build time. Doses must not change under a clinician mid-clinic; every change reaches DREAM through a deliberate, reviewable commit. `npm run protocols:sync` refreshes it and prints a dose-level diff for review. The two sides model protocols differently: SCRATCH carries spt, idt and challenge in one entry, while DREAM's UI selects skin tests and challenges separately. The generator splits accordingly, so Cefazolin still yields two records (iv, iv-challenge). Verified: all 116 records keep their exact positional order, checked at runtime against git HEAD rather than by parsing text. That matters because DREAM selects protocol variants by array index, so a single shift would silently re-point a saved clinical plan at a different dose. Regeneration is byte-identical; no fetch() exists in src/. No numeric dose changed. The 24 text differences are presentational and were reviewed individually. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
SCRATCH is served at https://scratch.yuson.au, now verified. The sync script's URL constant was a deliberate placeholder because the hostname was unknown — scratch.pages.dev belongs to MIT Scratch, since a Pages project only gets its requested *.pages.dev name if it is free. Also records the succession consequence in HANDOVER.md: SCRATCH and DREAM now share one personal domain. If yuson.au lapses, clinicians lose the reference handbook and the encounter-recording app simultaneously, and protocols:sync stops resolving. Moving to an institutional domain is one piece of work, not two. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
First sync against the published endpoint at scratch.yuson.au/api/protocols.json. The review gate reported exactly the expected change and nothing else: five drugs, diluent wording plus their version and last_reviewed bumps. No dose moved. Also fixes a real defect in verify-order.mjs. It compared against `HEAD:drugMasterlist.ts`, which stops being the hand-typed baseline the moment this change is committed — so on its second run it compared 116 records against an empty file and reported "116 of 116 positional mismatches, FAIL". That is a false alarm on the one check that guards saved clinical plans from being re-pointed at different doses, and a check that cries wolf is barely better than one that stays silent. It now defaults to origin/main, accepts a ref argument, and exits 2 with an explicit BROKEN CHECK message when the baseline yields implausibly few records, instead of reporting every position as a mismatch. Verified against the true baseline: 0 mismatches out of 116. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The ordering test compared DRUG_MASTERLIST against `git show HEAD:drugMasterlist.ts`. That baseline stops existing the moment this change is committed — and disappears permanently once it merges — so the test compared 116 records against an empty file and reported "116 of 116 positional mismatches". A false alarm on the check that guards saved clinical plans from being re-pointed at different doses. The expected order is now a committed fixture, expectedProtocolOrder.json, captured from origin/main at the cutover. It survives merging, needs no git at test time, and still fails loudly on a real reorder. Also updates three stale expectations that were correctly catching the diluent standardisation and the accompanying last_reviewed bump: Rocuronium, Cis-atracurium and Vecuronium now expect "0.9% sodium chloride", and lastReviewed expects 2026-08-20. Full suite: 766 passed, 92 files. Order check: 0 mismatches of 116. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This is the change that actually connects the two apps.
Why
DREAM's 116 protocol records were hand-typed copies of doses also authored in SCRATCH. The two silently diverged and nothing detected it. SCRATCH is now the source of truth.
How
drugMasterlist.tsbecomes a thin module merging:drugMasterlist.generated.ts— built fromprotocols.snapshot.json(6 drugs migrated so far)dreamOnlyProtocols.ts— the 110 records SCRATCH hasn't migrated yetDrugs move from hand-maintained to generated one tranche at a time. Public exports are unchanged, so all ~7 consumers needed no edits.
The snapshot is pinned and committed — never fetched. Doses must not change under a clinician mid-clinic.
npm run protocols:syncrefreshes it and prints a dose-level diff as the human review gate.Schema mismatch, handled
SCRATCH carries
spt,idtandchallengein one protocol entry. DREAM's UI selects skin tests and challenges separately —getDrugsByCategory()excludestestType: 'challenge'. So the generator splits: Cefazolin's single SCRATCH protocol yields two DREAM records,ivandiv-challenge.Verification
mainfetch()insrc/The order check was run at runtime, importing the real module and comparing against
git show HEAD:, not by regex over source text. DREAM selects protocol variants by array index, so a single positional shift would silently re-point a saved clinical plan at a different dose.Text changes, reviewed individually
24 presentational differences, no numbers among them — e.g.
Neat (100mg/mL)→Neat (100 mg/mL),1g→1 g powder for injection. Accepted as improvements.Diluent wording was not accepted as-is: SCRATCH said
Normal saline (NS)where DREAM prints0.9% sodium chloride. The explicit concentration is the safer term on a document followed at the bench, so SCRATCH is being standardised to match in a companion PR. Merge that one first, then re-runnpm run protocols:synchere before merging this.Not in this PR
UI work — showing dilution tables inline,
under_reviewbadges, and deep-links to SCRATCH pages — is a separate change.🤖 Generated with Claude Code