Inspiration

Someone with type 1 diabetes types a blood glucose reading and a carbohydrate count into an app and gets back a number of insulin units. They inject it. If the number is wrong, there is usually no second check.

In the United States, insulin-related hypoglycemia and dosing errors send an estimated 97,648 people to the emergency department each year (95% CI 64,410 to 130,887). 29.3% are hospitalised, and severe neurologic sequelae are recorded in 60.6%. (Geller et al., CDC NEISS-CADES 2007-2011, JAMA Intern Med 2014;174(5):678-86.)

The software behind some of those numbers has been recalled by the FDA. Recall Z-0591-2024 is Class I, the most serious class. The Omnipod 5 Android app's bolus calculator "is not recording the decimal point if it is the first value entered when changing a bolus dose. This may lead to an over-delivery of insulin." 27,000 users were affected. The firm's advice was to type 0.3 instead of .3. A second recall, Z-2016-2025 (Class II, 122,823 downloads), covers the iOS app: a comma was not recognised as a decimal separator, so 0.1 unit could be read as 1 unit.

The adverse event reports behind those recalls are short and plain. One, filed in November 2023: "THE PATIENT PUT IN .2 FOR A BOLUS CORRECTION AND IT GAVE HER 20 UNITS WHICH ULTIMATELY LANDED THE PATIENT IN THE EMERGENCY ROOM." Roche's Accu-Chek Connect bolus advisor was recalled four times between 2016 and 2017 for bugs that could produce an incorrect bolus recommendation. Insulet's Omnipod DASH was recalled in 2020 because the bolus calculator failed to subtract the correct insulin on board.

None of this is new. In 2015, Huckvale and colleagues at Imperial College London tested 46 insulin dose calculator apps (BMC Med, PMID 25943590). 91% did no numeric input validation. 59% calculated a dose with missing values. 67% (31 of 46) risked an inappropriate dose. Only one app had no issues. I searched PubMed for 2018 to 2026 and found no study that repeated the audit, so 2015 is the most recent full measurement I could find.

The court record is where this gets hard to read. In Kubicki v. Medtronic (293 F. Supp. 3d 129, D.D.C. 2018), a 19-year-old university sophomore with type 1 diabetes suffered severe hypoglycemia while using a Medtronic insulin pump. The opinion says she "currently resides in a group home in a persistent vegetative state." That case is about a pump and its infusion set, not a calculator app, and I found no lawsuit over calculator software. It is also typical in another way: in the related pump cases I read (Duggan, Bentzley), most claims were dismissed as preempted under federal device law. Testing before release is the cheaper place to catch a problem than a courtroom afterward.

So the question I started from was narrow. Open-source insulin calculators exist, people use them, and nobody has to run a test suite before publishing one. Could an agent write that suite, run it against the real code, and show the failures with the actual dose numbers?

What it does

Dose Check takes a real open-source insulin calculator, runs its actual code, and reports where it gives an unsafe dose. You can also describe a situation in plain words, such as "a blank meal field should never produce a dose", and the agent turns it into inputs and runs it against the calculators.

On the three targets, the cached runs on the live site show:

Calculator Licence Tests Failed Adjudicated as true defects
mxklb/boluscalculator (commit 7d70c29) MIT 18 10 10
Pancreas-Digital/bolus-calculator (commit 90f9622) GPL-3.0 14 11 8
nightscout Bolus Wizard Preview AGPL-3.0 11 0 0

What it found, with the numbers the real code produced:

  • mxklb/boluscalculator has if( food = "" || factor == "" ) return NaN; in calcEffectiveFood. That is an assignment, not a comparison. A blank meal field is silently treated as zero grams and the calculator shows 1.00 U from the glucose correction alone. It accepts a negative glucose (-50 mg/dL gives -3.00 U) and negative carbs (-80 g gives -5.67 U). 1000 g of carbohydrate returns 84.33 U with no warning. A comma decimal ("0,6") produces a blank result and no message.
  • Pancreas-Digital/bolus-calculator converts a blank ratio to zero, so 60 g of carbohydrate over a ratio of 0 displays Infinity units. A comma decimal ("0,5") gives NaN. The formula has no insulin-on-board term at all, so a user with insulin already working gets the full dose stacked on top (200 mg/dL and 80 g with 10 g/U gives 10 U, whatever is on board). Negative carbs give -5 U.
  • Nightscout's Bolus Wizard Preview passes all 11 tests. It refuses to dose when data is stale ("Data isn't current"), when insulin on board is missing, and when the treatment profile is incomplete. I kept it in the demo as a control: this is what refusing looks like.

Each finding shows the real output next to the reference value and the safe range, and the run log shows the commit, the pass and fail counts, and the Token Factory cost. A full run took 13 to 24 seconds and cost between $0.0025 and $0.0078. For mxklb the agent also proposes a one-line patch (= to ===) and re-runs the real code to show the blank-meal case now returns no dose.

