Guardrails Flow Map — Medication Intelligence¶
Date: 2026-06-04 · From: Gene (Pharmacy Ops) · For: Faraz + the room
What this is: one map of how a messy med list becomes a safe, structured object — and exactly where each guardrail sits, what it sees (input), and what it does (output). Companion to the styled page Guardrails_Flow_Map_2026-06-04.html (self-contained, presentable) and the running demo.
The brain is probabilistic; the boundary is deterministic + pharmacist-gated. Every red node below is a guardrail that stops or diverts the flow. Nothing reaches a member until it clears the final fail-closed rail. Clinical-line leakage target = 0.
The flow (Mermaid)¶
flowchart TD
IN([" INPUT<br/>scan printed Rx · type · import"]):::io
RES["RESOLVE IDENTITY<br/>RxNorm + RxClass<br/>name/NDC to RXCUI + drug class"]:::proc
IN --> RES
RES --> Q{"How well did it resolve?"}:::gate
Q -->|"exact / brand to generic"| SM["SMART-MATCH<br/>modal priors fill<br/>strength · form · frequency"]:::proc
Q -->|"real drug, not in local table"| LASA["GUARD · LASA<br/>never substitute a look-alike<br/>felodipine NOT amlodipine"]:::guard
Q -->|"close typo only ( >= 0.86 )"| TYPO["GUARD · typo<br/>did you mean…? suggest only,<br/>never auto-accept"]:::guard
Q -->|"no confident match"| INV["GUARD · never invent<br/>no guessed strength/dose"]:::guard
SM --> GATE{"CONFIDENCE x RISK GATE"}:::gate
GATE -->|"high conf + not high-risk"| AUTO["AUTO-DEFAULT<br/>1-tap accept"]:::ok
GATE -->|"high-risk: insulin · anticoag ·<br/>opioid · NTI · LASA"| CONF["FORCE-CONFIRM<br/>explicit human OK"]:::warn
GATE -->|"low confidence"| MAN["MANUAL ENTRY"]:::warn
LASA --> MAN
INV --> MAN
TYPO --> CONF
AUTO --> OBJ[("STRUCTURED MEDICATION OBJECT<br/>FHIR + provenance + confidence + flags")]:::obj
CONF --> OBJ
MAN --> OBJ
OBJ --> FLAGS["LIST-LEVEL FLAGS · Knowledge Graph<br/>duplicate-therapy · interaction · fall-risk<br/>PHARMACIST VIEW ONLY — never a member alert"]:::pharm
FLAGS --> ENG["DOWNSTREAM ENGINES<br/>reminders · Balance Meter fall-risk · refill"]:::proc
ENG --> RAIL{{"DETERMINISTIC CLINICAL-LINE RAIL · blast door<br/>fail-closed · default = do not emit<br/>dosing? diagnosis? disease claim?<br/>schema violation? timeout?"}}:::rail
RAIL -->|"clean: reminder / confirmation / insight"| EMIT([" EMIT to member"]):::ok
RAIL -->|"any clinical-line content"| BLOCK([" BLOCK to pharmacist queue"]):::block
classDef io fill:#741E4F,stroke:#741E4F,color:#ffffff
classDef proc fill:#ffffff,stroke:#8F3968,color:#241C1A
classDef gate fill:#F3E8EF,stroke:#741E4F,color:#741E4F
classDef guard fill:#FBE9E9,stroke:#B23A48,color:#7A1F2B
classDef ok fill:#E6F2EA,stroke:#3F7A52,color:#1E4D30
classDef warn fill:#FBF0DC,stroke:#B67A2A,color:#7A4E12
classDef obj fill:#F7F0E7,stroke:#741E4F,color:#241C1A
classDef pharm fill:#EDE7F3,stroke:#5B3A8F,color:#3A2363
classDef rail fill:#741E4F,stroke:#241C1A,color:#ffffff
classDef block fill:#F6D9DC,stroke:#B23A48,color:#7A1F2B
Color key: burgundy = input / member output · white = process · pink diamond = decision gate · red = guardrail (block or divert) · green = auto / emit (clears) · amber = force-confirm / manual · purple = pharmacist view only · burgundy hex = the deterministic rail (blast door).
Every guardrail — where it sits, what it sees, what it does¶
| Guardrail | Gate it lives at | What it sees (input) | What it does (output) |
|---|---|---|---|
| Never invent | Resolve / match | a name it can't confidently resolve to a real drug | → MANUAL; never guesses a strength or dose |
| Never substitute a look-alike (LASA) | Resolve | a real drug (RxNorm-known) that is not in the local table | → MANUAL; never swaps in a similar-spelling drug — felodipine ≠ amlodipine |
| Typo = suggestion, not auto-accept | Resolve | a close fuzzy match only (≥ 0.86, e.g. metformn) |
→ FORCE-CONFIRM "did you mean…?"; never silently accepted |
| Confidence × risk gate | Gate | match confidence + the drug's risk class | AUTO (high+safe) · FORCE-CONFIRM (high-risk) · MANUAL (low conf) |
| High-risk → force confirm | Gate | insulin · anticoagulant · opioid · controlled · NTI · LASA pair | FORCE-CONFIRM; always needs an explicit human OK, regardless of confidence |
| Confirm, never prescribe | Confirm | the pre-filled medication object | EDITABLE data-entry; editable SIG — never "take X" |
| Flags → pharmacist view only | Object → list flags | the whole list, cross-checked via the knowledge graph | PHARMACIST: duplicate-therapy / interaction / fall-risk — never a member alert |
| Deterministic clinical-line rail (blast door) | Output (final) | the final rendered member string, after all model rails | BLOCK → pharmacist on any dosing / diagnosis / disease claim / schema violation / timeout; else EMIT. Fail-closed; default = do not emit |
Why two kinds of guardrail¶
- Probabilistic "speed bumps" (the brain). Confidence scoring, fuzzy matching, modal priors, the model's own rails (Llama Guard / validators). They shape and slow the flow — route to auto vs. confirm vs. manual. They can be wrong, so they never get the last word.
- The deterministic "blast door" (the boundary). A non-ML, fail-closed rail with an allow-listed output schema (
reminder · confirmation · insight). Default state = do not emit. Anything clinical-line → block → pharmacist queue. This is the FDA wellness-vs-SaMD boundary, enforced in code outside the model's reach.
Companions: Guardrails_Flow_Map_2026-06-04.html (styled, self-contained) · Medication_Matching_Guardrails_for_Faraz_2026-06-02 (the written guardrail list) · AI_Foundation_Sprint_A-B_Specs (the rail contract) · the running demo (rx360_med_intel_demo, the live tester).