Fleet Explainers
Five animated diagrams of how the fleet builds software: the task queue, one worker's life, a wave over a dependency graph, the reconcile barrier, and the whole machine end to end. They were built as talk visuals for the Fleet Series, and they're grounded in the real system — where a plate simplifies, the notes under it say so.
Each plate is a single self-contained page: no frameworks, no network, just SVG and CSS. Nothing autoplays — press play, or scrub the beat rail and step with the arrow keys. Every frame is a complete picture, so you can freeze anywhere. Plates follow your light/dark preference and respect reduced-motion. The ⚡ scenario tabs inject failures: gates going red, conflicts, escalations — the failure paths are the point.
A full text transcript of every plate's narration is included below each one.
Plate 1 — The task & the queue
State is a color. Claimable is a three-part AND — Ready ∧ auto ∧ unblocked — and blocking fails closed.
Four swim-lanes (Spec Needed → Ready → In Progress → Done), a handful of task chips, and the three-check claim gate. A task is claimable only when it is Ready and tagged for autonomous work and unblocked — and blocking fails closed: a task with a dependency that can't be resolved stays locked for the whole story.
Depiction notes: The three checks light up one at a time for teaching; in the real system they're a single predicate evaluated at claim time.
Plate 1 transcript
- Four states, four lanes. Every task lives in exactly one — and the only door between Ready and In Progress is the claim gate.
- Specs get written; tasks become Ready. Ready is necessary — not sufficient.
- A padlock means blocked:
auth-apidepends onauth-model, and a dep that isn't Done blocks its dependent. flag-cleanuppoints at a task that doesn't exist. Unresolvable? Still blocks. The gate fails closed — bad refs stop the fleet, they don't slip past it.- The claim gate asks three questions, ANDed — re-checked against a fresh read at the moment of claim.
auth-model: Ready ✓ · auto ✓ · unblocked ✓ — claimed. In Progress.auth-api: Ready ✓ · auto ✓ · deps all Done ✗ — it stays put. No wave will ever include it while the padlock is on.docs-passis Manual — and an unset mode counts as Manual: Ready ✓ · auto ✗. The fleet never claims it. A human will.auth-modelsurvives its gauntlet (plate 2) and lands: Done.- The dep is satisfied — the padlock comes off. Nobody recomputed a plan. The blocked flag just flipped, and the frontier moved by itself.
- Re-check
auth-api: Ready ✓ · auto ✓ · unblocked ✓ — claimed. auth-apilands. Done.- End state: two landed; one waits for a human; one blocks until someone fixes the ref. Claimable = Ready ∧ auto ∧ unblocked. That AND is the whole queue discipline.
Plate 2 — One worker's life
Edit in a sandbox, verify through an ordered gauntlet, commit on the host — and on any red gate, revert: green or it never happened.
One worker, one task, the whole gauntlet: clean working copy on the host, then edit → scope → build → lint → tests inside a sandbox, then commit back on the host. The rule that matters: revert-on-fail — green or it never happened. Two failure scenarios: a gate goes red, and a worker gets stuck and escalates to a human.
Depiction notes: Some machinery isn't depicted (skip markers, retry details, sandbox preflight ordering). The stuck scenario merges the no-progress guard with the attempt cap — at a cap of two they coincide.
Plate 2 transcript
Scenario: green run
- One Ready leaf, one worker. Everything in the dashed box runs inside an isolated sandbox container — the repo itself lives on the host, on the rail below.
- First check, on the host: is the working copy clean? The worker refuses to edit over uncommitted work.
- Clean ✓ — the task is marked In Progress. Now, and only now, does the LLM get to type.
- Into the container. The model edits —
opencode run, with the repo's durable lessons prepended — and produces a changeset. - Gate 1 — scope: 3 files ≤
max_files(5). A sprawling diff is a failure, not a style. - Gate 2 — build: the static check. This one runs always, whatever the task says.
- Gate 3 — lint (when the task requires tests): autofix, then re-check.
- Gate 4 — tests: the full suite, still inside the sandbox.
- Green across the board. The changeset leaves the sandbox and the host commits it —
auto: fix retry logic, addressed by a jj change id, not a mutable sha. - Only now — after the commit exists — does the status flip: Done.
- The invariant: green or it never happened. The task board never claims what the branch can't show.
Scenario: ⚡ fail a gate (failure injection)
- One Ready leaf, one worker. Everything in the dashed box runs inside an isolated sandbox container — the repo itself lives on the host, on the rail below.
- First check, on the host: is the working copy clean? The worker refuses to edit over uncommitted work.
- Clean ✓ — the task is marked In Progress. Now, and only now, does the LLM get to type.
- Into the container. The model edits —
opencode run, with the repo's durable lessons prepended — and produces a changeset. - Gate 1 — scope: 3 files ≤
max_files(5). A sprawling diff is a failure, not a style. - Gate 2 — build: the static check. This one runs always, whatever the task says.
- Gate 3 — lint (when the task requires tests): autofix, then re-check.
- Gate 4 — tests: the full suite, still inside the sandbox.
- Tests: RED.
- Any red gate, same move: the edit is thrown away on the host — revert — and the task drops back to Ready. Nothing survives.
- No half-work, no “mostly done”. The next attempt starts from a clean copy — and if it keeps failing the same way, a human gets it (third tab).
Scenario: ⚡ stuck → escalate (failure injection)
- Fast-forward: attempt 1 dies at the test gate and reverts. Its failure gets a signature — the reason, normalized: shas, paths, numbers stripped.
- Attempt 2 runs the whole gauntlet again… and dies with the identical signature. Two matching failures in a row = stuck.
- Stuck → escalate: Spec Needed + Manual. A valid terminal state, not a crash — the loop stops burning budget and hands the leaf to a person.
- The guard is deterministic code, before any model call: max 2 attempts per leaf per epic, and matching signatures short-circuit even that. Done resets the streak.
Plate 3 — A wave
Claimable lights up, a wave of at most 4 is drawn, leaves land, locks pop — repeat until dry. Serial runs 1 at a time; concurrent runs up to 3 at once, and only disjoint-scope leaves co-dispatch.
A ten-leaf epic as a dependency graph, built in waves: everything currently unblocked lights up, a wave is drawn, leaves land, padlocks pop, repeat until the graph runs dry. Two scenarios run the same epic serially (one in flight) and concurrently (three in flight) — same end state, very different wall-clock.
Depiction notes: A couple of compressions: graph nodes flip to Done at the landing beat as a montage, and padlocks pop at the wave boundary. One subtlety the plate gets right (verified against the real scheduler): the concurrency cap staggers workers within a wave — only a file-scope collision defers a leaf to the next wave.
Plate 3 transcript
Scenario: serial (1 in flight)
- One epic, ten leaves, dependencies drawn as they are. There is no layer chart — nobody computed one. Just chips, edges, and a
blockedflag. - Claimable lights up: Ready ∧ auto ∧ unblocked. Four qualify. The padlocked ones wait on deps;
docsis Manual;migratehas no spec yet. - The wave: up to 4 of the claimable —
auto+first, then priority. Four qualify, four drawn. That's the cap, not a layer. - Serial dispatch: one in flight.
schemaruns the plate-2 gauntlet against the main working copy, in thedxcontainer. - …green → the host commits.
schemais Done. One leaf, one commit, no merge step — nothing ran in parallel. parser, same lane. Two down.storefollows…- …then
bench. Four commits, strictly one at a time. - Wave gate: the whole-project suite on the accumulated state — hard. The ensemble review — advisory; confirmed findings become new fix-up leaves.
- Locks pop:
api,cli,metricsare unblocked — becauseDoneflipped, not because anyone replanned. The frontier advanced by itself. - Wave 2: three claimable, three drawn.
- One at a time again:
api…cli…metrics. Seven commits on the epic bookmark. - Gate again — then
uiunlocks (both of its deps are Done). Wave 3 is a single leaf. Waves come from the frontier, so they shrink and grow with it. uilands. Eight commits. Nothing claimable remains…- …and nothing ever will be: what's left is human-owned. The loop exits clean —
waiting_on_human, not an error. The rhythm: light up → wave ≤4 → land → unlock → repeat until dry.
Scenario: concurrent (3 in flight)
- Same epic, same frontier — now with
dispatch_concurrency = 3. - Wave planned: still 4 leaves. At most 3 run at once (
dispatch_concurrency = 3) — and only leaves with disjoint file-scopes co-dispatch. - Three fan out — disjoint scopes, each into its own jj workspace in an ephemeral
run-oncesandbox.benchsteps back: its predicted files collide with a sibling, sopick_disjointholds it — colliding leaves can't run together. Still Ready, next wave's problem. - All three finish green — in isolation. Three workspaces, three commits, zero integration so far.
- The reconcile barrier: one door, one commit at a time, in dispatch order. Parallel work, serial truth. (Plate 4 is this, slowed down.)
- Wave gate on the combined state — suite hard, review advisory.
- Frontier advances — and Wave 2 is four again:
api,cli,metrics,bench, in priority order. - Again three co-dispatch;
benchdefers again — its scope still overlaps a sibling this wave. Deferred by file collision, not by rank: the concurrency cap staggers work inside a wave, it never pushes a leaf to the next one. - Green in isolation → through the barrier, one at a time. Six commits.
- Gate →
uiunlocks → Wave 3: justuiandbench— disjoint at last, so both co-dispatch.benchfinally rides. - Barrier → eight commits. Dry of auto work.
- Same end state as serial, same barrier-guarded truth — just less wall-clock. Concurrency is an optimization inside the wave, never a different correctness story. Exit:
waiting_on_human.
Plate 4 — The reconcile barrier
Concurrent leaves finish green in isolation; a single serial door integrates them in dispatch order. Conflicts are data; losers are demoted; wave-mates keep going.
The centerpiece. Three workspaces finish in parallel, then integrate one at a time, in dispatch order, each rebasing onto the head the previous one produced: parallel work, serial truth. Conflict is a field you check, not an exception you catch — losers are abandoned and demoted, never allowed to hold their wave-mates hostage. The third scenario is the best one: every leaf green alone, the suite red combined, and a bisect walk finds the semantic conflict.
Depiction notes: This one tracks the real reconcile logic closely — including the deliberately linear bisect walk. A few rare error paths aren't depicted.
Plate 4 transcript
Scenario: clean integration
- Where plate 3 left off: three leaves ran in parallel, each in its own jj workspace, each through the full gauntlet — green in isolation, committed, marked Done.
- Now the only door. Integration is serial, in dispatch order, no matter how parallel the work was. The barrier admits one commit at a time.
head = base. First in dispatch order:retry-coreenters — alone. Its workspace commit was resolved to a change id first; shas go stale across rebases, change ids don't.jj rebase -r krxq -d head→ conflict? no. Landed.head = krxq.retry-clienters. It rebases onto the head the last leaf produced — a target that didn't exist until a moment ago. That is why the barrier is serial.- Clean.
head = wmpt. metrics, same door, same order.head = zsov.- ≥1 leaf landed, so the main working copy repositions onto the final head. The wave gate (plate 3) now runs the whole suite on this combined state.
- Parallel work, serial truth. The fan-out was an optimization; this ordered line of commits is the product.
Scenario: ⚡ rebase conflict (failure injection)
- Where plate 3 left off: three leaves ran in parallel, each in its own jj workspace, each through the full gauntlet — green in isolation, committed, marked Done.
- Now the only door. Integration is serial, in dispatch order, no matter how parallel the work was. The barrier admits one commit at a time.
head = base. First in dispatch order:retry-coreenters — alone. Its workspace commit was resolved to a change id first; shas go stale across rebases, change ids don't.jj rebase -r krxq -d head→ conflict? no. Landed.head = krxq.retry-clienters — it touched the samebackoffcode the first leaf just rewrote.- The rebase “succeeds” either way — jj records a conflict as data on the commit, not a thrown exception. Check the field:
conflict: true. - Conflicted → read the paths,
jj abandonthe commit, demote the leaf: Done → Ready. Journaled as areconcile_demotion— this wave will not re-dispatch it; the replan owns the retry. metricsdoesn't care. It enters —headis stillkrxq— and rebases clean onto it.- Landed.
head = zsov. The hole wherewmptwould have been simply closed up. - Two landed, one demoted — and the wave-mates were never hostage to the conflicted leaf. Main WC repositions onto the final head; the wave gate runs on what actually landed.
- Disjoint file scopes at dispatch time make conflicts rare. The barrier is what makes them safe. Optimization above, correctness floor below.
Scenario: ⚡ semantic conflict → bisect (failure injection)
- Where plate 3 left off: three leaves ran in parallel, each in its own jj workspace, each through the full gauntlet — green in isolation, committed, marked Done.
- This time all three integrate cleanly —
conflict: false, three times. Chain:base → krxq → wmpt → zsov. - Then the wave gate runs the whole suite on the combined state: RED. Every leaf was green alone — this failure exists only in combination, and nothing attributes it for free.
- Bisect-on-red (runs only when: suite red ∧ concurrency > 1 ∧ ≥2 landed). Walk the chain, re-running the suite at each point. At
krxq: green. - At
krxq + wmpt: RED. First red leaf found:retry-cli. - Back it out: rebase the rest off it (
zsovmoves down one), abandonwmpt, demote the leaf — same demotion, different trigger. - Suite on what remains: green. The wave continues with what is true together.
- Two barriers, one law: what lands must be green together, in one serial order.
Plate 5 — The whole fleet
Goal → architect → ★approve★ → task tree → wave loop on a private epic bookmark → epic gate → ★human merges main★. The loop never touches main.
The whole machine on one map, bookended by the two human gates: a person approves the framing before any code exists, and a person merges to main at the end. Everything in between — architect, task tree, wave loop, epic gate — runs on a private branch, and main receives exactly one commit: the human merge.
Depiction notes: The wave loop runs three turns and then reads "dry"; real epics can span many runs. The three seals at the epic gate abstract a cross-family review quorum. Commit ids are stylized throughout.
Plate 5 transcript
- It starts as a sentence: “retire the legacy import path.” No tickets, no tree, no branch.
- The architect (strong tier) goes first:
A0inventory the repo, thenA1— a framing proposal. Scope, risks, approach. Still no code. - ★ Human gate 1. A person reads the framing and approves it — or doesn't. Hard gate: nothing autonomous starts without this stamp.
A2decompose: epic → features → leaves, ≤3 deep, every leaf small enough for one worker.A3emit to Forge — idempotently.- A private epic bookmark forks. Everything that follows lives here.
mainis not part of this story yet. - The wave loop turns — plan → dispatch (serial or ≤3 concurrent) → reconcile → gates → replan. Turn one: three leaves land.
- Every turn ends at the wave gates: whole-project suite (hard) + ensemble review (advisory).
- Advisory findings don't block — confirmed ones become new fix-up leaves. The tree grows mid-flight; the next wave will pick this one up.
- Turn two: four more land — including the fix-up.
- Turn three: the last two. The tree is dry — and an epic can span many runs; the per-run wave cap just parks it resumable.
- Epic gate: the whole epic diff gets a full-quorum, cross-family sign-off. Hard, fail-closed — three model families must all say yes.
- ★ Human gate 2. A person — and only a person — merges.
mainmoves for the first time in this whole story. - Two human gates bookend the autonomy. No code before an approved framing; nothing on
mainwithout a human merge. Everything in between is the loop you now know — plates 1 through 4.




