Lanes
A lane is an ordinary Overdeck conversation that a gauntlet orchestrator launches withpan lane start. It runs any harness, model and effort, in its
own working directory, and it nests under the conversation that launched it on
the Command Deck and the Agents page. A lane carries four facts on its row,
written once at launch: its parent conversation, its run key, its lane
key and its role. A gauntlet run is the set of lanes that share a run
key. There is no run table and no stored lane status: every state below is
computed when you read it.
Any conversation can launch lanes, whatever its harness, because
pan lane start is a shell command that reads $OVERDECK_CONVERSATION. The
pan-gauntlet-loop skill uses lanes to fan out builders
and critics.
Glossary
Commands
<lane> accepts a conversation id, a conversation name or conv-<name>. A lane on
another machine has no id here: name it by its conversation name.
--machine <label|environment-id|auto> runs the lane on that linked machine, or on the
one Auto placement chooses for auto (--needs <tag,…> limits the choice). A lane on
another machine is messaged with pan tell @<label>/<name>. See
Lanes on other machines.
Messages to a lane go through
pan tell conv-<name> "<text>"; there is no lane
verb for it. A lane on another machine is pan tell @<label>/<name> "<text>".
pan tell refuses a critic or verifier lane that already filed its
done report, because a critic is never re-tasked (--force overrides).
The HTTP API behind the CLI is POST /api/lanes (with machine and needs),
GET /api/lanes?run=&parent=&key=, GET /api/lanes/:name,
POST /api/lanes/:name/stop and POST /api/lanes/:name/reap with { park, keep }.
Exit codes
wait and report use the pan worker exit codes:
start, stop and reap exit 0 on success and 1 on any refusal.
disk-check exits 0 when there is room, 4 when the floor refuses, and 1 on a
usage or lookup error.
Lanes on other machines
--machine <label|environment-id|auto> runs the lane on a
linked machine: your desktop, your laptop, or a
linked Fly machine. auto lets Auto placement
choose by load; --needs <tag,…> limits it to machines with those capability tags
(for example --machine auto --needs docker for a critic that runs a stack). Naming
this machine launches here as usual.
The work is split between the two machines:
- This machine (home) keeps every rule that needs the run’s lanes: the parent and
launcher rules, the run, the critic link and its default
--at, the iteration, one live lane per run, key and role (--replacestops a lane on any machine), and the model, harness and effort from this project’sgauntletconfig. It then keeps a record of the remote lane under~/.overdeck/remote-lanes/<name>/and copies its reports into~/.overdeck/agents/conv-<name>/reports/, solist,show,wait,stopandreapwork from here. - The other machine (the owner) creates the lane’s directory under its own lanes
root, in its own project for the same repository, writes the brief, and starts the
conversation. It runs its own CPU check (
--forceoverrides it) and checks the harness.pan lane reportinside the lane works as it does here.
listandshowprint the conversation name inIDfor a remote lane, and the machine inMACHINE((offline)while it cannot be reached). The JSON hasid: nullandmachine: { environmentId, label, reachable, lastSeenAt }.- The
startJSON hasplacement: { label, reason, rejections }when Auto chose the machine, andnullotherwise;machineisnullwhenever the lane runs here. waitkeeps waiting while the machine is unreachable: a sleeping laptop delays a report, never fails the wait (it times out with exit 3, never exit 2).- A critic or verifier on another machine judges a commit that is on
origin: the builder must push first, or the launch is refused with 400. --reusecontinues on the newest lane’s machine, so it takes no--machine.- Orchestrator lanes run on this machine only.
- A Fly machine must be linked first with
pan machines add-fly --project <key>, and runs Claude Code and Codex lanes only. It is destroyed afterremote.fly.machine_idle_minutes(default 30) of idle conversations, so raise that for runs with long waits. - The Command Deck nests a remote lane under the orchestrator that launched it, and
the Awareness rail counts it in the run card. The row also shows the lane’s report
badge (
DONE,BLOCKED,FAILED) and, for a critic or verifier, its verdict; a builder’s verdict link opens its newest critic on whichever machine it ran. The group summary counts remote lanes inreportedtoo. - A request that arrives from another linked machine may not place a lane on a third
one (403
no-chained-hops).
Who may launch what
The door judges the effective launcher: the launching conversation, or, when that is a successor, the row reached by following parent links until a lane or a root. So a handed-off orchestrator keeps its standing.--runis required from a root; a lane launched by an orchestrator lane inherits that lane’s run.- A builder, critic or verifier lane launches nothing.
- A new builder iteration cuts its branch from the previous iteration’s branch
(the one its newest report recorded);
--fromoverrides the base. A branch that reporteddoneis frozen. --reuselaunches a fresh conversation in the directory of the latest earlier lane with the same run, key and role, keeping its iteration; that lane must not be live.- One live lane per run, key and role: a second launch is refused unless
--replace, which stops the live lane first. A lane that is still starting (not ended, created under 3 minutes ago) counts as live. --effortacceptslow,mediumorhigh.- With no
--model, the door uses the role’s model from the project’sgauntletconfig; with none there, the launch is refused. There is no hardcoded model fallback.
Derived state
ACTIVITY in pan lane list and activity in the API follow this table.
The first match wins.
Git facts (branch, head, commits ahead of upstream, dirty) are read for
git-backed lanes that are not archived.
This table is implemented once, as
laneActivityOf in @overdeck/contracts;
pan lane list, GET /api/lanes and the Awareness rail all use it.
In the Awareness rail
The Command Deck’s Awareness rail shows a gauntlet run as one card, grouped by project and run key. Lane conversations do not get cards of their own.- Counts line: role counts, then activity counts, only when non-zero, for
example
6 builders · 6 workingor6 builders · 1 needs you · 5 working. - Latest event: the newest lane report, failed start, end or launch, for
example
alpha critic i2 reported done · verdict pass. The card’s time is that event’s time, not transcript activity, so a busy run does not keep jumping to the top. - Click opens the conversation that launched the run.
- Expand lists the lanes, each critic or verifier under the builder it
judges, and reads
GET /api/lanes?run=<run>once for git facts and archived lanes. It does not poll.
Reports
A lane ends its work withpan lane report --file <markdown>:
--status done(default): the brief is complete. A builder may reportdoneonly from a clean tree whose branch has an upstream holding every commit;--allow-unpushedwaives the upstream check, never the clean-tree check.--status blocked: the lane needs a decision. Start the report withRULING,SPEND,ONE-WAYorBLOCKED.--status failed: the brief cannot be done.
pan lane report outside a lane (including in a successor of one) fails with
not inside a lane.
Reap
pan lane reap <lane> never kills a process and never discards work:
- It stops a live lane through the conversation stop door, and refuses if the harness is still alive after 30 s.
- It refuses a directory outside the project’s lanes root.
- It refuses, listing each PID and command line, while any process has its working directory inside the lane directory. Stop those processes through whatever started them.
- It refuses a dirty worktree unless
--park. With--parkit saves the uncommitted work (tracked changes and untracked files) to~/.overdeck/agents/conv-<name>/parked-<timestamp>.patch, verifies that the patch reproduces it, and only then cleans the tree. - It warns about commits not on the upstream; the branch is always kept.
- It runs
git worktree remove(never--force) andgit worktree prune. Aplaydirectory is removed only when it holds no.git. - It archives the lane’s conversation so a finished run leaves the Command
Deck.
--keepleaves the ended conversation listed instead. If archiving fails, the reap still succeeds and printsreaped, but archiving failed: <error>; archive conv <id> from the Command Deck.
~/.overdeck/agents/conv-<name>/ survive the reap, and archived lanes stay in
pan lane list and GET /api/lanes with activity: stopped, so a run’s
ledger stays complete. Reap builders before the orchestrator lane that launched
them: an archived orchestrator lane leaves its unreaped builders as orphans.
A lane is reapable — a candidate for pan lane reap right now — when a
newer iteration of the same run, key and role exists, no row of its own
iteration is working, needs-you or starting, and either some row of that
iteration filed a report or none is idle. Orchestrator lanes are never
reapable: archiving one orphans the builders it launched. Being reapable is a
candidate, not a promise — the reap itself can still refuse (a live process in
the directory, a dirty tree without --park).
pan lane list --run <run> --reapable lists the candidates; reap each with
pan lane reap <name> (--park for a dirty tree) before launching more lanes
in the run. pan lane wait prints the same names on stderr right after a
report arrives, so the loop is: wait, reap whatever it names, launch the next
iteration.
Disk floor
A lane launch is admitted against the same 10 GiB safety floor as a workspace start (PAN-4961), at the project’s lanes root: below the floor,start
refuses with HTTP 507, code: disk-low, and the Disk low — new workspace starts are held text naming free, reserved and needed GiB. --force does
not bypass it — it still bypasses only the CPU check. A lane that creates a
new worktree (a builder, critic, verifier or orchestrator without --reuse)
reserves the project’s workspace-start estimate with a 2 GiB minimum; --reuse
and play lanes reserve 0 bytes but are still checked. A launched lane’s
reservation keeps counting for 15 minutes after the door returns, while it
checks out and installs, so a burst of launches cannot each pass the floor
before any of them has written. A lane on another machine (--machine) is
checked by its owner, against the owner’s own lanes root; home runs no disk
check for it.
The lane door sees only lanes: a worktree you create yourself with
git worktree add, or through Agent-tool worktree isolation, is invisible to
it and to pan lane reap. Run pan lane disk-check --project <key> before
every launch wave and before creating one of these worktrees yourself; with
--path <dir> it checks the filesystem holding that directory instead (the
current directory by default). It exits 0 with a Disk ok: … line when there
is room, 4 with the refusal on stderr when there is not, and with --project
it also lists directories under the lanes root that no lane row claims — not
lanes, so pan lane reap cannot remove them; remove each with
git worktree remove <dir> once its work is merged or saved.
Critics and builders
A critic or verifier lane records the builder row it judges, once, at launch.-
--for <builder key>names the builder. It is required for a critic and optional for a verifier (a verifier sometimes judges a whole run rather than one builder).--keydefaults to--for; a different--keyis refused, as is a--forwith no builder of that key in the run. -
Without
--at, the critic is checked out at the head of the newestdonereport of the judged builder iteration. When that iteration has no done report, the launch is refused:builder <key> i<n> has no done report; pass --at <sha>. -
A critic or verifier files exactly one verdict, with its done report:
The verdict is
WOWED,IMPRESSED,NOT_YET,PASSorDEFECTS. The defect count defaults to the length of the verdict file’sdefectsarray. A done report without a verdict,--verdicton another role or on a blocked report, and a second done report from the same critic are refused. -
Pairing is by iteration. The critics of builder iteration n are the
critic and verifier lanes of the same run and key whose judged row has
iteration n, so a
--reuserespawn shares them. A builder of iteration n ≥ 2 is told the verdict it answers: the newest critic of iteration n − 1 created before it, named by conversation id, verdict and verdict file. -
pan lane show --run hotel --key 663prints the chain, for examplei1 built → critic c1: NOT_YET (7 defects) → i2 built → critic c2: WOWED, then one line per lane:#<id> <role> i<n> <activity> <verdict> <verdict file>.pan lane show <lane>finds the run and key from any lane of the chain. -
pan lane listshowsFOR(<key> i<n>, the judged builder iteration) andVERDICT(a critic’s verdict, or a builder’s newest critic verdict, with the defect count, as inNOT_YET 7). - Blindness is preserved. The link changes nothing a critic is told: its lane contract and brief contain neither the builder’s conversation name nor any text of a builder report. Only the commit it judges comes from the builder.
Handoffs and succession
pan handoff and pan fork record the same parent link on the successor, so a
handoff chain renders as a tree. When an orchestrator hands itself off, the
lanes it launched stay nested under it, and the successor nests beside them.
The successor shows continues ← #<id>, keeps launching in the same run with
the same --run, and sees every lane of the run with pan lane list --run <key>.
A successor is never a lane: continuing a lane’s own work in a fresh
conversation is pan lane start --reuse, not pan handoff.
pan handoff --fresh is refused (400) when the source is a lane or any
conversation in a lane’s successor chain: a --fresh successor has no
parent link and shows no continues ← link, which would silently drop the
lane standing (run/key/role) that a lane only carries through that
link. Use a normal handoff, or pan lane start --reuse, to continue a
lane’s work.
Configuration
Per-project defaults live in the optionalgauntlet block of
~/.overdeck/projects.yaml (see Projects):
<project dir>-lanes. It must be under your home directory, outside /tmp,
and must not contain the project checkout. The base ref defaults to
origin/<default branch>, fetched before each git-backed launch.
Workers or lanes?
Usepan worker run for issue-linked delegation inside
the managed pipeline: a worker belongs to an issue and reports back to the
agent that started it. Use pan lane start for gauntlet fan-out outside the
pipeline: a lane is a conversation, so it runs any harness, nests on the
Command Deck, and has a transcript view and a composer.