src/orchestrator/events.ts — run event bus + wire contract
Purpose
Section titled “Purpose”run() never calls a Logger directly: every log.X(...) emits a
RunEvent through busLogger onto an in-process bus; the terminal
renderer is just the always-on subscriber. An embedder attaches as
another subscriber with no orchestrator change (@vzn/vx-mcp reads run
history, not the bus). A bus handed in through RunOptions.bus keeps
the embedder’s subscribers across runs; what each run adds to it, the
run removes on its way out (orchestrator.md).
Public surface
Section titled “Public surface”createEventBus()— synchronous, order-preserving fan-out.busLogger(bus)—Logger-shaped facade that emits events.terminalSubscriber(sink)— drives a concrete renderer.WireEvent+toWireEvent— the SERIALIZABLE projection (task ids instead of node back-refs; bigint ns as decimal strings), andwireForwarder(send), the subscriber that pushes every event through it. Exported for an embedder that carries a run across a process boundary; nothing in core consumes it (the telemetry records intelemetry.tsare a separate projection).TaskView/OutcomeViewwithprojectNode/projectOutcome— the wire’s task and outcome shapes;RunStartInfois the run’s.- The outcome vocabulary, one copy for every surface:
outcomeWord(success/restored-local/restored-remote/up-to-date/failed/skipped/aborted),outcomeLabel(the word with a failure’s reason or a skip’s blocker),failedLabel,skippedLabelandskippedReason. The frame footer, the run report, the terminal summary and--summarizeall read these.
Invariants
Section titled “Invariants”- Fan-out is synchronous, so terminal output is byte-identical to the pre-bus direct-call era.
- Raw
TaskOutcomes are NOT serializable (bigint + graph back-refs); anything crossing a process boundary goes throughtoWireEvent.