medscript
a tiny handwritable symbol set for medication lists at handoffs — think "markdown for med lists." it makes the communication layer legible, compressible, and portable across paper, charts, and SMS. the next clinician must decide fast what's active, what changed, what's uncertain, and what's dangerous. today's notes hide all four inside competent-looking prose. medscript puts each one in the left margin, in a symbol, where the eye lands first.
frozen v1: 10 symbols, 2 modes, 1 ascii fallback, hard scope walls. a stranger can implement a parser/validator from the parser contract below without asking questions — that was the acceptance test, and the document is written to it. this page is the full spec, hosted at home so it can't die with somebody else's platform.
disclaimer — desk work only
this specification is desk work only. it has not been clinically validated. none of the authors are clinicians. this is a complete, rigorous, internally consistent design document — not a clinical guideline, not a medical device, not an endorsement for use in patient care. real-world validation requires real nurses, pharmacists, and physicians in supervised pilots. do not deploy this in clinical practice on the strength of this document alone.
the problem
at every handoff, the next clinician must decide fast: what's active, what changed, what's uncertain, what's dangerous. today's notes bury all four inside competent-looking prose. the prose looks complete, so nobody asks the question it silently didn't answer. that's where the harm lives.
medicine already standardizes terminology, coding, and structured orders. it does not standardize the scribble on the handoff sheet — the layer where meaning is actually lost. medscript is that layer. it is not a replacement for clinical judgment, an ordering system, or a terminology code. it sits under all of those.
the design analogy: sheet music. the notation must preserve meaning under pressure, stay readable across contexts, and feel intuitive enough that basic information decodes without a codebook.
design principles (non-negotiable):
= / + / ? / !! on first sight.§3 · the frozen set
the symbol leads the line — scan the left margin, know the story before reading a word. click any symbol to copy it.
reviewed, no change. the entry was verified against a source and stands as written.
= lisinopril 10mg PO ×1
click the symbol to copy
newly added at this transition point.
+ apixaban 5mg PO ×2
click the symbol to copy
discontinued or held — inactive either way. the bracket MUST say which (rule V4). preferred over - (ambiguous vs hyphen) and — (typographic).
∅ atorvastatin 40mg PO ×1 [pre-op hold — resume POD2]
click the symbol to copy
dose increased. the line MUST use old→new notation (rule V3).
↑ sertraline 50mg→100mg PO ×1 [titration — week 3]
click the symbol to copy
dose decreased. the line MUST use old→new notation (rule V3).
↓ metoprolol succinate 50mg→25mg PO ×1 [tapering — cardiology]
click the symbol to copy
conditional / PRN. the bracket MUST state the condition; ×N on a ~ line is the ceiling per 24h, not a schedule (rule V2).
~ oxycodone 5-10mg PO ×6 [pain ≥4/10 — max 6/24h]
click the symbol to copy
allergy, dangerous interaction, or care-blocker. demands attention before anything else on the page.
!! penicillin [anaphylaxis]
click the symbol to copy
something on this line is uncertain. the bracket MUST explain why — a bare ? is invalid (rule V1).
? aspirin 81mg PO ×1 [pt unsure if still taking]
click the symbol to copy
administration instruction: timing, food, condition. attaches to its line only — never the next one (rule R7).
= levothyroxine 75mcg PO ×1 @fasting / 30min pre-food
click the symbol to copy
N doses per calendar day. replaces qd/bid/tid/qid (already recognized as error-prone). non-daily schedules go in @ (rule V6).
×2 = twice daily · ×1 = daily · ×4 = four times daily
click the symbol to copy
frozen means frozen. no new symbols may be
added to v1 documents. proposals for v2 must be flagged
PROPOSED — not frozen and must not appear in v1-conformant
documents.
rejected during design (do not reintroduce silently):
⚠ (hard to handwrite) · [ ] hold marks (confused
with parentheses) · — for discontinuation (ambiguous vs
hyphen) · Δ for discrepancy (too learning-dependent) ·
HLD (english-dependent) · qd/bid/tid (error-prone).
stress tests · the hard cases
each "before" is a competent-looking, professionally formatted note — the kind a good clinician writes today. the danger in every one is the same: it looks complete, so nobody asks the question it silently didn't answer. the "after" should feel like relief, not cleverness.
the trap: PRN orders look precise ("5-10mg q4-6h PRN pain") while hiding three decisions: which dose, what threshold triggers it, and the daily ceiling. the nurse at 3 a.m. makes all three alone.
PRN MEDICATIONS (admission orders, 09/30)
Oxycodone 5-10 mg PO q4-6h PRN pain
— assess pain prior to administration; hold if RR < 12
— may repeat per pain scale[post-op floor, 09/30, 14:22, RN:SK] ALLERGY: = NKDA [verified 09/30] ~ oxycodone 5-10mg PO ×6 [pain ≥4/10 — start 5mg; ↑ to 10mg if unrelieved at 1h — max 6 doses/24h]
professional, formatted, signed — and the before hides: who chooses
5 vs 10? what pain score counts as "pain"? maximum in 24 hours? the
~ forces the condition into the bracket and the
×6 forces the ceiling into the open. the titration rule
(5 first, 10 if unrelieved) is stated instead of implied. nothing about
the clinical decision changed — it just stopped being invisible.
the trap: "dosed per weight" is written once, at ordering time. the weight itself — the number the entire dose depends on — lives somewhere else, measured at some earlier date, possibly estimated. the list carries the conclusion without the premise.
DISCHARGE MEDICATIONS — 09/30 Amoxicillin 250mg/5mL suspension — 1 tsp (5 mL) PO BID × 10 days Dx: acute otitis media. Dosed per weight. Counselled on completion.
[clinic, 09/30, 10:05, MD:RA] ALLERGY: = NKDA [verified 09/30] = amoxicillin 250mg/5mL PO ×2 [wt: 18kg 2026-09-30 — 45mg/kg/day ÷ BID × 10d]
clean, complete-looking, counselled and signed. hides: which weight? measured when — today, last month, estimated by a parent? what mg/kg basis? the premise is back on the line in the after: 18 kg, measured today, 45 mg/kg/day basis. anyone can re-derive the dose, and anyone can see when the weight goes stale.
the trap: the sliding-scale table looks like the most complete thing on the page — a neat grid of numbers. its completeness is the trap: it hides which protocol version this is, whether it covers meals or only correction, what happens below the bottom row, and where the basal insulin lives. nobody questions a table.
INSULIN ORDERS — 09/30
Per sliding scale (posted protocol):
BG 150-199 → 2u | 200-249 → 4u | 250-299 → 6u | ≥300 → 8u + notify MD
Humalog SC with meals and at bedtime
Lantus 20u SC at bedtime[floor admit, 09/30, 14:22, RN:SK] ALLERGY: = NKDA [verified 09/30] = insulin glargine 20units SC ×1 @bedtime ~ insulin aspart SC ×4 [protocol: unit sliding-scale v3 — correction only, with meals + hs — see chart] ? insulin aspart meal coverage [ordered? — scale reads correction-only — verify prandial plan]
v1 refuses to encode the table — that's the scope wall doing its
job (reference a protocol, don't encode it). the ~
carries the version number (v3) so a stale protocol can't
hide, and the ? line drags the unasked question — does
this cover meals? — into the open. the after is longer than the
table and less comfortable, which is the point.
the trap: the unknown gets formatted like the known. "likely atorvastatin" sits in a numbered list next to verified entries, and by the third handoff the "likely" has evaporated. formatting confers a certainty the content never had.
HOME MEDICATIONS — per patient interview, 09/30, 22:40 1. Lisinopril 10 mg daily — for blood pressure 2. Cholesterol pill — white, round tablet (likely atorvastatin) 3. Aspirin 81 mg daily 4. Multivitamin daily Allergies: NKDA (per patient)
[ED triage, 09/30, 22:41, RN:MT] ALLERGY: ? NKDA [pt-reported — unverified] !! MEDICATION HISTORY UNKNOWN ? lisinopril 10mg PO ×1 [pt-reported dose — unverified — likely for BP] ? atorvastatin ?mg PO ×1 [pt: "white round cholesterol pill" — drug AND dose unverified — DO NOT assume] = aspirin 81mg PO ×1 [pt brought bottle — verified] ? multivitamin [pt-reported — brand/dose unknown]
the structural flag (!! MEDICATION HISTORY UNKNOWN)
says the truth up front: this list is a starting point, not a record.
every guess carries its ? and its reason, in the same
position on every line, so the uncertainty survives transcription —
it's in the grammar now, not in an adjective someone can drop. note
the = aspirin: the one verified item is visually distinct
from the four uncertain ones. that distinction is the whole
deliverable.
the trap: psych meds carry two risks ordinary lists don't: invisible non-adherence (the list says "daily," the patient stopped weeks ago) and discontinuation harm (stopping abruptly is itself a clinical event). a standard med list records neither — it records the prescription, which is the one thing everyone already knows.
PSYCHIATRIC MEDICATIONS (intake note, 09/30) - Sertraline 100 mg qAM — pt reports stopped taking ~3 days ago, "felt fine" - Quetiapine 25 mg qhs PRN sleep/agitation Plan: restart sertraline prior to discharge
[floor admit, 09/30, 14:22, RN:SK] ALLERGY: = NKDA [verified 09/30] ∅ sertraline 100mg PO ×1 [abrupt stop ×3d — pt-reported — discontinuation syndrome risk — DO NOT restart at full dose without MD order] ~ quetiapine 25mg PO ×2 [agitation/sleep — max 2/24h] ? sertraline adherence pre-stop [duration of consistent use unknown — verify with pharmacy fill history]
the ∅ marks sertraline inactive with the reason on
the line — the discontinuation is treated as the clinical event it
is, not as a footnote to a prescription. the bracket blocks the
dangerous default (restart at 100 mg) explicitly. the ?
line names the missing adherence history and says how to get it. every
sentence in the before is true and the note is still dangerous,
because the risks are in the whitespace between sentences.
§4 · structure
provenance first. the first non-blank line of every document is the provenance line: who wrote this, where, when. format: [floor admit, 05/10, 14:22, RN:SK]. validators MUST find at least one date token and one author token (RN:SK, MD:RA); otherwise the document is invalid.
allergy block above the list, always. immediately after provenance, the literal heading ALLERGY:, then one or more lines. every allergy entry leads with !!: !! penicillin [anaphylaxis] / !! sulfa [SJS]. no known allergies? write = NKDA with a verification note: = NKDA [verified 09/30]. the block is mandatory — its absence is a validation error, because a missing allergy block is indistinguishable from a forgotten one.
incomplete history is structural, not a footnote. if the medication history is incomplete or unknown, flag it as its own line between the allergy block and the med list: !! LIST INCOMPLETE or !! MEDICATION HISTORY UNKNOWN. a document carrying this flag may have zero med lines — that is valid and honest. guesses formatted as entries are not.
pediatric weight + date in brackets. any line whose dose is weight-derived MUST carry [wt: 18kg 2026-09-30] — weight in kg, then the date it was measured. the mg/kg basis SHOULD be stated too (45mg/kg/day).
one medication per line (expanded mode). inline mode compresses with / separators — see the modes section.
brackets carry meta, never primary data. drug identity, dose, route, and frequency always live outside brackets. brackets carry reactions, hold types, conditions, provenance detail, and explanations.
@ attaches to its own line only. an instruction never applies to the next line.
recommended bracket keywords (conventions,
not symbols — parsers treat bracket text as free text but SHOULD
recognize these): rx: (prescription basis) · wt:
(weight) · hold / resume: · permanent ·
protocol: (external protocol reference) · verify ·
pt: (patient-reported) · supp: (supplement) ·
NKDA.
§6 · modes
expanded mode — one medication per line; the canonical record.
inline mode — for notecards, wrist tags, EMS sheets; segments
separated by /. same list, both modes, below:
[floor admit, 05/10, 14:22, RN:SK] ALLERGY: !! penicillin [anaphylaxis] !! sulfa [SJS] ? metoprolol succinate 25mg PO ×1 [rx: ×2 — pt taking once daily, clarify intent] = lisinopril 10mg PO ×1 ∅ atorvastatin 40mg PO ×1 [pre-op hold — Dr. Chen — resume POD2] = levothyroxine 75mcg PO ×1 @fasting / 30min pre-food ∅ metformin 500mg PO ×2 [contrast hold — confirm radiology — reinstate post-procedure] ? aspirin 81mg PO ×1 [pt uncertain if still taking — verify pre-op] ? fish oil 1000mg PO ×2 [supp / pt-reported / unverified] !! St. John's Wort [pt-reported / undosed / serotonin + anesthesia interaction — CLARIFY PRE-OP]
read the left margin: ? = ∅ = ∅ ? ? !!. in four seconds
you know: two uncertainties, two holds, one critical interaction,
everything else verified. that scan is the entire point of verb-first
architecture.
[floor 05/10 14:22 RN:SK] / ALLERGY: / !!penicillin[anaphylaxis] / !!sulfa[SJS] / ?metop25×1[rx:×2] / =lisinopril10 / ∅atorva40[pre-op/POD2] / =levo75@fast / ∅metfm500[contrast/reinstate] / ?ASA81[pt?] / ?fishoil×2[supp] / !!SJW[anesthesia]
inline positional rule: if ALLERGY: appears as a
segment, it MUST be the first segment after provenance; !!
entries following it are allergy entries until the first
non-!! segment. NKDA inline: =NKDA.
inline loss rules (documented, not accidental):
metoprolol succinate → metop25); parsers treat inline drug names as prefix hints, not canonical.mg./ and after the status symbol is optional.? and ~ still require brackets).§7 · degraded environments
photocopy, SMS, paper-only, power outage: every non-ascii symbol gets a plain-ascii equivalent that survives the worst channel. a conformant ascii transcription replaces all non-ascii symbols consistently; validators MUST accept full-ascii documents and MUST warn on mixed mode (rule V7).
| unicode | ascii | notes |
|---|---|---|
= | = | — |
+ | + | — |
∅ | XX | double-cross. never single X (collides with ×), never -. |
↑ | ^ | — |
↓ | v | — |
~ | ~ | — |
!! | !! | — |
? | ? | — |
@ | @ | — |
×N | xN | lowercase x. x2 = twice daily. |
→ | -> | dose change: 50mg->25mg. |
– / — | -- | free-text dashes only. |
the worked example above, transcribed:
[floor admit, 05/10, 14:22, RN:SK] ALLERGY: !! penicillin [anaphylaxis] !! sulfa [SJS] ? metoprolol succinate 25mg PO x1 [rx: x2 -- pt taking once daily, clarify intent] = lisinopril 10mg PO x1 XX atorvastatin 40mg PO x1 [pre-op hold -- Dr. Chen -- resume POD2] = levothyroxine 75mcg PO x1 @fasting / 30min pre-food XX metformin 500mg PO x1 [contrast hold -- confirm radiology -- reinstate post-procedure] ? aspirin 81mg PO x1 [pt uncertain if still taking -- verify pre-op] ? fish oil 1000mg PO x2 [supp / pt-reported / unverified] !! St. John's Wort [pt-reported / undosed / serotonin + anesthesia interaction -- CLARIFY PRE-OP]
§5 + §6 · for implementers
a document is v1-conformant iff: it parses under the grammar (expanded or inline), it satisfies all validity rules and structural rules, it uses only the frozen symbol set (plus ascii equivalents), and it stays inside the scope walls. syntax is the grammar; the V-rules decide validity — a document can parse and still be invalid.
the document shape (expanded mode, EBNF condensed):
document = provenance , newline ,
allergy-block , newline ,
[ history-flag , newline ] ,
med-list , [ newline ] ;
provenance = "[" , prov-tag , { "," , prov-tag } , "]" ;
(* validity (R1): at least one date token and
one author token required *)
allergy-block = "ALLERGY:" , newline ,
( allergy-entry , { newline , allergy-entry }
| nkda-line ) ;
allergy-entry = "!!" , space , drug-name ,
[ space , "[" , free-text , "]" ] ;
nkda-line = "=" , space , "NKDA" ,
[ space , "[" , free-text , "]" ] ;
history-flag = "!!" , space ,
( "LIST INCOMPLETE"
| "MEDICATION HISTORY UNKNOWN" ) ;
med-line = status , space , entry-body ;
status = "=" | "+" | "∅" | "↑" | "↓" | "~" | "!!" | "?" ;
entry-body = drug-name , [ dose ] , [ route ] , [ frequency ] ,
{ "@" , admin-text } , [ "[" , annotation , "]" ] ;
dose = simple-dose | range-dose | change-dose ;
simple-dose = number , unit ; (* 25mg, 75mcg *)
range-dose = number , "-" , number , unit ;
(* 5-10mg; ~ lines only *)
change-dose = number , unit , "→" , number , unit ;
(* 50mg→25mg; ↑/↓ lines *)
frequency = "×" , count ; (* count ≥ 1; ×0 invalid *)
parsing drug-name vs dose: scan tokens left to right after the
status symbol; the first token matching number unit
(e.g. 25mg, 2puffs) begins the dose. everything
before it is the drug name. units and routes are open vocabularies —
parsers MUST NOT reject unknown units or routes.
the validity rules:
? requires a bracket. ? was doing double duty (uncertainty vs discrepancy) in early design. resolution: broad ?, mandatory bracket explaining why. ? aspirin 81mg PO ×1 alone is INVALID; ? aspirin 81mg PO ×1 [pt uncertain if still taking — verify pre-op] is valid.
~ requires condition + ceiling. every ~ line MUST carry ×N (maximum doses per 24h) and MUST carry [...] stating the condition. in a ~ line, ×N is the ceiling, never a schedule: ~ oxycodone 5-10mg PO ×6 [pain ≥4/10 — start 5mg, ↑ to 10mg if unrelieved at 1h — max 6 doses/24h]. a ~ line may express a dose range with a hyphen (5-10mg) — the only place a range is allowed.
↑/↓ require old→new. dose-change lines MUST use old-dose → new-dose: ↓ metoprolol succinate 50mg→25mg PO ×1 [tapering — cardiology]. a bracket explaining why SHOULD be present.
∅ requires a hold classification. ∅ means inactive — temporary hold and permanent discontinuation share the symbol, and the bracket MUST say which: [pre-op hold — Dr. Chen — resume POD2] or [discontinued — adverse reaction — permanent]. one symbol, explicit bracket.
!! med lines SHOULD explain. !! St. John's Wort [pt-reported / undosed / serotonin + anesthesia interaction — CLARIFY PRE-OP]. allergy-block !! entries SHOULD carry the reaction in brackets; when the reaction is genuinely unknown, write [reaction unknown] rather than omitting.
×N means per calendar day. ×1 = daily, ×2 = BID, ×3 = TID, ×4 = QID. ×0 is invalid. non-daily schedules are not expressible in ×N — put them in @: @q8h, @q48h, @weekly, @every Monday. (qd/bid/tid/qid themselves are banned from v1.)
mode detection. a document containing any of ∅ ↑ ↓ × → is unicode mode. a document using XX, ^, v, xN, or -> in symbol positions is ascii mode. mixed mode parses but validators MUST warn.
blank lines are permitted between sections and are ignored. leading whitespace on a line is ignored.
what a validator MUST report, at minimum:
unknown symbols, missing provenance, missing allergy block, ?
without bracket, ~ without ceiling/condition, ∅
without hold classification, ↑/↓ without old→new,
weight-based dosing without a wt: bracket, and mixed
unicode/ascii mode. a parser SHOULD recover line-by-line: one invalid
line must not invalidate the whole document.
pocket validator — a demo of the §5 rules against a single med line. not a clinical tool, not a conformance suite; the rules above are the contract.
§9 · scope
IN
general medication reconciliation at transition points.
OUT
specialized domains. for each: write the status line, then [protocol: <name/version>]. reference a protocol; never encode it.
also out — not a scope question, a category question: prescribing and order entry (medscript is a communication layer, not an order format); clinical decision support; terminology coding (RxNorm/SNOMED mapping). medscript must not be mistaken for an order — it describes what a list means, it does not authorize anything. a protocol reference looks like this:
[floor admit, 09/30, 14:22, RN:SK] ALLERGY: = NKDA [verified 09/30] = insulin aspart SC [protocol: unit sliding-scale v3 — see chart]
§11 · decision log
| # | tension | resolution | rule |
|---|---|---|---|
| 1 | ? doing double duty (uncertainty vs discrepancy) | broad ?, mandatory bracket explanation | V1 |
| 2 | hold vs discontinue | ∅ = inactive, any duration; bracket MUST classify temporary (with resume condition) vs permanent | V4 |
| 3 | PRN encoding | ~ + bracket carries condition + ceiling; ×N on ~ lines = ceiling per 24h | V2 |
| 4 | sliding-scale insulin / complex protocols | reference, don't encode: [protocol: <name>] | §9 |
| 5 | qd/bid/tid/qid | banned; ×N only; non-daily schedules in @ | V6 |
| 6 | no known allergies | = NKDA in the mandatory allergy block | R2 |
| 7 | inline drug-name truncation | allowed but lossy; expanded mode is canonical | §6.3 |
| 8 | unit elision in inline mode | allowed; omitted unit defaults to mg | §6.3 |
| 9 | ascii for ∅ | XX — never single X, never - | §7 |
| 10 | dose range vs dose change | - for PRN ranges (5-10mg), → for changes (50mg→25mg) | §6.2 |
§10 · prior art
adoption
standards don't spread through endorsement. they spread through use — and use concentrates at chokepoints. nursing handoffs are the chokepoint:
that's the adoption vector: make the handoff sheet better, and the standard rides the highest-frequency, most-painful, most-mobile workflow in medicine. a standard spreads through use, not endorsement.
status
v1 is frozen as a technical specification (2026-09-30) — complete, internally consistent, parser-implementable. changes require a v2 proposal; do not extend v1 ad hoc.
desk work only — not clinically validated
say it again at the bottom, because the bottom is where people stop reading the top: this is desk work only. it has not been clinically validated, none of the authors are clinicians, and it is not safe to use in patient care on the strength of this document. real-world validation needs real nurses and pharmacists in supervised pilots before any patient ever sees it.
name note: MEDSCRIPT is a working title with trademark baggage in pharmacy services (multiple USPTO filings, past litigation). clearance pending. nothing in the spec depends on the name.