This is a submission for the Hacktoberfest Open-Source AI Challenge Week 2: Side Hustle

Not for clinical use — synthetic patients. Not a medical device. OpenMedCheck is a concept demonstrator. Every patient in this post is invented.

What I Built

OpenMedCheck is an open-source prescribing check for emergency-department doctors. One proposed medication order goes in, together with the patient's record. One decision card comes out: NO_ISSUE, WARNING or BLOCKED, with the reason, the doctor's own words as evidence and, where the rule table allows one, a re-checked alternative. When nothing conflicts it says one line, "No issue found by these checks", and gets out of the way.

The problem it solves. In the ED, orders are written under time pressure, and the fact that makes a drug a bad idea is often only in free text:

  • a suspicion in today's note, written before anything is coded ("Suspected ACS"), or
  • one line in a long past medical history ("Upper GI bleed from a duodenal ulcer in 2021").

Prescribing modules mostly check drug pairs against coded data. They don't read the note. And the alerts they do raise are often overridden: a 2025 systematic review of drug-drug interaction alerts found no significant improvement in patient-important outcomes. We think the problem isn't too many alerts in general. It's alerts without the patient's context.

Two synthetic patients show the idea:

  • Bed 3: a 64-year-old man with chest pain, daily aspirin in the record, and "Suspected ACS; ECG and troponin pending" in today's note. The order is dexketoprofen, an anti-inflammatory (NSAID). Nothing is coded yet, so the rules alone say NO_ISSUE. Once Gemma reads the note, the card turns red (BLOCKED), quotes the doctor's words, and offers paracetamol 1 g every 8 hours, already re-checked. One click applies it: "No issue found by these checks".
  • Bed 5: a 66-year-old man with a wrist sprain after a bicycle fall; ibuprofen is ordered. Today's visit looks routine. But between the knee arthroscopy and the cataract surgery, his past medical history says "Upper GI bleed from a duodenal ulcer in 2021". Rules alone: NO_ISSUE. Once Gemma finds the line: amber (WARNING), the line highlighted where it was written, and paracetamol offered instead.

Who would pay. Not the ED doctor. The buyer is whoever owns medication safety in a hospital: pharmacy and medication-safety teams and clinical-informatics teams. They carry the cost of medication errors and of alerts that don't help, and they can't extend the checks inside their vendor's prescribing module. EHR and decision-support vendors could also embed it as an open, inspectable component.

The model is open core. The engine stays open source. A hospital would pay for:

  • a maintained, cited rule table, kept current as drug labels and guidelines change;
  • integration with its record system (HL7 FHIR, CDS Hooks);
  • support and the regulatory path.

This is a hypothesis I haven't tested with buyers yet.

Demo

Code

OpenMedCheck

Open-source prescribing check for emergency-department doctors. Flags a medication order that conflicts with the patient's labs, diagnoses, past medical history or free-text note — and stays silent when nothing conflicts. Deterministic rules decide; Gemma 4, running fully locally, can only escalate.

CI Security License: Apache-2.0 Python 3.12+ Status

Quick start · How it works · Configuration · Architecture · Contributing

🚨 Disclaimer

Not for clinical use — synthetic patients. Not a medical device. OpenMedCheck is a concept demonstrator built for the DEV Hacktoberfest Open-Source AI Challenge. It works on synthetic patients only, gives no medical advice, and must not be used for any real patient.

Table of contents

What it does

In one example: the…

Apache-2.0. Python 3.12, FastAPI, Ollama, Pydantic; about 1,080 automated tests.

How I Built It

