cerulion graph run executes your graph as several OS processes — one per process group — running in lockstep. You get fault isolation for free: a node that crashes takes down its own group, not the whole robot.
Execution is identical either way: the workers stay in step at every execution level, so the run is byte-identical to running everything in one process. Multi-process buys isolation, not latency — nodes fused into one process still take the fastest path between each other.
What happens by default
The derived partition is the cost-aware optimum when
graphs/<name>.costs.yaml exists (written by cerulion graph profile), and otherwise a process-per-node baseline — maximum fault isolation.
Persisting a derived partition needs your consent
Declining is not aborting: the run proceeds with exactly the deployment a written file would have produced.
Ctrl+C is how you abort.
Choose the split deliberately
Two commands turn the default into a decision you own.Measure the graph
graphs/perception.costs.yaml — per-node median tick cost, per-edge fire rates, and a frozen compute budget. A node that never sampled enough fires is reported as such rather than guessed at. Each node’s fire target is derived from a warm-up observation of its own rate; --fires N overrides that with one uniform target for every node, which is what you want when a node’s first fire lands after the warm-up — a period longer than three seconds, say.A profile run is a measurement, not a deployment, so it stays local-only by design.Derive the partition
--dry-run to write (--yes skips the confirmation), and pass --budget-ns N to override the artifact’s frozen per-group budget.See what the runtime sees
process_groups: — which group covers which levels. It exits non-zero on an invalid partition, which makes it a useful CI check next to graph validate.Writing process_groups: by hand
block topic, because crossing a process boundary costs a real hop. A hand-written process_groups: block may split one, and the supervisor accepts the split when the edge is creditable — exactly one in-graph producer and no non-block consumers. It then mints that edge a cross-process credit word, a shared-memory page both ranks map, so the producer’s defer crosses the boundary without losing a frame.
When a worker dies
The supervisor’s default is to keep the robot up:
Under
continue, a cross-process block edge whose consumer’s rank died is called out specifically: the warning names the topic, the consumer input, the dead rank, and the producers now deferred behind a credit nobody will return — loudly once per regime, with a periodic re-announcement while it lasts, and a retraction if one of those producers turns out to have died too.
If you are chasing the last microseconds at level boundaries, CERULION_BARRIER_SPIN_US tunes how long a worker waits actively before it sleeps. CERULION_CREDIT_WAKE=0 turns off the credit wake plane that lets a credited producer park until the consumer drains — correct either way, you only lose wake latency. See Environment variables.
Recording a multi-process run
Every multi-process run keeps the record of what fired when, whether or not you asked it to record — that is what makes a run you never planned to record still re-executable afterwards.cerulion graph run --no-rings declines that bookkeeping on a memory-tight robot; because nothing captured without it could be re-executed, such a run takes no captures at all, and --no-rings is refused together with --record.
Next steps
Record and replay a run
Turn a run into an MCAP bag and re-execute it.
Backpressure and deadlines
The
block policy and the constraint it puts on partitioning.