compilerPart of the Travi AI Agent monolith (
app/modules/compiler/). Read00-overview.md§3 (hard rules) before changing anything here.
| Purpose | Documents → validated facts → tasks. Owns the seven gates; the only legal publisher of clinical instructions from machine extraction. |
| Owns (data) | extracted_fact, model_registry, model_run |
| Public surface | compiler.publish() (internal), review-queue endpoints |
| Depends on | ingestion (sources), llm_gateway (Bedrock), episode (task creation), medication |
| Requirements owned | DPC-003/004/007/008, DQS-004/011 |
| Constraining ADRs | ADR-006, ADR-009 |
| Key references | reference/testing.md §17.2 (thresholds), adr/ADR-009.md |
| Stage | Technology | Output | Failure Handling |
|---|---|---|---|
| Ingest | ingestion module, S3 put, fingerprint |
source_document row |
Duplicate fingerprint → no-op; malformed → DLQ + alert. |
| Parse | PDF/CDA/HL7 parsers; OCR (Textract) only for scanned uploads | Normalized text + layout spans | Unparseable → pending_manual, ops queue; never guessed. |
| Segment | Deterministic section splitter (regex + heading dictionary) | Sections w/ char offsets | Unknown layout → whole-doc window with lower gate thresholds. |
| Extract | Bedrock Claude (pinned), temp 0, JSON-schema tools, one section per call | extracted_fact rows (gate g1) |
Schema violation → 1 retry → reject; Bedrock outage → pause (§5.4). |
| Validate | Gates 1–7 below | gate_status per fact | Any gate fail → rejected or pending_review; never partial-publish. |
| Publish | compiler.publish() → plan items + tasks in one txn |
Tasks proposed→required |
Idempotent via natural keys (§6.3). |
| # | Gate | Concrete Pass Criteria | On Fail |
|---|---|---|---|
| 1 | Input qualification | Source is a recognized final/amended clinical document type; finality ≠ prelim unless cohort allows; language supported. | reject |
| 2 | Schema validation | Strict Pydantic parse of tool JSON; enums valid; required fields present; no additionalProperties. | retry×1 → reject |
| 3 | Terminology normalization | Medication → RxNorm CUI (RxNav local dataset); labs → LOINC; frequencies → structured sig. Unmapped → uncertain class, never guessed (DPC-003). |
pending_review |
| 4 | Deterministic clinical checks | Dose within RxNorm strength bounds; route valid for drug form; date sanity (within episode window ±60 d); duplicate-fact collapse. | pending_review |
| 5 | Source-span entailment | span_text must appear verbatim at span_start/end in the parsed source (exact offset check) and every clinical token in the fact (drug, dose, route, freq, date) must be present in the span. No span → no fact. |
reject |
| 6 | Materiality gate | Facts classified material (med change, follow-up, lab, safety instruction) require source_level ≤7; material + uncertain → pending_clinical_decision task, not an instruction (DQS-004). |
pending_review |
| 7 | Publication gate + DQS-011 | Deterministic semantic check: cosine(Titan-embed[pinned] of fact restatement, span) ≥ 0.83 and an LLM-judge NLI pass with temp 0; below either → human review queue. Patient-facing summary text is additionally diff-checked to contain no clinical token absent from published facts. | pending_review |
Safety invariant (non-negotiable). No AI output originates, modifies, or discontinues a clinical instruction. The only writers of
medication_plan_itemmutations are (a)compiler.publish()on fully-gated facts from level ≤7 sources, and (b) clinician confirmation endpoints with step-up. Prompt text and agent code have no code path to these writers.
{ "schema_version": "med_change.v1",
"drug_text": "spironolactone 25 mg", // verbatim from span
"rxnorm_hint": null, // model may NOT invent codes
"change_type": "new|changed|continued|stopped|uncertain",
"dose": {"value": 25, "unit": "mg"}, "route": "oral",
"frequency_text": "once daily",
"span": {"start": 1042, "end": 1107}, // char offsets in parsed source
"instruction_context": "start after discharge",
"confidence": 0.0-1.0 } // advisory only; gates decide
| Level | Source | Re-rankable by tenant |
|---|---|---|
| 1 | Final signed discharge medication list / finalized AVS | NO |
| 2 | Signed discharge summary | NO |
| 3 | Clinician in-app confirmation (step-up) | NO |
| 4 | Active EHR orders (MedicationRequest current) | YES |
| 5 | Official post-visit note / uploaded AVS (verified channel) | YES |
| 6 | Structured patient-reported outside appointment/med data | YES |
| 7 | Pharmacy/agent-verified evidence | YES |
| 8 | Consented transcript-derived proposals (Later feature) | NO (floor) |
| 9 | Unconfirmed machine extraction (pre-gate) | NO (floor) |
Conflicts: higher level wins; loser retained on the fact with superseded_reason; equal-level conflicts → DPC-007 pending-confirmation task routed to the previsit brief (L2 exception).
emit_fact.model_registry rows pin: model_id, prompt_version, extraction schema_version, safety_policy_version, embed model_id, thresholds, deployed_at, status. Every model_run (one per LLM call) stores registry ref, token counts, latency, and gate outcomes. Rollback = flip previous registry row to active (runbook RB-07, ≤10 min); nightly benchmark (§17.2) runs against the active and candidate rows so upgrades are evidence-gated.
pending_manual.pending_review twice daily during pilot; queue >20 or age >12 h pages ops.