02 / Patient Experiences
The lift is down. Nobody told the third floor.
A staff console that can tell every device in the building what is out of service, in every shipped language, within a deliberately closed set of things it is safe to say on a channel that cannot promise arrival.
Five things a hospital can say here. And three it deliberately cannot.
A closed set is not a limitation that was accepted reluctantly. It is what makes the channel translatable without a provider, renderable offline, and impossible to misuse under pressure, because there is no free-text field on the surface that matters most.
| Kind | What it means | Free text |
|---|---|---|
| SERVICE_INTERRUPTION | Something in the building has stopped working: a lift, an entrance, a service. | none |
| AREA_CLOSED | A wing, entrance or department is shut. | none |
| DEPARTMENT_BUSY | A department is under unusual load. An operational fact, never a clinical one. | none |
| CUSTOM | The one place a staff member can write their own words, translated at send time. | yes |
| RESOLVED | Stands a notice down. A notice that was raised must be closable by the same closed vocabulary. | none |
Every reader sees the scope printed on the notice itself, in their own language: Operational information only. This is not an emergency notification system.
The three that were removed
LOCKDOWN, EVACUATE and SHELTER
were deleted from the notice kinds. Not hidden behind a permission, not
disabled by configuration. They were removed from the type, so no
deployment can turn them back on and no operator can reach them under
pressure.
The reasoning is delivery, not squeamishness. This channel polls; it cannot promise a message arrives, and it certainly cannot promise it arrives in time. A channel with that property must not carry a message whose entire value depends on arriving. The free-text field is refused at the API boundary if it contains emergency wording, and the refusal names the overhead page instead.
The consequence, stated deliberately
Because nothing this channel now carries is time-critical, its delivery latency is an operational fact rather than a clinical-safety question, and no sign-off on that latency is required before deployment. That is the trade the closed set buys. It is not a substitute for an overhead page, the building's own emergency systems, or a person walking the room, and the software says so on every notice it renders.
Delivery
The message that has to be right is sent as a key, never as text.
A notice travels as an identifier. Every device renders it from its own shipped dictionary, in its own language, with no network call to a translation provider and no dependence on one being reachable. The one message that must arrive intact never leaves the building's own vocabulary.
Why not translate it
A translated sentence can be late, wrong, or absent. A key cannot: the string it renders shipped with the device and was reviewed before release. Notices are the surface where being late is worse than being plain.
Touches no reader data
Raising or standing down a notice reads and writes nothing belonging to any reader, a property pinned by a test, because it is the kind of thing that quietly stops being true during a refactor.
First on the console
The notice panel sits at the top of the staff console, above everything else, because it is the only surface that has to be right when somebody is in a hurry.
Free text is translated once, at send time, and never again.
When a staff member does need their own words, the message is translated into every shipped language at the moment it is sent, and each rendering is stored. Two things follow from that, and both matter more than they first appear.
First, what a reader saw is fixed and auditable. It cannot silently change later because a provider changed how it translates. Second, nobody stands in a corridor waiting on a translation API while the message they needed ten seconds ago is still in flight. A language that fails to translate falls back to the English source: worse to read, never wrong.
Announcement bodies and every per-language rendering are AES-256-GCM encrypted before they reach the database, not because they are personal information, which they must not be, but because an operator can type anything into a free-text box, and the at-rest posture should not depend on them never making a mistake.
Scheduled announcements
Recurring building-wide messages, with a run-level idempotency guard so two racing scheduler ticks cannot double-send the same run.
Read state
Acknowledgement counts, with the reach caveat rendered from the API payload itself, so the screen cannot drift from what the server believes it is saying.
Metrics
Announcements by category, notices raised and time-to-stand-down, scheduled runs and stale gaps. CSV export for whoever asks.
Retention
Expired announcements, their translations and their acknowledgements are purged on a configured schedule. The audit log keeps its own, longer clock.
Facility scoping
An announcement stamped with a different facility is never served, pinned by a test, because a multi-site deployment is where that goes wrong.
Acknowledgement is a reach estimate, not proof of anything. It stores an announcement id and a timestamp, and there is no column for anything else. Until 5 September 2026 it also stored a permanent identifier the reader's browser had minted, which was removed rather than renamed, so the count is now a count of acknowledgement events rather than of distinct devices. Treat it as an upper bound. Every screen that shows the number says so, in the same sentence as the number.
Stated plainly
The latency, admitted rather than buried.
The reader app polls. There is no push. Both feeds, operational notices and announcements, reach a device up to one poll interval late, and reach a device with no network, a flat battery, or a locked screen not at all.
A device whose polls keep failing backs off rather than retrying at full rate, because when the tunnel drops, every device in the building fails the same request at the same moment, and a synchronised retry storm is what stops the server coming back.
Poll interval
10s
While the tab is on screen. Polling stops entirely while it is hidden.
Backoff ceiling
80s
Where a device with repeatedly failing polls settles, instead of hammering a recovering server.
Push notifications
0
No install, no app store, no push. Both portals are plain responsive websites.
This is the whole argument for the closed set. A channel that cannot promise arrival must not carry a message whose value depends on arriving, so the messages whose value depends on arriving were removed from it. The staff console holds a persistent connection because it lives on a station screen; the reader app does not, because a phone in a waiting room cannot be relied on to hold one.
Seven panels. Nothing else.
The console is served through the public tunnel, so it holds only what a staff member needs for the building and for their own account. It cannot create, reset, or delete anyone else's account. That lives on a listener bound to the hosting device's loopback interface.
| Panel | What it does |
|---|---|
| Operational notice | Raise or stand down one of the closed set of kinds. Sent as a key, never as text. First on the screen, because it is the only surface that has to be right. |
| Message the building | Free text to every device, translated once at send time into every shipped language and stored per language. A failed translation falls back to the English source. |
| Scheduled announcements | Recurring facility-wide announcements, with a run-level idempotency guard against double-sends. |
| Sent messages | Everything this building has been sent, newest first, with the acknowledgement count and its reach caveat rendered from the payload so the screen cannot drift from the server's own claim. |
| Patient handouts | Publish a leaflet or notice anyone in the building can open on their own phone, alongside the announcement feed rather than inside it. |
| Reported by visitors | Problems people have reported with a place: a lift, a corridor, a sign. Grouped by place rather than by reporter, because there is no reporter to group by. |
| Metrics | Announcements by category, notices raised and time-to-stand-down, scheduled runs and stale gaps. CSV export, and the screen says on its face that these are operational and not clinical. |
The waiting-room QR is not on this list. It moved to the hosting device, with the reader's poster and the equipment code on separate pages so the one that gets printed for a public wall cannot carry the staff code.
Staff sign in with a local account or with Microsoft. The Entra application is registered in the hospital's own tenant, so their conditional access applies natively and they can revoke it without asking us. Local accounts are argon2id with revocable server-side sessions and a mandatory second factor. There is deliberately no account lockout, because an emergency department must never be locked out, so per-account backoff plus mandatory TOTP is the compensating control.
Next: the record of where the equipment has been.
Asset Tracking is the third module: QR labels, an append-only event log, and a record that says where a machine was last left rather than where it is. It is feature-complete and it has never run in a hospital, and the page says exactly what it has not had.