AI Foundation — Sprint A–B Buildable Specs (the contract, the door, the gate)¶
Date: 2026-06-04 · From: Gene (Pharmacy Ops) · For: Faraz + Emil
Scope: the three foundation artifacts that gate everything else — build these first. (A) the shared medication-object contract (FHIR), (B) the MCP server the knowledge layer exposes, and (C) the deterministic clinical-line rail. Each is concrete enough to start coding. (Parent: AI_Foundation_Architecture_Spec_for_Faraz_2026-06-04.)
Why these three first: the contract is what every engine produces/consumes; the MCP server is the one door to the knowledge; the rail is the boundary that must exist before any model emits to a member. The med-object contract is also Phase 0 of the Medication Intelligence Engine and the object the Replit demo renders — so building it now feeds both.
A · The shared medication-object contract (FHIR)¶
Base standards: FHIR R4 MedicationRequest + Medication + Provenance, the SIG aligned to NCPDP SCRIPT SCS (so Surescripts ingest is a mapping, not a rewrite), with three Rx360 extensions (provenance-weight, confidence, flags). This is the canonical object; every input path produces it and every engine reads it.
A.1 The object (illustrative JSON — metformin 500mg BID with food)¶
{
"resourceType": "MedicationRequest",
"id": "mr-uuid",
"status": "active",
"subject": { "reference": "Patient/member-uuid" },
"medicationReference": { "reference": "Medication/med-uuid" },
"dosageInstruction": [{
"sequence": 1, // multi-phase → tapers (sequence 2,3…)
"text": "Take 1 tablet by mouth twice daily with food", // live SigText preview
"timing": { "repeat": { "frequency": 2, "period": 1, "periodUnit": "d",
"when": ["MORN","EVE"] } },
"route": { "coding": [{ "system": "http://snomed.info/sct", "code": "26643006",
"display": "Oral route" }] },
"doseAndRate": [{ "doseQuantity": { "value": 1, "unit": "tablet" } }],
"additionalInstruction": [{ "text": "with food" }] // food/timing rule
}],
"_rx360": { // ── Rx360 extensions ──
"provenance": { "source": "pharmacist-verified", "weight": 1.0,
"npi": "1234567890", "verifiedAt": "2026-06-04T15:00:00Z" },
"confidence": { "score": 0.97, "gate": "auto-default" }, // auto | force-confirm | manual
"flags": [ { "type": "fall-risk-class", "view": "pharmacist" } ],
"audit": { "ocrOriginal": "METFORMIN 500MG TAB ...", "confirmed": "..." }
}
}
// Medication resource (referenced):
{ "resourceType": "Medication", "id": "med-uuid",
"code": { "coding": [{ "system": "http://www.nlm.nih.gov/research/umls/rxnorm",
"code": "860975", "display": "metformin 500 MG oral tablet" }] },
"form": { "coding": [{ "display": "oral tablet" }] },
"ingredient": [{ "itemCodeableConcept": { "text": "metformin" } }],
"_rx360": { "ndc": "00093-1048-01", "drugClasses": ["biguanides"], "deaSchedule": null } }
A.2 Field → source-of-truth → who consumes it¶
| Field | Source | Consumed by |
|---|---|---|
Medication.code (RXCUI) |
RxNorm | all engines (identity) |
_rx360.drugClasses (RxClass) |
RxNav/RxClass | Balance Meter (FRID/Beers), rail (duplicate/interaction) |
dosageInstruction.timing + text |
structured SIG builder | Scheduling (cadence/tolerance) |
additionalInstruction (food/timing) |
DailyMed/SPL | Scheduling (dose-context payload) |
_rx360.provenance (weight) |
verification gate | Balance Meter weighting, scheduling precision |
_rx360.confidence.gate |
matcher | the UX (auto/force-confirm/manual) |
_rx360.flags |
knowledge-graph query | pharmacist view only |
_rx360.audit |
OCR + confirm | the learning loop |
A.3 Validation rules (non-negotiable)¶
- Never invent — if
Medication.code(RXCUI) can't be resolved at confidence ≥ threshold, the object isgate: "manual"; no strength/dose is auto-filled. - High-risk →
gate: "force-confirm"regardless of confidence (insulin · anticoagulant · opioid · controlled · NTI · LASA — from the ruleset-config). - Provenance required on every stored object; member-self ×0.5 → pharmacist-verified ×1.0.
- Schema-valid against FHIR + the
_rx360extension schema before it leaves the app (XSD/JSON-schema validation on save). - PRN meds carry no scheduled
timing(flagged PRN; refill/usage tracked instead).
B · The MCP server — the one door to the knowledge layer¶
The curated KB + graph + vector index are exposed through one MCP (Model Context Protocol) server, so every engine queries the knowledge the same way and governance isn't re-implemented per engine. Stateless (per the 2026 MCP roadmap) for horizontal scale.
B.1 Tools (signatures)¶
# Every tool returns a PROVENANCE ENVELOPE (see B.2). Read-only; no PHI in queries.
resolve_drug(query: str | None, ndc: str | None, rxcui: str | None)
-> { rxcui, ingredient, brand, form, route, strengths[], drug_classes[], dea_schedule }
modal_defaults(rxcui: str) # the smart-match priors
-> { strength, dose_form, frequency, timing[], food }
graph_query(rxcui: str, member_med_rxcuis: str[])# the "smart intelligence" flags
-> { duplicate_therapy[], interactions[], time_separation[], frid_class: bool }
tolerance(rxcui: str | None, drug_class: str | None) # drug-forgiveness (scheduling)
-> { cadence, tolerance_window, food_rules, high_stakes: bool } # preliminary until sign-off
semantic_search(query: str, k: int = 8) # fallback / dose-context
-> { chunks[]: {text, kb_entry_id} }
B.2 The provenance envelope (on every response)¶
{ "value": { ... }, // the answer
"source": "RxNorm" | "OpenFDA" | "DailyMed" | "curated-KB" | "graph",
"kb_entry_id": "kb:biguanides:tolerance:v3", // traceable
"version": "2026-06-01",
"clinical_line_flag": false } // true → must route through the rail / pharmacist, never auto-served
B.3 Notes¶
- Structured-first:
resolve_drug/modal_defaults/graph_queryhit deterministic tables;semantic_searchis the fallback. (Don't let the vector store answer what a table can.) - The graph is a projection of the lakehouse, synced via the event stream — read here, never hand-edited.
- Engines (and any LLM) get clinical facts only through this server — never from model memory.
C · The deterministic clinical-line rail — the boundary¶
The terminal, fail-closed gate on every member-facing output. Non-ML by design (heterogeneous failure vs the model-based rails upstream).
C.1 Contract¶
gate(candidate_output: str, target: "member" | "pharmacist", grounding: GroundingRefs)
-> { action: "emit" | "block_and_route", reason?: str, queue?: "pharmacist" }
C.2 The rules (all deterministic)¶
- Allow-listed output schema — member-facing payload must be exactly
{type: reminder | confirmation | insight, ...}. Any other shape → block. - Lexicon / pattern detectors (block on match):
- Dosing language: a quantity+unit (
\d+\s?(mg|mcg|mL|units|tablet)) attached to an instruction verb (take|increase|decrease|titrate|split|double|skip|stop). - Diagnostic assertion: "you have / this is" + a condition; naming a specific disease as fact.
- Clinical-management claim: treatment/management directive.
- Grounding check: every clinical field in the output must carry a
kb_entry_id(from the MCP envelope); ungrounded clinical content → block. - Lexicon sweep against the Rx360 Wellness Lexicon red-flag set.
C.3 Fail-closed behavior¶
On any detector match, low confidence, schema violation, or rail error/timeout → block_and_route → pharmacist queue. Default state = "do not emit." A timeout is a block, never a pass-through.
C.4 Hard rules¶
- Do not stream member-facing clinical-adjacent output — buffer fully, gate before the first token reaches the member.
- OCR'd Rx text is untrusted (injection vector) — never treated as instructions.
- Target metric: clinical-line leakage = 0, measured by sampling emitted member output.
D · How A–B–C feed the demo + the build¶
- The med-object (A) is what the Replit demo renders ("one object → reminder + fall-risk + refill") and Phase 0 of the Med-Intelligence Engine.
- The MCP server (B) is how the demo does live enrichment (RxNorm/OpenFDA) + the smart-link flags.
- The rail (C) is the visible "guardrail" in the demo (warfarin → force-confirm; a dosing string → blocked → pharmacist queue).
- Sprint order: A (contract) + C (rail) can be built in parallel; B (MCP server) wraps the knowledge once the contract exists. Then the engines + the demo.
E · Open for Faraz¶
- Backend stack (confirms FHIR-validation lib + the
_rx360extension JSON-schema home). - MCP server hosting (stateless behind a gateway) + which knowledge sources are live vs cached in v1.
- The high-risk + lexicon rulesets — confirm they live as pharmacist-curated config (not code), versioned.
- Confidence thresholds (auto / suggest / manual cut-points) — set with Dr. B + measured in pilot.