ADR-0050 — Dose correction is a per-field patch: explicit strike, corrected effective drives the winner¶
- Status: Accepted
- Date: 2026-07-15
- Refines: principle 4 (acknowledged
uncertainty — an imprecise near-truth beats a precise untruth; no forced fabrication); the
§3.6 bitemporal model
(
t_effectiveis the freely-correctable claim). Extends the dose-timeline overlay opened by slice 2 (db/032_medication_dose.sql) on the surface from slice 1. Reuses the existingclinical.medication-dose-correction.assertedverb — no new event type.
Context¶
Slice 2 gave the medication surface a bitemporal dose timeline (point 0 seeded from the assert's started,
plus one point per -dose-change) and a -dose-correction verb that overlays a targeted point. But that
overlay carried only amount/unit/reason/info_source, and both timeline views computed the effective
date purely from the original event. Two gaps followed directly from that shape:
- A mis-keyed effective date could not be fixed. This is not cosmetic: the current dose is the latest-effective point, so a wrong effective date can make the wrong dose "current" or scramble the titration trail's chronology. Correcting the date is bitemporal repair, not a display-label edit — but the verb had no field to carry the fix.
- The point's clinical reason could not be fixed either, and a related defect sat underneath: the
correction row stored a
reasoncolumn (meant as "why I'm correcting"), but the dose-history view read the original event's reason, so the correction's reason was written and never surfaced — dead data.
Slice 2's own correction semantics also had a latent sharp edge: a correction payload with no set fields was interpreted as "strike the dose to unknown" (omission-as-strike). That collapses two different clinician intents — "I have nothing new to say about the dose" and "I no longer know the dose" — onto one wire shape, which is exactly the ambiguity principle 4 warns against: uncertainty must be a first-class, explicit value, never inferred from what a clinician left blank.
Decision¶
A dose correction is a per-field patch of three independent groups — dose (amount+unit), effective
(value+precision), reason (the point's clinical reason) — with an explicit strike array for set-to-unknown,
and the corrected effective date participates in current-dose winner selection.
- Per-field patch, not whole-point restatement. For each group G: present as a set-key → touched,
value becomes the given value; named in
strike→ touched, value becomes unknown; neither → untouched, the point's existing value is kept. This makes the common case — fix one field, leave the rest — safe: correcting a date typo never silently wipes the dose, and vice versa. A group named both as a set-key and instrikeis a contradiction the floor rejects. Thedosegroup is patched as one atomic{amount?, unit?}object (the same shape assert /-dose-changeuse) — per-field keep applies between groups, not inside the dose quantity, which is one clinical value. strikemakes set-to-unknown first-class and explicit. Slice 2's implicit "omit everything = strike the dose" is retired; a correction that touches nothing (no set-key, nostrikeentries) is now a no-op the floor rejects. To assert "I don't know the dose," a clinician namesdoseinstrike— an explicit act, not an absence.- The corrected effective date drives current-dose winner selection. The dose-timeline views'
ORDER BYnow sorts on the corrected effective value when theeffectivegroup was touched, falling back to the original otherwise. Winner selection is by effective-date order, so fixing a mis-keyed date changes which point reads as current and can reorder the titration trail — this is the bitemporal repair the un-correctable date made impossible, not a cosmetic label change. - The correction rationale is a separate
note, distinct from the point's clinicalreason. Slice 2's correctionreasonmeant "why I'm correcting" but was never surfaced. This slice repurposesreasonto mean what it means everywhere else on a dose point — the clinical reason ("titration", "renal dosing"), now patchable and finally surfaced in the dose history — and addsnoteas the always-additive audit rationale for the correction act itself ("mis-keyed the date"). The two answer different questions ("why is the dose what it is" vs. "why was this correction made") and neither should overwrite the other. (The CLI spells this--correction-note, not--note— the latter is already the attestation vouch note on--attest-as, and the two must stay distinguishable at the point of care.) schema_versionbumpsclinical.medication-dose-correction/1 → /2. Cosmetic in the sense that nothing keys off the version number, but honest: the interpretation of an omitted field changed from "unknown" (v1) to "keep" (v2), and that is exactly the kind of silent meaning-shift a version bump exists to flag. No/1correction event exists in production (pre-clinical posture), so there is no live-data migration concern — only the honesty of the signal itself.
No new event type, no new envelope field, no floor bypass, no SCHEMA counter bump — the existing verb, the
existing floor function name, and the existing apply trigger are all reused (extended, not replaced). The
change is confined to one new migration (db/035_medication_dose_effective_correction.sql)
that ALTER TABLE-extends slice 2's medication_dose_correction table and re-defines the correction floor,
apply trigger, and the two dose-timeline views in place; db/031–034 are untouched.
Consequences¶
Now possible: a mis-keyed effective date or clinical reason on any dose point is correctable without restating the whole point; the dead correction-reason column is surfaced; set-to-unknown is an explicit, legible act instead of an inferred one.
Convergence boundary — must be read alongside the per-field patch. The overlay keeps slice 2's shape: one
row per corrected point, highest-HLC-wins wholesale (cairn_hlc_overlay_wins) — this is what keeps set-union
sync convergent without inventing a dangerous field-level merge. Per-field patch therefore applies within
one correction event, against the original point; it does not compose across corrections of the same
point. A later correction of a point supersedes an earlier correction of that same point wholesale, rather
than field-merging with it. Concretely: if correction A fixes the effective date and a later correction B (on
the same point) fixes only the dose, B's effective group is untouched — meaning "keep the point's own
effective" — and B wins the row outright, so A's date fix is lost unless B restates it. To keep an earlier
field-fix alive, a subsequent correction of the same point must restate it. This is a deliberate, documented
boundary, not an oversight: true per-field merge across corrections would require per-field HLC tracking
(either multiple overlay rows or a per-field overlay guard) to remain convergent under set-union sync, and that
is a real design cost this slice did not take on. It is left as a deliberate future refinement if the
wholesale-supersede boundary proves to bite in practice.
Accepted costs / deferred:
- Correcting the medication's top-level started date (patient_medication_current.started_value, the
assert's own field) is out of scope — a correction targeting point 0 fixes that point's effective in the
dose timeline only. A future "correct the statement's started/term/sig" slice would handle the assert-level
field.
- The effective value stays an uncertainty-capable free string, matching -dose-change; the floor checks
structure/type only and never parses the date (locale-neutral).
- The backfill that reshapes pre-035 correction rows (reason → note, touched-flags inferred as
dose-only-corrected) is guarded and idempotent, but is cosmetic housekeeping only possible because no /1
correction event exists in production yet — this discipline does not generalize once the record carries real
patient data.
How we would know the bet is failing: if clinicians routinely correct the same dose point more than once and lose earlier field-fixes often enough that restating becomes a recurring workflow tax, that is the signal to build the per-field-HLC refinement rather than continue relying on "restate to keep." Absent that pressure, the simpler wholesale-supersede overlay is the right cost/complexity trade for a still-early clinical surface.
Not a new founding principle. This is principle 4 (acknowledged uncertainty — explicit strike over inferred omission) and the existing bitemporal model (§3.6 — effective time is the freely-correctable claim) applied to a correction overlay already in production. Matches the pattern of the two prior medication ADRs — ADR-0047 (reconciliation as a link) and ADR-0049 (attestation as a separable overlay) — each of which extended the medication surface's overlay machinery without adding new wire shape.