A patient taps a kiosk and asks for help. A staff member replies from another screen. The same message may appear in the app, trigger a push notification, fall back to Short Message Service (SMS), and later show as read.

If each screen decides what those events mean, the thread turns into a rumor mill. One client marks the message as sent. Another treats a push attempt as delivery. A third retries after a network pause. In a clinic, that ambiguity creates audit gaps, extra staff work, and a patient expectation problem: sent must mean something actionable.

The rule I wanted was simple: every message has a durable state, every transition has an owner, and urgent communication can leave the app channel when policy allows it.

That rule is why I built BidirectionalMessagingService as the coordination layer for kiosk messaging, rather than treating chat as a screen-level feature.

The invariant comes before the interface