Case study
MedLit: medical records, explained at your reading level
Patients can read their own medical records; most cannot understand them. MedLit turns a FHIR chart into cited plain-language explanations, and never lets the model touch a lab value. Built solo as graduate research, in the same months six companies converged on the same architecture.

The problem
Patients got their records. They didn’t get understanding.
Federal information-blocking rules require providers to hand patients their electronic health information, and portals satisfy the rule with raw data: “Essential hypertension (disorder), SNOMED 59621000,” medications by clinical name, lab values with no context. In the 2003 National Assessment of Adult Literacy, 36% of US adults scored at basic or below-basic health literacy. The channel that empowers patients also overwhelms them.
The obvious shortcut, pasting a chart into a general-purpose chatbot, has a measured accuracy problem: in a 2024 JMIR evaluation study, GPT-4 fully correctly answered only 46.7% of 30 patient lab-interpretation questions. The behavior is already mainstream: a West Health-Gallup survey in April 2026 found one in four US adults asking AI about health, and 11% of recent users reporting advice they believed unsafe. Any model can generate the explanation; the 46.7% is what happens when nothing checks it against the record.
The timing
Epic, OpenAI, Anthropic, Google, Amazon, and Microsoft all shipped patient-facing record explanation within months of this build. None of us were copying each other. The problem was ripe, and everyone landed on the same shape.
I brought full new guy energy to the spring. I legitimately thought I had cracked something nobody else saw, and six companies were landing on the same shape the whole time. Their launches erased the market for a standalone product and validated the architecture in the same quarter. It was graduate research; I’ll take the validation.
The dates are public now. OpenAI shipped ChatGPT Health on January 7, Anthropic shipped Claude for Healthcare on January 11, Epic rolled its Emmie assistant into MyChart, Amazon took its Health AI from One Medical members to amazon.com in March, Microsoft announced Copilot Health on March 12, and Google connected medical records to its health coach on March 17. All six ground answers in the patient’s own record, explain results in plain language, disclaim diagnosis, and keep physicians in the evaluation loop. Epic’s R&D director gave the reason plainly: grounding in the chart “makes a pretty big difference from an accuracy and safety standpoint.” That is the bet this architecture made in February.
What none of them ship
None of the six documents per-claim citations a patient can open; Microsoft is the only one that cites sources at all, and to vetted publisher content rather than the references behind each answer. None states that lab interpretation bypasses the model. None commits to a numeric reading-grade target or scores its output. None runs locally; every one of the six sends the record to a cloud provider, and for at least one, ChatGPT Health, that puts the record outside HIPAA, which covers providers and their business associates but not consumer apps (HIPAA Journal, 2026). And none has published accuracy numbers for the patient-facing product itself. A solo research build has none of their data, distribution, or clinical review, and all five of those decisions.
The approach
Three rules the system never breaks
Grounded in sources you can check
The patient's own SNOMED, RxNorm, and LOINC codes key retrieval from NIH MedlinePlus, FDA drug labels, and RxNav. The model rewrites the retrieved content for the reader; the claims come from the sources, and every response returns its citations.
Labs never touch the model
Reference ranges decide normal versus abnormal deterministically. A model cannot hallucinate a lab value into range because the model is not in that code path.
Reading levels measured on the output
Every output is post-scored with Flesch-Kincaid and Gunning Fog, so the interface shows the reading level the text actually has, whatever the prompt asked for.

01 · The record, readable
One patient, one page, plain language
A patient’s conditions, medications, and labs arrive as coded FHIR entries and leave as a structured summary a person can act on. Medications show the name from the pill bottle (Spiriva) via RxNav alongside the clinical name tiotropium bromide, interactions come from the FDA label, and each abnormal lab is flagged by its reference range.

02 · Receipts included
Every explanation shows its sources and its score
Each generated explanation carries a “Grounded in” panel linking to the exact MedlinePlus, openFDA, and RxNav references behind it, plus a readability badge computed on the output text. Toggle between Simple, Standard, and Detailed and the score updates, measured each time.

