Implementing a spec in parallel waves
A coding agent works a 12-task spec one task at a time. Most of those tasks don't depend on each other — the work is a graph, not a list. wave-pilot schedules it like one, and gates every wave on a green build.
- AI agents
- Orchestration
- Parallelism
- Build systems
Hand a coding agent a twelve-task spec and it does the correct, slow thing: it works through the tasks one at a time. But look at the dependencies and most of those tasks have nothing to say to each other. The work is a graph; running it as a list leaves most of the parallelism on the floor.
wave-pilot treats the spec as the graph it actually is — and, just as importantly, refuses to let parallelism corrupt the result.
The two failure modes
| Approach | What goes wrong |
|---|---|
| Serial, one task at a time | Correct, but independent tasks wait on each other for no reason |
| All tasks at once | Fast, until two agents touch the same files and the tree is incoherent |
| Dependency-ordered waves | Run the widest safe set each step; integrate and gate between waves |
The win is the third row: at every step, run everything whose prerequisites are done — no more, no less.
Building the schedule
The orchestrator reads tasks.md, builds a dependency DAG, and assigns waves with a Kahn-style topological pass. Within a wave, tasks are launched in critical-path order so the longest chain starts first and never becomes the thing everyone waits on at the end.
DAG: T001 ─┐ wave 1: T001 T002 T004 (no deps) T003 ─┴─ T001 wave 2: T003 T005 (depend on wave 1) T005 ──── T002 wave 3: T006 … (depend on wave 2) T006 ──── T005Fan-out is the easy half. You spawn an agent per task in the wave and let them run.
The half that actually matters
The hard half is not corrupting the tree. Three things keep it honest:
- Isolation. Each agent works against its own view; results are produced, not committed in place.
- A merge controller. Results are integrated one at a time, deterministically, rather than racing into the same files.
- A per-wave build gate. A wave isn’t “done” until the integrated result compiles and passes. A wave that breaks the build stops the line instead of poisoning the next wave with a broken foundation.
That gate is the difference between “parallel and fast” and “parallel and trustworthy.” Without it you’ve just multiplied the blast radius of a bad change.
Where it pays, and where it doesn’t
It pays when a spec is genuinely wide — many independent tasks with a few long chains. It pays less on a deeply sequential spec, where the DAG collapses to a line and you’re back to serial (correctly so). And it asks something of the input: the dependencies have to be declared honestly, because a missing edge is a race waiting to happen.
The principle is the same one I bring to delivery generally: reach for leverage, but earn it with a gate. Fan out for speed; gate on a green build for correctness.