DEV Community

Cover image for The nurse and doctor sent the same message. Only one changed the chart.
siva surya
siva surya

Posted on Edited on

The nurse and doctor sent the same message. Only one changed the chart.

Lantern is an AI care team built on Switch: agents working alongside nurses, a doctor and room service across nine rooms of one hospital ward. Two agents have run on Switch so far, and two more run as code. Built for the Flip the Switch hackathon.

What you're watching. The agents in this video are rule-based stand-ins, labelled on screen. The real Switch path, with intake and care-coordinator running as Switch agents, passed this same case three runs in a row on Nov 4. The record service, checks and refusals in the video are real. Exactly what is real, below.

Nurse Ana typed the correct order. The right patient, the right change, the right reason, even the doctor's explanation, word for word.

Lantern refused her.

Moments later Dr. Lee typed the same words, character for character. The chart moved from version 3 to version 4, and the patient got an answer.

That is not a bug. It is the entire idea.

Same words. Only one counts.

Fictional patients, a simulated chart, human roles played by our team. Lantern is a hackathon prototype for Flip the Switch. It is not a clinical system and gives no medical advice.

Read the code · By Siva Surya Chandran

The most dangerous sentence on a ward

It is 6:40 in the morning. Ana's shift ends at seven. A patient has just told her bedside tablet:

"Can't wait for breakfast. Dr. Lee said it's fine now that my procedure moved."

Her chart says NPO: nothing by mouth.

Nobody here did anything wrong. A doctor said something reasonable, the chart has not caught up, and in twenty minutes a different nurse takes over. The patient may be right. The chart may be right. And the next person to walk in will not know there was ever a question.

The Joint Commission defines a hand-off as "a transfer and acceptance of patient care responsibility achieved through effective communication" (AHRQ PSNet summary). Read that again and stop at acceptance. Sending a message is not transferring a responsibility. Its alert on hand-offs lists wrong-site surgery, treatment delays, falls and medication errors among reported adverse events, and cites a 2016 study estimating that communication failures played a part in about 30% of malpractice claims (Sentinel Event Alert 58, PDF).

The world does not need another AI that writes things down. We built for the moment after: when the words and the record disagree, and somebody has to notice, the right person has to decide, and the next shift has to know who owns what's left.

What was said. What was confirmed. Who owns what's next.

We kept AI out of the room that matters most

Lantern is a ward built on Switch: nine private rooms where people and AI teammates share one history. The most important design decision we made was not a prompt. It was a membership list.

Nine channels. One shared shift record.

Who Their job What they can never do
intake (agent) Spots a possible mismatch and quotes the patient exactly Diagnose, or enter a patient room
care-coordinator (agent) Carries the case to the doctor with cited chart facts, briefs the next nurse Recommend a decision or change an order
interpreter, scribe (agents) Translate both ways, file dictated notes Turn a translation or a note into an order
record-bot (plain code) Opens cases, checks every claim, posts the ✓ Improvise. It has no model in it
Nurses Confirm, record medicines, hand off, accept Change an order
Dr. Lee Decides Decide outside the case thread
Room service Close tray and turnover tasks Touch the chart

The AI in this hospital is not allowed to say "resolved". Only the record service can. Agents notice and carry. People decide. Code enforces.

Case 17, step by step

1. The bedside. Patient 101's remark lands in her room. record-bot forwards it to nurse-station, keeping the original post id.

2. The agent notices, and the code checks the agent. intake posts a possible discrepancy against ord-diet-1 · NPO · v3. If its quote is not an exact substring of what she really wrote, the service throws the card away. An agent cannot put words in a patient's mouth.

3. A person clarifies. Case 17 opens. Only now does the patient get a fixed message that the team is checking. Ana replies CONFIRM Case 17.

4. Evidence, with receipts. record-bot opens the case in doctor-review and @mentions care-coordinator, which pulls chart facts through a logged lookup tool and cites each one. Then it says the sentence we are proudest of: "I am not recommending a decision."

A receipt proves the source, not the decision.

Here it is in the room. Note Ana's DECIDE at the bottom of the thread, which changes nothing:

doctor-review in the recorded run: the coordinator's cited summary, the verified receipts, and the nurse's refused DECIDE

5. The refusal. Ana's DECIDE fails the first check. There is no argument to win, no prompt to jailbreak, nothing to persuade. The chart stays at v3 and the refusal goes on the audit log.

Here is the check itself, trimmed from lantern/service.py. Edited posts and other rooms are filtered out before it runs:

