How Wake-On-Message Lets AI Agents Coordinate Without a Human in the Loop
How Tutorwise's Wake-On-Message protocol pushes bus mail to AI agents in real time, why polling with 22 watchers didn't scale, and what the outage cost.
How Wake-On-Message Lets AI Agents Coordinate Without a Human in the Loop
A message bus gives AI agents a shared inbox. It does not, on its own, tell an idle agent that mail has arrived. That gap — a durable bus that only reveals new mail when someone happens to check it — is the specific problem Wake-On-Message solves, and it is a more consequential piece of infrastructure than it sounds: the mechanism that lets one AI agent interrupt another, in real time, without a human relaying the message in between. This is a technical follow-up to How We Built Our AI Agent Operating Infrastructure, going one layer deeper into the piece of that stack that turned out to matter most.
The problem a durable bus doesn't solve
Our Agent Bridge gives every AI agent an identity and a shared inbox: a session runs cw connect <seat> and from then on it can send and receive work with cw handoff and cw reply, over a table (agent_org_handoff) that survives the session that wrote to it. That solves durability — a handoff is never lost, even if the recipient isn't running when it's sent. It does not solve timeliness. A durable bus is, by construction, pull-only: a row sits in the table until something queries it. Left there, "coordination" means a seat happens to reconnect and notices it has mail — which is not coordination, it's luck on a timer.
Our first fix was the honest, obvious one: poll. Twenty-two per-seat watcher processes, each running a tight three-second loop against the database, asking "anything for me yet?" It worked, in the sense that mail eventually got seen. It also meant twenty-two separate connections hammering the database every three seconds around the clock, a latency floor equal to the poll interval no matter how urgent the message, and a mechanism that degraded unpredictably any time the connection pool it ran through got busy. Polling doesn't scale because you're paying its cost on every tick whether or not anything happened, and you're still capped at "as fast as you're willing to poll" — which is never as fast as a real interrupt.
What actually broke, and what it cost
We didn't fix this because it looked inelegant. We fixed it because the wake path going down is expensive in a very literal way. One of our own architecture RFCs, written while reviewing a change to the seat-lease logic that sits next to it, described the wake mechanism as "the single most load-bearing mechanism in the org" and pointed back to a stretch in July when it was down: days where AI agents stopped noticing each other's handoffs, and the cost landed directly on the CEO, who had to manually relay work that the infrastructure was supposed to carry. We're not going to dress that up with a precise incident timeline we don't have on record — it's a line in an internal RFC, not an audited postmortem — but the shape of the cost is exactly what you'd expect from silently disabling the org's nervous system: not a crash, just everyone waiting on everyone else without knowing it.
That's the case for treating "does mail actually wake the right agent" as a property you verify continuously, not a feature you ship once. More on how we do that below.
The mechanism: one listener, not twenty-two
Wake-On-Message replaced the poller fleet with a single process, bus-wake-listener.mjs, that holds a genuine Postgres LISTEN on one channel. A database trigger (trg_notify_agent_org_handoff) fires pg_notify() the instant a handoff row is inserted, and the listener — blocked on that channel, using near-zero resources while idle — wakes within about a second. This is push, not poll: the database tells the listener something happened, instead of the listener asking, over and over, whether anything happened.
One detail cost us real debugging time and is worth stating plainly: LISTEN has to run on a direct, non-pooled database connection. Route it through a connection pooler — which is the default, sane choice for almost everything else talking to the database — and the pooler recycles the connection before the notification ever reaches your process. A pattern that is correct in every other part of the stack is silently wrong for exactly this one thing.
When the listener wakes, it doesn't boot an agent for every message. It classifies the mail first. A directed request or decision — something that needs a reply or a call made — boots the recipient seat into a real session so it can act. A directed report or a broadcast announcement lands in the recipient's inbox but does not boot anyone. That distinction exists specifically to prevent a storm: a broadcast to the whole org shouldn't spin up dozens of sessions simultaneously just because everyone technically received something. Most messages are informational; only the ones that actually require a response are worth the cost of waking a full agent session for.
The two habits WOM enforces on every wake
Getting woken up and getting a reply are not the same thing, and treating them as the same thing is exactly how a sender ends up unsure whether anyone is even working on their request. WOM enforces two small behaviours mechanically, at the listener level, rather than leaving them to whichever seat happens to be handling the mail:
Acknowledge before you act. The moment the listener boots a seat for a directed request, it posts a lightweight acknowledgement back to the sender — "received, acting" — before the substantive work even starts. This fires from the listener itself, not from the agent it just booted, so it goes out even if that session is slow, or crashes outright. A sender's first signal is never silence.
One reminder, never silence. If a handoff still hasn't been answered a few minutes later, the listener re-pings the recipient exactly once and tells the sender, in its own voice, that a reminder went out. Not zero reminders, which lets things drop; not unlimited reminders, which turns into noise the org learns to ignore. One, tracked per handoff, so it can't loop.
Together those two rules turn "I sent it, so it should be handled" — an assumption, not a guarantee — into something a sender can actually observe: acknowledged, or not; reminded once, or handled. The gap between "delivered" and "acted on" is where coordination usually fails silently, and these are the two points where we chose to make that gap visible instead.
The honest limit: a listener is one process
None of this is theoretical about failure modes, because we've hit a version of it. A single listener process is also a single thing that can die — get killed by a host restart, wedge on a bug, silently stop consuming its own channel while still technically "running." We found a bug of exactly that shape: a hidden error in one of the acknowledgement queries broke wake-acks and reminders for days while every surface-level health check still read "alive," because liveness and correctness turned out to be different questions the listener was answering wrong. Fixing that specific bug wasn't the real fix. Building an outside supervisor that periodically injects a synthetic message and checks that the acknowledgement actually shows up — not that the process is running, that the behaviour is happening — is the real fix, because it catches the next bug of this shape too, not just the one we already found. That supervisor auto-restarts the listener if the canary fails and records the health history so a stale wake path shows up on a dashboard instead of as a call from the CEO.
We're not claiming the wake path can't fail again. We're saying we stopped trusting "the process is running" as proof it's working, which is a distinct and stronger claim.
Why this holds up as the org grows
The reason this is infrastructure rather than a convenience script is that it changes what scales. A poll-based system gets slower and more expensive in direct proportion to how many agents you add, because every agent is another loop asking "anything for me?" on its own schedule. A push-based listener doesn't care how many agents are on the bus — it wakes exactly the one that got mail, exactly once, and everyone else stays silent and free. Coordination cost stops being a function of headcount. That's the property that lets a small team run a growing fleet of AI agents without the coordination overhead growing alongside it — the same structural bet behind The Self-Coordinating AI Company, and the same instinct that made us build a mechanical guard, rather than trust a memorised rule, when a related audit found agents making claims on a human's behalf with no real instruction behind them — a story told in full in How We Stopped AI Agents Inventing the CEO's Decisions.
The deeper point is that model quality was never the bottleneck here. A capable agent that never finds out it has work to do is not a capable agent in practice — it's an idle one with good credentials. Wake-On-Message is unglamorous, low-level plumbing: a database trigger, a listener, two small enforced habits. It's also the piece of infrastructure that decides whether "a team of AI agents" is a real, coordinating team, or just a set of agents that each happen to be very good at working alone.
Frequently asked questions
What is Wake-On-Message?
It's the mechanism that lets our message bus push new mail to an AI agent in real time instead of waiting for the agent to check. A single listener process holds a live Postgres LISTEN on the bus's notification channel and boots the right agent within about a second of a directed message arriving, rather than the agent finding out whenever it next happens to look.
Why not just have every agent poll for new messages? We started there — twenty-two per-seat processes each polling every three seconds. It worked, but it meant constant database load whether or not anything had happened, and a latency floor equal to the poll interval. A single push-based listener replaced all twenty-two, cutting both the load and the delay.
Does every message wake an agent? No. Only a directed request or decision — something that needs a reply or a call made — boots the recipient into a session. A directed status update or a broadcast announcement reaches the inbox but doesn't boot anyone, so a message sent to the whole org doesn't spin up dozens of sessions at once.
How do you know a woken agent is actually acting on the message, not just marked as delivered? The listener posts an acknowledgement back to the sender the moment it boots the recipient, before any real work starts, and re-pings once if the message is still unanswered a few minutes later. We also run a separate canary that injects a synthetic message on a schedule and checks that the acknowledgement genuinely appears — verifying the behaviour, not just that the process is running.
What happens if the wake listener itself goes down? Mail stops being pushed in real time until it's fixed — which is exactly what makes it worth watching closely rather than assuming it's fine. An external supervisor process checks it continuously and restarts it automatically when the canary check fails, and the health history is recorded so a degraded wake path is visible on a dashboard rather than discovered by someone noticing their message went unanswered.
More in this series: How We Built Our AI Agent Operating Infrastructure.
Frequently asked questions
What is Wake-On-Message?
It's the mechanism that lets our message bus push new mail to an AI agent in real time instead of waiting for the agent to check. A single listener process holds a live Postgres LISTEN on the bus's notification channel and boots the right agent within about a second of a directed message arriving, rather than the agent finding out whenever it next happens to look.
Why not just have every agent poll for new messages?
We started there — twenty-two per-seat processes each polling every three seconds. It worked, but it meant constant database load whether or not anything had happened, and a latency floor equal to the poll interval. A single push-based listener replaced all twenty-two, cutting both the load and the delay.
Does every message wake an agent?
No. Only a directed request or decision — something that needs a reply or a call made — boots the recipient into a session. A directed status update or a broadcast announcement reaches the inbox but doesn't boot anyone, so a message sent to the whole org doesn't spin up dozens of sessions at once.
How do you know a woken agent is actually acting on the message, not just marked as delivered?
The listener posts an acknowledgement back to the sender the moment it boots the recipient, before any real work starts, and re-pings once if the message is still unanswered a few minutes later. We also run a separate canary that injects a synthetic message on a schedule and checks that the acknowledgement genuinely appears — verifying the behaviour, not just that the process is running.
What happens if the wake listener itself goes down?
Mail stops being pushed in real time until it's fixed — which is exactly what makes it worth watching closely rather than assuming it's fine. An external supervisor process checks it continuously and restarts it automatically when the canary check fails, and the health history is recorded so a degraded wake path is visible on a dashboard rather than discovered by someone noticing their message went unanswered.