Gemma 4 E4B, open weights, running locally through Ollama, on an 8 GB laptop GPU. The whole design rests on one split, with three parts that each have one job:

  1. Gemma reads. It reads the free text (the reason for consult, the past medical history, today's note) and answers a fixed set of questions in strict JSON. Is there a suspected ACS? Which medicines does the text say the patient takes? Which past events are recorded? Every answer must include the exact words from the text.
  2. Code checks. A plain program, with no AI, verifies each answer:
    • Are those words really in the text?
    • Do they name what Gemma claims ("mild" is not "MI")?
    • Are they denied ("ACS ruled out"), a relative's ("Father had an MI"), or an old event presented as current?

Anything that fails is dropped. This is what keeps a misread from becoming an alert.

  1. Rules decide. A fixed table of 25 if-then rules, written from drug labels (Spanish SmPCs) and published guidance, re-runs on the record plus the verified facts. Example: NSAID + suspected ACS + antiplatelet → BLOCKED. Every rule cites its source and carries a review mark. A clinical specialist reviewed 23 of the 25; the two past-history rules came later and are marked not reviewed.

The guarantee is escalate-only:

  • Gemma's own opinion can make a card stricter, never softer. A rules BLOCKED is final.
  • For the conflicts the rules decide, Gemma's opinion counts only if a rule of that kind fired.
  • Doses come only from the rule table, never from the model. An unreviewed dose shows as "Dose: consult SmPC".
  • If the model is down, slow or answers badly twice, the app still answers with the rules alone. A clean result then says "Free-text note not checked", because a check that couldn't read the note must not look like a pass.

Does it work? On an open set of 76 synthetic ED cases, Gemma reading the note lifted the match with the expected outcome from 72% (rules only) to 97%. Misses fell from 21 of 42 to 1, with 0 false alarms on the 34 cases that should stay silent. The median model step was 1.87 s. Caveats:

  • The cases, prompt and checker were built together, so this is a development set, not a clinical validation.
  • These figures predate the past-history step, which isn't measured yet. [HUMAN: replace with the prompt v8 run on all 89 cases, or keep this sentence.]

One lesson shaped the design. Early prompts asked Gemma to judge the conflict itself, and it kept attaching "NSAID with ACS" to whatever drug was ordered, including paracetamol. The fix was to stop asking the model what the code already knows. Gemma does what it's good at, reading messy text and quoting it, and the rules decide.

Process. I worked spec-first with Claude Code as the coding agent:

  • I explored the idea and wrote a spec with numbered requirements and acceptance criteria.
  • Each task was implemented test-first, with tests named after the criteria.
  • AI reviewers checked the safety invariant and the clinical wording on every change.

Full explanation: How OpenMedCheck decides.

Why Does Open Innovation Matter?

Here the open model isn't a cheaper option. It's the only option that works.

  • The data can't leave the building. An ED note is special-category health data under GDPR (Art. 9), and many hospitals won't send every order and note to a third-party model API. With open weights, Gemma runs on the hospital's own GPU (a 6.6 GB download). The app binds to 127.0.0.1; its only outbound call is to the local model; and nothing from the patient context is logged, stored or cached. A closed API would need a data-processing agreement and an outbound connection before the first test.
  • It runs on a laptop. Everything in this post ran on one laptop with an 8 GB GPU. A demo, a hospital pilot and a vendor evaluation can all start without cloud credits.
  • I could compare models freely. Because the weights are open, I ran the same 76 cases through three Gemma variants on the same machine. Gemma 4 E4B gave 97%. Gemma 3 4B gave 78% with 5 false alarms. Gemma 4 12B gave 80%, with 32 of 76 calls timing out on 8 GB. Swapping models is one setting (OPENMEDCHECK_MODEL); fine-tuning on a hospital's own note style is a possible next step.
  • It can be audited. A hospital can pin, inspect and re-test the exact model it runs, next to a rule table a pharmacist can read. That matters for something that may one day be regulated as medical-device software.
  • No per-call fee. Inference runs on hardware the hospital already owns, so cost doesn't grow with every order checked.

My Agent Session

I built this spec-first with Claude Code. The two sessions below show how the idea was shaped and specified before any code was written.

1. Ideation: from a rough idea to a zero-egress Gemma 4 prescribing check, with research, competitors and a go/no-go verdict.

Ideating PrescribeGuard AI: a zero-egress Gemma 4 prescribing-safety sidecar
You

/ideate

Agent
▸ Bash ls ~/ideas && cat ~/ideas/README.md
$ ls ~/ideas && cat ~/ideas/README.md
README.md, tcgrass — one idea so far (TCGrass, Go, 49/80)
▸ AskUserQuestion Which ideation mode? Shape / Explore / Revisit TCGrass / Compare
Which ideation mode? Shape / Explore / Revisit TCGrass / Compare
Shape a new idea

Asked for the one-liner, origin, first user, current alternative and goal.

You

[Pasted draft spec] PrescribeGuard AI (ER-Gemma Clinical Sidecar): a real-time, zero-egress clinical safety microservice for emergency departments. Evaluates one proposed prescription against labs (creatinine, eGFR), diagnoses, home meds and allergies; returns APPROVED / WARNING / BLOCKED with risk tier, ≤8-word headline, ≤2-sentence rationale, renal flag and an alternative. Dual layer: deterministic guardrails + Gemma. Three synthetic presets: (A) ACS + aspirin + dexketoprofen → BLOCKED; (B) eGFR < 30 + ciprofloxacin → WARNING; (C) clean baseline → APPROVED. NFRs: zero egress, self-hosted open weights, < 2 s, fail-safe when the model is down.

Show more
Agent
▸ get_challenges + list_my_mlh_events Check for matching DEV challenges and registered MLH events
Check for matching DEV challenges and registered MLH events
Hacktoberfest Week 2 'Side Hustle' (Best Use of Gemma) closes Oct 18; Week 3 opens Oct 19; registered for Hack Day Barcelona Oct 24

Flagged early: (1) regulation decides which idea this is — patient-specific prescribing advice is very likely a medical device under EU MDR Rule 11; (2) the differentiator is fewer alerts, not more detection; (3) clinical content needs review (preset A's 'coronary vasoconstriction' rationale, unsourced '80–90% bypass'). Round 1 questions: goal, first user, domain access, current alternative, time.

You

Goal: Hacktoberfest Side Hustle + Best Use of Gemma; a viable product deployed with fake patients, the challenge is the Gemma implementation. User: emergency physician / MIR resident in Spain. Buyer: hospital pharmacy, CMIO, private groups. A specialist can review 3–10 cases. Today: EHR prescribing modules on drug databases (Bot PLUS, Vidal, Medimecum) — context-blind pair tables, alert fatigue, no alternatives, free-text ED notes invisible.

Agent
▸ Agent (market-researcher) Alert-override evidence, MDR/AI Act, Gemma 4 vs MedGemma licence, Cloud Run G...
Alert-override evidence, MDR/AI Act, Gemma 4 vs MedGemma licence, Cloud Run GPU serving, competitors, Spanish EHR claims, training pivot, buyers
Overrides 49–96% (van der Sijs 2006), 46–96% (JMIR 2020), 64% in one ED (Yoo 2020). MDR Rule 11 → ≥ IIa, plausibly IIb. Gemma 4 is Apache 2.0; MedGemma terms forbid uses making Google a device 'manufacturer' → use Gemma 4. Cloud Run L4 GPU scales to zero; cold start ~19 s for a 4B model.

Key insight: the ED's free-text impression ('sospecha de SCA') is invisible to today's engines until coding at discharge — reading it at order time is where Gemma is genuinely needed. Proposed restating 'zero hallucinations' as an invariant: final status = stricter of rules and model; the model can escalate, never downgrade; rules-BLOCKED is final; alternatives re-checked by rules; schema-invalid output falls back to the rules verdict.

You

Framing: hospital CDS product. Deadline Sunday 22:00 CEST including post and demo. Model: Gemma 4. Infrastructure: analyse the best cost-effective option. Specialist review and testing is possible.

Agent
▸ Read guard-edits.sh Why were writes to ~/ideas blocked?
Why were writes to ~/ideas blocked?
The guard falls back to the session's repo (on main) when the target directory doesn't exist yet; creating the folder lets the notebook's allow-main marker apply.
▸ WebSearch Gemma 4 E4B Ollama Cloud Run GPU L4
Gemma 4 E4B Ollama Cloud Run GPU L4
Official Google tutorial: Gemma 4 with Ollama on Cloud Run GPU, tag gemma4:e4b

Infra comparison: (A) Cloud Run L4 + Ollama, scale to zero, internal-only ingress for the model — recommended, ~US$1/h while running (estimate); (B) A with min-instances=1 in judging week ~US$150–200; (C) Vertex AI endpoint — always on; (D) spot VM — too much ops; (E) CPU-only — breaks < 2 s; (F) hosted Gemma API — breaks zero egress. Wrote idea-brief.md, research-notes.md and draft-spec.md; preliminary score 50/80.

Agent
▸ Agent (devils-advocate) Pre-mortem for the next 27 h and judging; strongest objections; MVP cuts; sco...
Pre-mortem for the next 27 h and judging; strongest objections; MVP cuts; score check
Biggest risk: no preset needs Gemma — rules decide all three. Fix: put suspected ACS only in the free-text note so rules pass and Gemma escalates. Also: Gemma could suggest an unknown drug that passes by default → fixed drug list; rename APPROVED to 'No issue found by these checks'; cipro renal dosing is interval/CrCl-based; feature freeze Sun 12:00, post from 13:00. Revised score ~44/80, still Go for the challenge.

Verdict proposed: Go for the challenge build, not as a business (CE marking + slow hospital buyer). MVP: 3 presets with the free-text ACS case as the Gemma pitch, ~6 rules, escalate-only invariant, fixed drug list, internal-only Gemma on Cloud Run L4, tests, 60 s GIF, DEV post. Next: new-project --from-idea, then a lean /spec starting with a latency spike.

2. Spec-driven planning: requirements with acceptance criteria, the escalate-only design and the task plan.

Spec-driven planning for OpenMedCheck: an escalate-only Gemma 4 prescribing check
You

/spec

Agent
▸ Bash Read docs/discovery (idea brief, draft spec, research notes), docs/spec templ...
$ Read docs/discovery (idea brief, draft spec, research notes), docs/spec templates, ADRs, scaffold code
Idea brief verdict Go for the DEV Hacktoberfest Week 2 challenge; spec files are empty templates -> mode: New, pre-filled from the brief
▸ Bash git switch -c docs/spec
$ git switch -c docs/spec
Switched to a new branch 'docs/spec'

The idea brief covers most of product.md, so I'll pre-fill it and only ask about the gaps: budget cap and warm GPU during judging, specialist review timing, GCP project/GPU quota, and how judges reach the demo.

You

Budget: 25 EUR, scale to zero; leave infrastructure for last and start with local testing using Ollama. Specialist review: yes, today. GCP: at the end, first local testing with Ollama. Demo access: public, no login, capped.

Agent
▸ Write docs/spec/product.md
docs/spec/product.md
Problem, personas, goals G-1..G-4, success metrics, scope and non-goals, constraints, glossary, open questions Q-1..Q-5, assumptions A-1..A-3

Phase 1 written. Goals: one decision card per order reading the free-text note and labs; a tested escalate-only invariant (rules set the floor, the model can only escalate); self-hosted Gemma 4; submitted by Sun 22:00 CEST. Gate: do you confirm scope and non-goals? And does your machine have a GPU for gemma4:e4b?

You

Agree we don't deploy for the challenge, just leave the infrastructure documentation. We only focus on the product and local implementation.

Agent
▸ Edit docs/spec/product.md (goals, metrics, scope, non-goals, constraints, open que...
docs/spec/product.md (goals, metrics, scope, non-goals, constraints, open questions)
Local-only v0.1; GCP architecture documented, not deployed; EUR 0 cloud spend; new Q-6: do the challenge rules require a hosted demo?

Taking 'Agree' as scope approval. One question sets the latency NFR: what hardware runs the model locally?

You

NVIDIA GPU (>= 8 GB VRAM)

Agent
▸ Write docs/spec/requirements.md
docs/spec/requirements.md
REQ-001..REQ-014 with EARS acceptance criteria; NFR-001..NFR-012 (rules card p95 < 200 ms, final card p95 < 3 s warm, 10 s model timeout, >= 95% valid output, no patient context in logs, 127.0.0.1 only)

Phase 2 written. Must: one card per order, Layer 1 deterministic rules, Gemma layer with schema-constrained output, escalate-only invariant with property-based and prompt-injection tests, rules-only fail-safe, presets A/B/C, editable chart, progressive red/amber/green card, one-click alternative, safety wording, runs locally from the README. Gate: approve the Must requirements and NFR numbers?

You

Regarding GCP infrastructure at the end, leave the plan documented but not implemented.

Agent
▸ Edit requirements.md REQ-013, product.md scope
requirements.md REQ-013, product.md scope
REQ-013: reference architecture + step-by-step deployment plan marked 'Not implemented'; AC-013.3: no Terraform changes, no cloud resources

Done. Apart from that, do you approve the Must requirements and NFR numbers?

You

approved

Agent
▸ Write docs/adr/0002..0005 (FastAPI + Jinja2 + vanilla JS; escalate-only two-layer e...
docs/adr/0002..0005 (FastAPI + Jinja2 + vanilla JS; escalate-only two-layer engine; Gemma 4 E4B via local Ollama with JSON-schema output; Cloud Run + L4 reference architecture, documented only)
4 ADRs written as Proposed
▸ Write docs/spec/design.md
docs/spec/design.md
Components, data model, interfaces, check flow and invariant, security design and threats, failure modes, GCP reference architecture + deployment plan, testing strategy, traceability

Phase 3 written. Key point: the model can only choose an alternative from a per-request enum of allowed drug IDs and never supplies a dose; every alternative is re-checked by the rules. Gate: approve the design?

You

I approve

Agent
▸ Write docs/spec/tasks.md
docs/spec/tasks.md
12 tasks in 4 milestones: spike first (riskiest), app skeleton, rule table + review sheet for the specialist, combiner, model client and fail-safe, clinical review, UI, hardening, measurement, README, GCP docs (last), demo video and post

Phase 4 written; every Must requirement maps to a task. Gate: approve the task plan?

You

Approve

Agent
▸ Edit AGENTS.md (overview, stack, architecture, project rules, domain rules)
AGENTS.md (overview, stack, architecture, project rules, domain rules)
Updated
▸ Agent spec-reviewer: review docs/spec and ADRs for ambiguity, contradictions, invar...
spec-reviewer: review docs/spec and ADRs for ambiguity, contradictions, invariant gaps, traceability
READY WITH FIXES. Blockers: (1) the alternative re-check could approve another NSAID after a model escalation; (2) 'Sign order' enabled while the model check is still pending; (3) model free text could carry an unchecked dose or drug. Plus should-fixes: timeout budget vs retry, undefined strictness, narrow property tests, non-deterministic real-model test, config could break zero egress.
▸ Edit requirements.md, design.md, tasks.md, product.md, ADR 0005
requirements.md, design.md, tasks.md, product.md, ADR 0005
Alternatives keyed by (drug, conflict type) with same-class exclusion; Sign disabled while checking and stale responses dropped; model text with dose patterns/other drug names/banned words is invalid; one 10 s deadline across retry; strictness = status then tier; renal flag forces >= WARNING; full-ModelVerdict property tests; >= 9/10 real-model runs; loopback-only config guard
You

Work in parallel. Before starting with tasks we need to define project skills.

Agent
▸ Bash git commit (spec files and ADRs only)
$ git commit (spec files and ADRs only)
docs(spec): add OpenMedCheck v0.1 specification and ADRs 0002-0005

Proposed project kit: safety-invariant-reviewer and clinical-content-checker subagents; rule-table-change, model-eval and run-local skills; a Gemini glob rule for the check pipeline; AGENTS.md rules; narrow permissions with edits under infra/ denied.

You

okay, I approve

Agent
▸ Write .claude/agents (2), .claude/skills (3), .agents/rules/safety-invariant.md, AG...
.claude/agents (2), .claude/skills (3), .agents/rules/safety-invariant.md, AGENTS.md Project kit, .claude/settings.json
Frontmatter valid; settings.json valid
▸ Edit .claude/settings.json after an automated security review flagged wildcard all...
.claude/settings.json after an automated security review flagged wildcard allow rules
Narrowed to exact commands: uv run openmedcheck, ollama list, ollama ps, curl health check only; script runs and other curl calls now prompt

Project kit committed. Next: start Ollama with gemma4:e4b, then run T-001 (model spike) and T-002 (app skeleton) in parallel worktrees.

Prize Categories

Best Use of Gemma: Gemma 4 E4B runs locally through Ollama and reads the note and the patient's history. Its quoted findings drive the cited rules, under an escalate-only guarantee.