def refuse(reason, tell=True):
    self._audit(post, "DECIDE", "rejected", reason)
    if tell and c is not None and c["doctor_root"]:
        self._say(f"decrej:{post.id}", DOCTOR, f"DECIDE ignored: {reason}.", c["doctor_root"])

if user.role != "doctor":
    return refuse("only a verified doctor can decide", tell=False)
if c is None or not c["doctor_root"] or post.root_id != c["doctor_root"]:
    return refuse("must be a reply in that case's doctor thread")
if c["state"] != CONFIRMED:
    return refuse(f"case is {c['state']}, not CONFIRMED_CONFLICT")
chart = self.chart(c["patient_id"])
if version != chart["chart_version"]:
    return refuse(f"stale chart version v{version}; current is v{chart['chart_version']}. Re-review and resubmit")
Enter fullscreen mode Exit fullscreen mode

No model runs anywhere in that path. Note tell=False: a nurse's refusal goes to the audit log with no reply in chat, which is why the screenshot above shows her post and no answer. The dashboard's "refused by code" count and Command audit are where it shows up.

Six checks between a message and the chart.

6. Advice is not a decision. Dr. Lee says: "She can have a regular diet today, her procedure moved to Thursday." Lantern files that as ADVICE, offers a draft DECIDE, and changes nothing. A sentence said out loud does not become an order until she sends it herself.

7. The chart moves. Her DECIDE passes all six checks. v3 becomes v4, and Patient 101 receives Dr. Lee's own words, attributed, never an AI's paraphrase.

Mission control: Patient 101 now at chart version 4

One decision, and the whole ward moves

The kitchen hears about it. The diet change opens a tray task in room-service. Ana tries DONE: refused. Sam posts DONE T-101-D17: closed, with his name on it.

The language barrier gets a witness. Patient 103 writes "El fisioterapeuta dijo que puedo sentarme en la silla para el almuerzo." The nurse sees the English beside the original, and the doctor's answer returns in Spanish, labelled as a translation, under the doctor's original words. It can be read aloud. It can never change an order.

Two entries for one medicine get flagged, not hidden. Acetaminophen recorded at 08:00 and again at 12:10 raises a possible double entry. Both stay visible. GIVEN is a record of what a nurse says happened; it never rewrites an order, and Lantern does not judge the dose.

A curious patient hits a wall. Patient 102 asks about Case 17 and is told Lantern only shares updates about their own care. Their account gets a 403 on the other room.

Mission control watches everything and can change nothing. Fourteen read-only panels: patients, cases, evidence, chart versions, medicines, room tasks, translations, reminders, tracking and the command audit.

Dashboard panels: medicines, room turnover, tasks, translations, reminders, tracking and command audit

Seven o'clock: where hospitals lose things

Ana posts HANDOFF. The coordinator briefs Ben on what's open. And then, nothing happens. The case still belongs to Ana, because Ben hasn't said so.

A handoff is not a handoff until someone says accept.

One word from Ben, and the owner changes on the record.

nurse-station in the recorded run: HANDOFF, the brief, ACCEPT, and the owner change

What is real in the demo

For demo purposes, we made rule-based stand-ins for the agents, and every frame of the video says so. It is not a live four-agent Switch run. intake and care-coordinator passed the full case as real agents in Switch Console, three times in a row, with refusals tested live, on Nov 4. That run was not recorded. interpreter and scribe have only run as code stand-ins.

Every guardrail lives outside the agents, so the same rules apply whichever agents are running.

In the video What it really is
record-bot posts, ✓ checks, refusals, chart changes, task closes, owner changes Real, from one run against a local Mattermost
Patients, nurses, doctor, room service Scripted, played by our team's driver
intake, care-coordinator, interpreter, scribe Stand-ins, rule-based, labelled on screen
Wispr bubbles Re-enactment, labelled; the commands were typed
Jira board and Google Calendar Real items we created by hand through our connectors for the demo. The dashboard's tracking rows come from a demo mapping file. Lantern's own Jira and Calendar hooks are tested against fakes only

The Lantern tasks on our test Jira board, with avatars blurred

The Lantern follow-ups in Google Calendar, with personal details blurred

A ward has no free hands

Every staff command in Lantern is one short, speakable line, designed for Wispr Flow, which works in any text box with no integration code: CONFIRM Case 17. HANDOFF Case 17 open: confirm she ate without nausea. REMIND Case 17 in 20 min check she ate. Whisper Mode fits a 7:00 handoff beside sleeping patients.