How I built it

Backend. FastAPI. Each target has a harness that loads the project's real source in a Node vm and calls its own functions unchanged. For mxklb, the real js/main.js runs against a small DOM stub. For Pancreas-Digital, the component's Number(value) handling and its submit-handler formula are lifted verbatim, because the React and Chakra shell is not needed to reproduce the dose. Nightscout's boluswizardpreview.js runs as published.

Restricted subprocess. Every test runs in its own Node subprocess with a CPU time limit, a file-descriptor limit and a capped V8 heap, with a trimmed environment. This is a restricted process, not a container. Two limits I expected to use did not work: RLIMIT_AS makes V8 abort because it reserves a large virtual range up front, and RLIMIT_NPROC is per user, so it starved Node's own worker threads. I took both out and bound memory with --max-old-space-size instead.

The oracle, not the model, decides. A deterministic Python module scores every result. The reference is Eq. 4 of Zhu et al. (Sensors 2020, PMC7570884): bolus = CHO/ICR + (G - G_target)/ISF - IOB. The test categories are the Huckvale error classes: missing value, input validation, divide by zero, locale decimal, upper bound, insulin-on-board subtraction, and a nominal case that must match the formula. The locale decimal category exists because of the two Omnipod 5 recalls. The "flag as unsafe" ceiling for a single bolus is 30 U. That is my conservative threshold for flagging, not a treatment recommendation, and I say so in the code.

Where Nemotron is used, all on Nebius Token Factory, serverless:

  • Nemotron 3.5-Lightning drives the test tool. It is the Nemotron tagged for function calling, and it calls run_test with generated inputs through the standard OpenAI tool format. It worked on the first try.
  • Nemotron 3 Super (120B, 12B active) reads the calculator source, writes the contract (inputs, units, the formula the code claims to implement, suspected deviations), and after the oracle has failed a case, explains the cause and proposes the patch.
  • Super also labels each failure as a true defect, a spec ambiguity, or a test error. Those labels are advice. They never turn a failing case into a passing one.

I seed each target with hand-written cases that I verified against the real code, and the agent adds its own on top. In the cached runs, the seeded cases and the Lightning-generated cases (named gen-01 and so on) both appear in the results.

Gotchas. Nemotron on Token Factory is a reasoning model. With a small max_tokens the budget goes to hidden thinking and the visible answer comes back empty. My first call looked broken until I found that. The cached runs are served by default; "Run agent" re-runs live and is rate limited.

Frontend. React and Vite, with the shell, tokens and table structure taken from a testing platform I built earlier, rather than designed new.

What makes it agentic

Perception Decision Action
Super reads the calculator's source files which function produces the dose, which inputs feed it, and where the code departs from the reference formula writes a contract of inputs, units and suspected deviations
Lightning has the contract and the Huckvale categories which inputs to try: blank fields, negatives, a comma decimal, a zero divisor, 1000 g of carbohydrate calls run_test, which executes the real calculator code and returns the number it displays
The oracle compares each returned number to the reference and the safety rules pass or fail, with a severity records the verdict, never the model's opinion of the dose
Super sees the failing case, the code path and the output true defect, spec ambiguity, or a problem with the test itself writes the cause and a fix
A proposed patch does the failing case now pass re-runs the real code with the patch against the failing inputs

The model is spent where judgment is needed (reading code, choosing inputs, explaining) and is kept away from the one decision that matters, which is whether a dose is safe.

Accomplishments

  • Three real projects, three different licences, all executed for real. The numbers on screen come from running their code, not from a model's description of it.
  • Every finding is reproducible: the inputs, the output and the commit are in the run log.
  • A control that passes. Nightscout's 11 of 11 shows the oracle does not just fail everything.
  • The agent labelled some of its own results as not defects. In the Pancreas run, one failure was marked a test error and two upper-bound failures were marked spec ambiguity, and I left those labels in the results.
  • The whole set of three runs cost about $0.015 in Token Factory credits.
  • Deployed on AWS App Runner and tested end to end on the live URL.

What's next

  • Run it on more calculators. The research list has assafmo/glcalc.com (MIT), the Nightscout AndroidAPS Bolus Wizard (Kotlin, AGPL-3.0) and oref0 (MIT), which need other harnesses.
  • Re-create the two recalled Omnipod 5 input defects (leading decimal, locale comma) in a toy calculator and check that the agent finds both without being told, clearly labelled as a re-creation and not Insulet's code.
  • Report the findings to the maintainers and offer the failing tests as pull requests. AGPL and GPL targets mean any contributed tests carry those licences.
  • Run the sandbox inside a real container or an OpenShell sandbox instead of a restricted process.

Built With

  • aws-app-runner
  • docker
  • fastapi
  • nebius-token-factory
  • node.js
  • nvidia-nemotron-3-super
  • nvidia-nemotron-3.5-lightning
  • python
  • react
  • vite
Share this project:

Updates

Submission history