Appendix A - 20. Orchestrator-as-reactor over an event bus
The Structure of Orchestrator-as-reactor over an event bus — its shape at a glance:
Emitters publish topics drawn from a closed registry; the orchestrator polls the queryable surface, matches each event to its playbook entry, and takes the prescribed action — the playbook is the active half that turns a signal into a reaction.
flowchart LR
Sub[Substrate emitters] -->|typed topic| Bus[(Event bus)]
Reg[(Const-string topic registry)] -.->|topics must be declared| Bus
Bus --> Orch{Orchestrator polls}
Orch -->|match topic → playbook| React([Prescribed action])
Accessible description: substrate emitters publish typed topics, drawn from a closed const-string registry, onto the event bus; the orchestrator polls the queryable surface, matches each event to its playbook entry, and takes the prescribed action.
Projected from the catalogue entry lifecycle-and-observability / Orchestrator-as-reactor over an event bus.
On this page: Intent · Motivation · Applicability · Structure · Sample Code · Consequences · Example use within DocAble · Related Patterns
Intent
Intent — A typed event bus with a closed, const-string topic registry and a companion playbook, over which substrate emits lifecycle/health events. The bus turns the orchestrator into a reactor over the fleet: it reads health from a queryable, self-documenting signal surface and reacts to each event with a playbook-prescribed response, which keeps a fleet of agents productive over long-running sessions instead of drifting into silent breakage.
Motivation
A fleet's health — is cron running, is the merge-train yielding, are tombstones stuck — is invisible without a signal surface, so degradation accretes silently (cron broken for hours before anyone notices). Worse, without a reaction loop the orchestrator is a passive observer: it can only steer the fleet if it reacts to what the substrate reports. The failure is silent substrate degradation and an un-reacting orchestrator, and it recurs continuously across a long session. The fleet slowly stops being productive while each dispatch still looks locally fine.
Applicability
- A typed topic registry (const strings + a lint) so topics are enumerable and typo-proof.
- Emit points wired into the substrate at the events that matter.
- A playbook keyed by topic — the signal is only actionable if each topic says what healthy and broken look like.
- An orchestrator that actually polls it on a defined cadence.
Structure
The Structure diagram appears at the top of this page.
Sample Code
Topics are a closed const-string namespace, so a typo can't silently create a dead topic that disables a signal; every topic must carry a playbook entry, so a raw signal becomes an actionable reaction. The reactor loop is: poll, match topic to playbook, act.
TOPICS = {"cron.tick", "merge_train.yield", "tombstone.stuck"} # closed registry — emit off-list = fail loud
PLAYBOOK = {
"merge_train.yield": {"healthy": "0-1 per session", "wrong": "repeated yields",
"do": "open the merge-train recovery playbook"},
"tombstone.stuck": {"healthy": "none queued", "wrong": "queued with no progress",
"do": "inspect the dedup event, clear the stuck closer"},
}
def emit(topic: str, bus: list) -> None:
assert topic in TOPICS, f"undeclared topic '{topic}'" # typo-proof by construction
bus.append(topic)
def react(bus: list) -> list[str]:
actions = []
for topic in bus:
entry = PLAYBOOK.get(topic) # a topic with no entry is un-actionable noise
if entry:
actions.append(f"{topic}: {entry['do']}")
return actions
Consequences
- Only as useful as playbook coverage. A topic without a playbook entry is emitted but not interpretable; the substrate-observability rule exists because that gap is the common failure.
- Consumption is discipline. The bus is Hard emission, but acting on it depends on the orchestrator honoring the poll cadence. The signal can be perfect and still ignored.
- Registry + emit-point maintenance. New topics need registry rows, emit wiring, and playbook entries kept in sync.
Example use within DocAble
- The event bus + its const-string topic registry.
- The event-bus playbook (topic → healthy / what-looks-wrong / response entry); the Observability-block requirement for any topic-introducing design doc.
- The session-start + post-cherry-pick monitoring cadence.
Related Patterns
- Consumer — the reaction half is an operational playbook: each topic's playbook entry is the situation-keyed procedure the orchestrator runs when the event fires.
- Enabler — cron-alerts-gate: alerts are derived events on this bus, promoted into a blocking gate.
- Counterpart — the Observability-block lint keeps every emitting substrate's topics documented (the playbook honest).
- See also (sibling) — agent-registry, deploy-heartbeats: the fleet's other signal surfaces.
- See also (a small flavor) — lifecycle-hooks §"the measured leash": a soft guidance hook that ships its own per-firing telemetry log + a yield query is this telemetry idea in miniature, a probabilistic mechanism instrumented so you can tell working quietly from silently dead.