03 · Try it in one click
A demo that costs nothing to run
Eight synthetic patients with real clinical coding, a one-click demo session, and a pre-generated explanation cache, so the live site answers instantly and never touches an API key. The repo runs locally the same way: docker compose up, no accounts required.
Enter the demo →The pipeline
Retrieval is keyed by the chart’s own codes
A FHIR record already names what to look up. Every condition, medication, and lab carries a SNOMED, RxNorm, or LOINC code, and the federal health APIs accept those same codes as query parameters. MedLit passes them straight through. There is no embedding search and no vector database, so retrieval cannot fetch an article about a lookalike drug; it is exact by construction, and a citation is always about the coded entity in the patient’s chart. What that buys is a testable failure surface: if a citation is ever wrong, the code in the record is wrong, and codes can be checked at ingest.
Sourcing decisions the big launches never have to show
Drug interaction text comes from openFDA labels, and the reason says something about the ecosystem: the best free structured interaction API, RxNav’s, was retired in January 2024 when the DrugBank license behind it expired, and no public replacement has appeared. Condition-to-condition interactions have no public API at all; nothing free encodes “diabetes raises cardiovascular risk” as structured data. MedLit covers that gap by feeding the retrieved content for every active condition into one prompt and treating the result as synthesis, which is looser grounding than the rest of the pipeline and labeled accordingly.
By the numbers
01234567890123456789
sources
MedlinePlus, openFDA, RxNav, LOINC, and FHIR ground every claim
01234567890123456789
LLM calls
in lab interpretation; reference ranges decide
0123456789012345678901234567890123456789
errors flagged
plus 22 warnings, first validator run on ten patients; zero errors after cleanup
01234567890123456789
miscoded drugs
plus 4 stale codes, all caught before any user saw them
012345678901234567890123456789012345678901234567890123456789
ms cached
for a repeat answer; cold generation runs about 2 seconds
0123456789012345678901234567890123456789
dollars a month
total monthly infrastructure for the live system
What the build surfaced
The synthetic data was lying
During a review, a physician panelist noticed a medication card citing a topic for ticagrelor, a blood thinner, under a tiotropium label, a COPD inhaler. I went hunting for the bug in my pipeline. There wasn’t one. The synthetic record carried RxCUI 1116634 with the display “Tiotropium 18 MCG Inhalation Capsule”; ask RxNav what 1116634 actually is and the registry answers ticagrelor. The data was lying, and every system that trusted it was confidently wrong together. The fix became a validator that checks every coded value against its authoritative source. In the same small dataset it found a “morphine” that is actually remifentanil and an “oxybutynin” that is actually torsemide, plus four retired codes; one case came from Synthea output, two from bundles on the public HAPI test server. Standard tools, trusted defaults.
That validator now ships as fhir-code-lint: pip install it, point it at a directory of FHIR R4 bundles, and it checks every code against RxNav and MedlinePlus Connect, with JSON output and exit codes for CI. If a pipeline joins coded clinical data with external knowledge, lint the codes at ingest. The first bug is checkable right now, no page of mine in between: RxNav’s raw answer for 1116634 →
The evaluation first replicated a finding from Will and colleagues (JMIR, 2025), whose ChatGPT rewrites of patient education material settled at grade 7.6 when prompted for fifth grade: even prompted for 5th grade, Llama 3.1 8B floored near grade 7.7 (Flesch-Kincaid 7.7, 8.1, 9.4 across the three levels, monotonic, still above the Simple target). Then Groq decommissioned that model in August 2026, the default moved to GPT-OSS 20B, and the same nine-generation evaluation came back 5.1, 7.9, 12.6: every level inside its target band, no floor. The reading-level floor turned out to be a property of the model, and the instrumentation built to explore it is what caught the change.
Two designed comparisons never ran. The pipeline has three grounding modes (full, MedlinePlus-only, none) built to measure how grounding depth changes hallucination rate, and that ablation is still waiting; a multi-model comparison stalled on free-tier rate limits after one model. The evaluation that did run is small: nine generations, one model, automated metrics only. The harness and raw outputs ship in the repo, so both questions can be answered by anyone with an API key.
One program
MedLit is one of four artifacts in one research program, and the validator its wrong-code story produced is another. How each one forced the next is on the work page.
The receipts are public. So is the person who built them.
