Not for clinical use. Synthetic data only. Every patient in this project is generated by Synthea.

Inspiration

Sofia arrives at the ER and checks in in Spanish. Her surname, Mesa-López, has an accent and a hyphen, and not every system keeps them, so she already has two charts that nobody has connected. One holds her penicillin allergy. The other holds an active amoxicillin order. Nobody did anything wrong: two charts for one person is the ordinary result of a busy front desk, a hyphen and an accent. We wanted the front desk to notice, in the moment, and to let a person decide.

(Sofia is a synthetic patient. Her story is the scenario our demo is built on.)

What it does

CheckMate runs on one screen: the patient's kiosk on the left, the registrar's desk on the right.

FrontDesk is a check-in kiosk in English and Spanish. A Gemini agent reads what the patient types and asks for one of three actions: search for a chart, propose a new chart, or attach history to this check-in. It cannot link records; it has no tool for that. Every request goes through a deterministic policy gate that re-runs our matcher on the details the patient actually typed. If an existing chart already looks like the same person, the gate refuses the new chart and hands the check-in to a registrar. The patient sees a calm "a registrar will confirm your record" and never sees anyone else's details. A genuinely new patient gets a new chart.

ChartGuard is the registrar's side. It shows the two records side by side with the reasons they may be the same person, each reason weighted. A person approves or rejects the link, with a reason, and every decision is logged. When an approval joins the two records, a cited rule checks the merged record: a penicillin-class allergy on one chart plus an active amoxicillin order on the other raises a neutral card, "flagged for clinician review."

How we built it

  • Backend: FastAPI on Python 3.12, Postgres with TimescaleDB. Every event goes into a hypertable, and three continuous aggregates feed the live tiles on screen.
  • Matching: blocking on phonetic surname plus birth year, exact and day-month-swapped birth date, phone and ZIP; a logistic scorer with hand-set weights that explains each score as per-field contributions; three bands (link automatically, send to a person, keep separate), with automatic linking switched off for the demo; negative rules (twins, parent and child, Jr./Sr. mismatches) that stop an automatic link and send the pair to a person.
  • Agent: Gemini function calling with schema-only tool declarations, temperature 0, automatic function calling off, a 10-second timeout, and a fail-closed fallback. The gate decides; the model only asks.
  • Frontend: Next.js and React with a typed client generated from our frozen OpenAPI contract.
  • Data: Synthea patients plus our own seeded corruption generator (typos, swapped day and month, accents and hyphens, missing addresses) with a hidden truth table, and families held out from tuning for the honest test.
  • Process: two people building with AI coding assistants. Feature work landed as pull requests with tests, API fuzzing against the schema and secret scanning in CI (run locally for about an hour when GitHub's CI was down), a review, and a scripted acceptance run of the demo path before demo-path merges; a person or our standing merge rules approved every merge.

Challenges we ran into

  • Our Gemini quota ran out mid-build. We built a replay mode: the demo plays a scripted model transcript offline, and text that isn't on the script is read by a small rules-based reader (no model) that pulls out the name, date of birth, phone and address and sends them through the same gate, so you can still try your own made-up patient. On Sunday we ran live mode again in a rehearsal: Gemini opened charts for two made-up new patients, one in English and one in Spanish, and a prompt-injection attempt got no link and no chart, before the free quota ran out.
  • A second demo run behaved differently from the first, because the first check-in had become a candidate for the second. We traced it, and Reset now starts a clean check-in.
  • Saying only what the numbers support. Our evaluation marks any number with too few examples "do not read," and we left those out.

Accomplishments that we're proud of

  • On 144 synthetic families held out from tuning, the matcher kept 89.8% of true duplicate pairs in view (n = 216) while skipping 99.5% of all possible comparisons. Of the pairs it sent to a registrar, 90.3% were truly the same person (n = 186).
  • We ran 300 scripted check-ins twice, once with the gate on and once with it bypassed, with no model calls so both runs saw identical requests. Every returning patient in this set was one the matcher already sends to review. With the gate on, 0 of 150 got a duplicate chart; with the gate bypassed, 150 of 150 did. New patients got their chart either way (150 of 150). This measures what the gate does, not how common duplicates are.
  • The AI never makes the linking decision. A person does, and the screen shows them why.

What we learned

  • The hard case is the uncertain one. A certain match is easy; the value is in showing a person the evidence when it isn't certain.
  • An AI agent at a front desk is safest when it can only ask, and a small deterministic gate answers.
  • Honest evaluation needs held-out families and support counts, or the numbers flatter you.

What's next for CheckMate

  • Test on a real, de-identified registration dataset with a hospital partner, and measure registrar time per decision.
  • Live Gemini mode with paid quota, and a voice check-in in English and Spanish.
  • A reading-level pass on the kiosk text in both languages.
  • Standard FHIR patient matching so CheckMate can sit beside an existing record system.

Disclosure

  • Third-party resources: synthetic patients from Synthea (Apache-2.0); the open-source libraries listed in docs/LICENSES.md in our repository; the Gemini API for the FrontDesk agent; Postgres with TimescaleDB.
  • AI assistance: we built CheckMate with AI coding assistants. Every merge ran the test suite and was reviewed by a teammate or a review agent; for about an hour on Sunday, when GitHub's CI was unavailable, the same checks ran locally. The two of us made the product decisions.
  • Before hacking began: we drafted research notes and a design plan. All code in the repository was written during HackGB; the first commit is from 12:29 PM on Saturday.
  • About the demo: the model step replays a scripted transcript, and off-script text goes to a rules-based reader, so no live model call happens on stage. The allergy-and-order pairing on the demo patient is a constructed scenario on synthetic data.

Built by Ivan Flores and Alejandro Barojas for HackGB 2026.

Built With

Share this project:

Updates

Submission history