And voice changes nothing about power. Dictation is just a faster keyboard. A whispered DECIDE from a nurse is refused exactly like a typed one, because Lantern checks who posted, not how the words got there.

How Wispr Flow fits Lantern

Building it on Switch, step by step

This is what worked for us on Switch Console 0.40.1. Our Nov 4 run used Claude Code agents, and the steps don't depend on which coding agent you pick.

1. Start Console and chat as a normal user. Console runs a local server with Mattermost in Docker, on a port it chooses (we saw 64096), so set your MM_URL to match. Switch ignores the Mattermost admin account, so if your agent seems deaf, check who you're typing as.

2. Put room membership in one file. This is the first boundary, and it's a list, not a prompt. Our setup_mattermost.py script creates the rooms from it:

rooms:
  patient-101:   [patient-101, record-bot]
  patient-102:   [patient-102, record-bot]
  nurse-station: [nurse-ana, nurse-ben, intake, care-coordinator, interpreter, scribe, record-bot]
  doctor-review: [dr-lee, nurse-ana, care-coordinator, interpreter, scribe, record-bot]
Enter fullscreen mode Exit fullscreen mode

3. Create each agent in Console and add it only to its staff rooms. intake goes in nurse-station. care-coordinator goes in nurse-station and doctor-review. Their instructions are in docs/agents/.

4. Set who can address each agent, and don't stop at "Anyone". A new agent is owner-only, so our service account got "I only take instructions from my owner, and this chat account isn't linked to a Switch user yet." The obvious fix is "Anyone", and it's a trap: Switch also puts agents in Mattermost's default Town Square, where our patient accounts are members and can't be removed. So we used custom rules:

Agent Users Agents Rooms
intake any none nurse-station
care-coordinator any none nurse-station, doctor-review

After that, @intake from a patient in Town Square got: "You're not permitted to direct messages to me in this room — my operator has restricted who can address me here."

5. Test the bot path, not just the human path. A person's @mention waking an agent doesn't prove a service account's post will. scripts/check_intake.py posts three casual phrasings as the patient and checks that a case opens.

6. Let your service own the threads and the ids. Switch message ids (sw_...) aren't Mattermost post ids, and intake put the wrong one on a card, which the check rejected. We kept the check strict and gave the agent a plain-code lookup to copy the right id from. The service starts every case thread itself, and the agent's instructions say to reply inside that same thread, because care-coordinator once answered top-level and the thread check never saw it.

7. Expect progress posts and slow replies. "Starting a session..." and "Working... 13s" are real posts. A checker that answers every agent post can loop, so ours ignores them. Replies took 15 to 70 seconds and a backlog drains after a reset, so wait for agents to go idle before you record.

One thing we couldn't do: register a custom agent from a script. The Agent Protocol needs a registration key, and Console 0.40.1 had no page for making one, so record-bot is a normal Mattermost account using the REST API.

Take Switch away and you have a chatbot and a database. You'd lose the agents working under their own names in the staff's own threads, the case crossing from nurse to doctor in plain sight, and isolation that is a setting instead of a request.

Don't trust us. Check.

Claim Evidence
98 automated tests pass, no network needed recordings/tests.txt
A nurse's DECIDE is refused, the chart holds tests/test_must_pass.py, the dashboard's Command audit
Invented patient quotes are rejected tests/test_must_pass.py
Only room service closes a tray task The DONE check in lantern/service.py
Every cut, speed change and insert in the video recordings/final/cutlist.md

Run it yourself in under a minute:

git clone https://github.com/sivasuryachandran/Lantern && cd Lantern
python3 -m venv .venv && .venv/bin/pip install pyyaml
.venv/bin/python -m unittest discover -s tests   # 98 tests
.venv/bin/python scripts/demo_offline.py         # Case 17, no Mattermost needed
Enter fullscreen mode Exit fullscreen mode

What this doesn't claim

Lantern is not connected to a hospital or an EHR. Its clinical safety is unvalidated, and no nurse has reviewed it yet: we wrote the questions and ran out of time. That is the first thing we'd fix, followed by a recording with the Switch agents live.

One rule shaped all of it, and we would build it the same way again.

The agents are allowed to be wrong, because they are not allowed to decide.

#HowISwitch #FlipTheSwitch #Switch #WisprFlow #WeMakeDevs #DevToolsAcademy

Top comments (0)