How it works

Spec to a compiled plan — represented as a Petri net — running multiple agents


Brunch orchestrates multi-agent work: it turns a spec into a compiled plan, represents that plan as a Petri net, and runs multiple coding agents against it. The goal is to build code in parallel in a more predictable way — making explicit which steps can run at once, which must wait, and when a step counts as done.

One coding step is done; the rest are an unordered cloud joined by question marks — which can run at once, which must wait, and when does one count as done? Brunch's plan and net answer exactly these questions.

Spec → plan → net → build

Dependencies in the plan become arcs in the net, so the net already knows what waits for what.

Spec

What you produced — meaning, requirements, criteria.

Plan

Epics & slices with a depends_on DAG.

Net

Places, transitions, tokens. Deps become arcs.

Build

Each transition fires an agent in an isolated worktree.

Brunch turns a spec into a plan of epics and slices, compiles the plan's dependencies into a Petri net, then builds it by firing one agent per transition in an isolated worktree.

Tokens carry pointers, not code: what each step actually did goes to a log on disk, which keeps the net itself small and fast to reason about.

The plan, and the net it becomes

A plan is a list of epics and slices. Each slice names an exact change, a real test target, and what it depends on:

slices:
  - id: types
    definition: "Todo types in src/types.ts"
    verification: [{ kind: unit-test, target: tests/types.test.ts }]
    depends_on: []

When the plan compiles, each depends_on becomes an input arc in the net. A step's transition can only fire once a token has arrived on every input arc — so a service slice that depends on both store and validation waits until both have produced their tokens. The dependency graph you wrote in YAML becomes the firing rules that govern the run.

place + token · step donetransition · the work

service is a join — its transition fires only once both store and validation hold a token.

A Petri-net join. store and validation each complete and hold a token; only then does the service transition fire and service complete. A dependency in the plan becomes an input arc, so service waits for a token from both store and validation before it can run.

Two lanes

Every slice compiles to the same little subnet, with two lanes. One lane writes and runs the tests; the other checks the result is actually what the spec asked for. A step is completed only when both lanes are happy.

spec-readyevaluateready ↗ · needs work ↘

Mechanical lane · does it run?

needs-morewrite-testswrite-coderun-tests

↺ fail · rewrite  ·  ↑ pass · re-check

Semantic lane · is it the right thing?

done-specassess-semanticsatisfied

↩ rejected · back to tests  ·  ✓ satisfied · completed

completed

The semantic lane judges intent against the spec — not just green tests. A retry budget stops the step if it runs out.

Each step becomes a subnet with two lanes. The mechanical lane writes and runs the tests; the semantic lane checks the result matches the spec. The step is completed only when the tests pass and the work matches what the spec asked for; a retry budget halts the step if it runs out.

The mechanical lane asks does it run? — it writes tests, writes code, and runs the tests, looping on failure within a retry budget. The semantic lane asks is it the right thing? — it judges the result against the spec's intent (criteria, integration, reachability), not just a green test suite.

Passing tests aren't the whole story

A step is done when the tests pass and the work matches what the spec asked for. If the tests are green but the result isn't what the spec described, the semantic lane sends it back to the tests rather than marking it complete. The run-tests step spends from a retry budget; when the budget is exhausted, the step stops with a reason instead of looping forever.

A step runs write-tests then run-tests; even with every test green, the assess step checks whether the result is what the spec asked for. If it is, the step is completed; if not, it loops back to write-tests. A retry budget stops the step if it runs out.

How it runs as a net

Take the bundled layered-todo demo. Its slices form a small diamond and a downstream epic:

  • types has no dependencies, so it can fire first.
  • store and validation both depend on types, so they run in parallel once it's done.
  • service is a join — it can't fire until both store and validation have finished.
  • the cli epic (add, list, done, e2e) waits behind a cross-epic gate until the whole core is complete.
waitingrunningdone (token)▮ transition — fires when its inputs are ready
An animated Petri net of the layered-todo demo. types fires first; store and validation then run in parallel; service is a join that waits for both; finally the cli epic (add, list, done, then the e2e gate) completes. Each place turns amber while running and green with a token when done, looping left to right.

Watching the run in Petrinaut, you see tokens flow left to right: parallel where the plan allows it, holding at each join until its inputs are ready.

How it streams to Petrinaut

The link to the live view is one-way: cook runs the build and streams it; Petrinaut subscribes and renders.

execution authority

brunch cook

Runs the net, dispatches one agent per step, owns all state.

events · SSE
events · SSE

localhost

GET /stream

Ephemeral SSE server with a replay buffer, so a late joiner re-reads the whole run.

subscribe · read-only
subscribe · read-only

browser canvas

Petrinaut● live

Folds each event onto the marking and animates the live net.

brunch cook runs the build and streams every firing over a one-way localhost SSE server; Petrinaut subscribes read-only and renders the live net. The same events on disk replay the run with no server running.

cook stays the single execution authority — Petrinaut only renders, and can never drive the run. Because every firing is also written to petrinaut-events.jsonl on disk, the same run replays later with no server running at all.

Where this goes

Brunch is an active research prototype. The current direction includes:

  • Generating plans from more advanced specs, and in other runtimes.
  • Analysing the plan more thoroughly before running it.
  • Resuming stopped runs.
  • Adding more layers of governing agent behaviour.
  • Converging on a richer intent graph of the spec.

To see all of this in action, follow the Run guide.