Decision — long-running work on the Bus is an operation
Status: accepted 2026-11-03.
Why
Agents spend most of their time waiting: on a build, a merge, a reply from a person. When a Bus request stays open for the whole wait, the caller has to stay alive and connected for it, a broker restart or a lost connection turns the outcome into a guess, and the caller cannot do anything else meanwhile. The Bus broker is deliberately not durable, so the fix is not to make the broker hold the wait. The fix is for the service doing the work to hand back an id at once and keep the outcome itself.
The decision
A service command whose work can outlast a request (minutes rather than seconds) is an operation:
- Start. The command answers at once with
{op}, a string id unique to that service. A caller may supply the id itself so that a retry after a lost reply joins the same work instead of starting it twice. - States. An operation is in one of
queued,running,done,failed,cancelled,lostorunknown.done,failed,cancelledandlostare terminal and final.unknownmeans the service has never seen the id; it is not terminal, because a caller may register interest in an id before it submits the work. <svc>.op.done. When an operation reaches a terminal state, the service publishes one<svc>.op.donetopic, not retained, carrying{op, state, rc?, log?, reason?}and any service-specific fields. Every terminal state uses this one topic,cancelledincluded.<svc>.op.get {op}. Answers{op, state, ...}with the same envelope, at any time. A caller that was not listening whenop.donewent out uses it to catch up.<svc>.op.cancel {op}. Asks for the operation to stop and answers{op, cancelled, state}, wherecancelledsays whether the request changed anything.- Retention. The service keeps a terminal outcome for at least as long as
it documents (days, for a build queue), so
op.getstill answers after the caller has been away.
A caller watches <svc>.op.done and calls <svc>.op.get when it starts
waiting and after every reconnect, because op.done is not retained and can
be missed. The broker stores nothing for any of this.
Consequences
- An agent session can register interest in an operation, end its turn and
give up its process; the agent supervisor resumes it with the outcome when
op.donearrives (see ACP agent sessions on the Bus). The supervisor itself publishesagent.op.donewhen a session's job ends, so a parent waits on a child the same way. - A caller that restarts, or a broker restart, no longer loses an outcome:
it is one
op.getaway. - Services adopt this as their long commands are touched. A service whose
only long command already has its own status query may answer
op.get/op.cancelas aliases over it.
Not doing
- No durable queue, timers or delivery guarantees in the broker.
- No generic topic-pattern waits: interest is always in one operation id.