Orchestrate a feature or PR end-to-end as the lead — dispatch implementation to a delegate family based on task risk, model capability, and remaining quota; run bounded adversarial-review rounds with cross-family reviewers; verify everything independently; and hold the merge gate. Use when the user wants a whole feature/fix delegated and supervised, rather than a single implement or review pass.
Installs into .claude/skills of the current project.
Are you the author of Dev Lead?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/aarontzeng-dev-lead)
---
name: dev-lead
description: Orchestrate a feature or PR end-to-end as the lead — dispatch implementation to a delegate family based on task risk, model capability, and remaining quota; run bounded adversarial-review rounds with cross-family reviewers; verify everything independently; and hold the merge gate. Use when the user wants a whole feature/fix delegated and supervised, rather than a single implement or review pass.
---
# Dev lead: dispatch → implement → adversarial rounds → merge gate
This is the orchestration layer over the twelve family skills:
`{claude,codex,agy,opencode,grok,cursor}-implement` and
`{claude,codex,agy,opencode,grok,cursor}-adversarial-review`. It adds no new
mechanics —
it decides who does what, how many rounds, and when to stop. Read the
underlying skill before invoking it; every operational gotcha (sandbox
allow-lists, workspace traps, silent-death modes, status commands that lie)
lives there and is not repeated here.
The lead role itself is portable: all six CLIs can read the same skills
directory (symlink the others' skills paths to Claude's), and the
`claude-implement`/`claude-adversarial-review` skills supply the missing
direction — Claude as a worker for a codex or agy lead. A codex-led run has
been completed end-to-end (the interactive codex CLI as lead, briefed by a
pilot doc, ran capability probes first and delivered through the merge gate).
The distinction that makes this work: **capability is a property of the
RUNTIME, not the brand** — a companion's restricted sandbox may be unable to
commit in a worktree while the same vendor's interactive CLI under an
approved elevated runner passes the gate. What does not port automatically is
host-specific tooling (harness-tracked background tasks, subagent APIs) — a
foreign lead substitutes its own process management.
**Host capability gate — check BEFORE accepting the lead role.** The lead
must be able to run the suite and execute mutations ITSELF; that is the
non-delegable half of the job. A host whose sandbox cannot bind the sockets
the tests need, or cannot lock a worktree's shared git index, can still be a
fine implementer — but as lead it would have to delegate its own
verification, which collapses the independence the role exists to provide.
If the host cannot run the repo's tests and mutations natively, it does not
lead that repo. An explicitly approved, scoped, reproducible elevated runner
counts as host capability when it covers the required suite, mutation
commands, and local git checkpoint operations — record the exact command
class and approval scope in the run log; never silently substitute elevation
for a failed default sandbox.
The lead is this session's model — a skill cannot switch it, so the user
picks the tier before invoking. Do not trust a system prompt's "you are
powered by X" line when lead identity matters (attribution, quota
accounting): read the transcript's per-message model field, which is the
source of truth. Lead work splits into long-context bookkeeping/verification
(the wall-clock bulk) and a few concentrated-judgment moments (dispatch,
finding verification, merge verdict); on a mid-tier session, an optional
frontier-tier subagent at the Phase-3 verdict check buys judgment exactly
where it pays.
## Plan mode — fan out a question, not a change
Everything below assumes the deliverable is a **change**. When it is a
**plan** — a spec, a roadmap, a decision that needs facts nobody has yet —
run the same machinery with three substitutions, and read
[`docs/research-briefs.md`](../../docs/research-briefs.md) first: 23 measured
rules, each carrying the failure that produced it.
1. **Split the problem into falsifiable questions, not topics.** "Investigate
how X works" invites a plausible synthesis with invented citations. "Which
file and line performs the project comparison?" does not. Name the settled
decisions in the brief verbatim, or legs will produce findings against
choices that already have an ADR.
2. **Demand a completeness argument you can re-run.** Not "say what search
establishes this is exhaustive" but "give the exact command and the number
of results it returned; we will re-run it." Measured: this single demand,
not the question's phrasing, is what turned a one-route answer into a
seventeen-route enumeration from the same leg on the same question. And a
demanded argument is still only *checkable* — the pilot's own best
enumeration carried a false completeness claim, self-rated 1.0.
3. **Dispatch through the existing review adapters.** There is no
`*-research` skill and there should not be: the runtime adapter is the
expensive part and it is role-independent. Swap the brief, keep the
containment.
**Prefer a leg that can execute.** Reading legs find where to look; an
executing leg finds what is actually true there. Give it a scratch directory,
forbid commits and tracked-file edits, and require that any test it writes is
shown to fail against unmutated source before it counts.
**Nothing a leg returns enters the plan unverified.** Fan-out saves the lead's
*search* time, not reading time. A false finding in a review gets challenged;
a false premise in a plan gets built on.
Output contract: legs return facts with citations and confidence. **Legs never
write plan prose.** The lead writes the plan.
## Phase 0 — Intake (no delegation yet)
Refuse to dispatch until the task has:
1. **Acceptance criteria** concrete enough to verify mechanically.
2. **Scope**: the files/directories a correct change should touch.
3. **Risk class** — decides everything downstream:
- **HIGH**: auth, money, concurrency, data loss, notification delivery,
anything with an offline/partial-failure path.
- **MEDIUM**: behavior changes with test coverage, bounded refactors.
- **LOW**: mechanical changes, test-only, docs-adjacent code.
- **A repo plan's own dated risk assessment overrides this table** when it
is more specific — the table is a prior; the plan author saw the actual
change. (Measured: a change touched notification delivery — table says
HIGH — but the plan's dated assessment said MEDIUM with reasons; the
plan was right and delegation worked.)
If a `triage.json` exists, run `"$DEV_LEAD/scripts/triage.py" scope --files <planned paths>`
for a risk floor and lenses; it only raises. The tables still decide; record
the suggestion next to the final decision in the run log. `unmatched_files`
means the local rules have a gap worth fixing. This planned-path form has no
commit identity; Phase 2 records the frozen commit before any review launch.
Survey the code yourself first — **premise-check the task's factual claims**.
The measured incident: a task said "remove the duplicate query" about a query
that was a deliberate safety re-check; the delegate faithfully removed it. A
wrong premise dispatched is a wrong feature implemented. If the repo has its
own plan discipline, the plan document precedes dispatch.
**Premortem — for HIGH risk, and for any task that deletes or disables
existing behavior.** Before writing the task brief, run one prompt against
the plan: *"Assume this change shipped and failed — we are in the
postmortem. List the scenarios we forgot to consider."* This is the
premise-check systematized, and it attacks the one axis nothing downstream
covers: every later gate (adversarial review, mutation testing,
cross-family) reads a DIFF, and a diff structurally cannot show what the
plan wrongly left out or wrongly marked deletable. Two measured shapes of
that blind spot:
- A plan marked live code as "dead code, delete" — the diff just shows an
unreferenced block removed; no reviewer has grounds to object. The
premortem's top-ranked failure was the exact thing that block handled.
- Two operations each individually correct composed into a broken runtime
behavior (a credential read and a rotated-credential write, each
OS-prompt-gated, produced a double biometric prompt per login). Three
review legs passed it, because the defect lived in the composition at
runtime, not in any hunk. "Imagine the user's complaint after ship"
reaches it; reading the diff harder does not.
Deletion-shaped tasks get the premortem even at MEDIUM: removing behavior
is where a wrong premise is most expensive and least reviewable. LOW-risk
mechanical work skips it — this is one prompt, not a new phase.
Premortem output is hypotheses, not findings. Verify each named scenario
against the actual code before it shapes the brief; what cannot be
verified goes into the task brief labeled as an unverified assumption for
the delegate and reviewers to check — never silently absorbed as a
requirement. Skipping that verification step turns this into an anxiety
generator that bloats every brief with speculative hardening.
## Phase 1 — Dispatch
### Read the roster first
The user may name only adapters ("implement with agy, review with codex +
cursor + opencode"). The roster declares the model and effort for each
adapter, role, and round; it is not a launcher default.
```bash
"$DEV_LEAD/scripts/roster.py" plan --round r1 \
--implement agy --review codex,cursor,opencode
```
`plan` prints each leg's exact `leg-cmd.sh` arguments. A model the user
names for this round overrides the roster for this round only
(`--implement agy=<model>[:<family>]`) and is reported as an override; it
is not written back. A fix round passes `--round fix`.
A collision `plan` reports — the implementer's family equal to a reviewer's,
or two reviewers sharing a family — is a stop. Substitute a leg and report
which leg changed and why. Never drop a leg silently. An unset leg is the
same stop: ask, do not invent a model.
**A fix round's cross-family requirement is relative to who implemented THAT
round, not to r1** (owner ruling, 2026-09-22). `fix.review` inheriting r1's full leg
set is a menu, not a floor. How many of those legs a fix round of the lead's
own change takes is not a judgment either (owner ruling, 2026-10-01):
`triage.py scope --fix-of <the head the last round reviewed>` sizes it by the
fix's delta, on the ladder in Phase 2 step 3 -- documentation only takes the
lead's own read, a small fix with a failing-before test and a killed mutation
per finding takes one leg, anything else takes the range's minimum. Whichever
legs run come from a family other than the fix's author.
**Compose every launch command with the suite's `leg-cmd.sh`, never from recall.**
```bash
"$DEV_LEAD/scripts/leg-cmd.sh" <adapter> <role> --model <model> \
[--effort <e>] [--target <dir>] [--base <ref>]
```
(`$DEV_LEAD` is resolved by the same snippet Phase 2 uses for
`freeze-target.sh` — the suite's scripts never resolve against the target
repo's cwd.)
It reads [`data/launch.json`](../../data/launch.json), prints the exact command on stdout and the
adapter's gotchas on stderr, and **refuses the spellings that are actually
got wrong** — an `--effort` flag for a family that puts effort in the model
name, a missing `--effort` on codex's review (which takes it per call since
0.6.28), a missing `--variant` for opencode. Every one of those refusals is a
mistake measured in a single session (2026-09-08). Since 0.6.56 it also
refuses a claude effort outside `low`/`medium`/`high`/`xhigh`/`max`, on review
and implement alike: that CLI only warns about an unknown value and runs at
its default. A claude implement leg also needs `--allow-bash '<the repo's test
command>'`, or its delegate cannot run a single test (claude-implement).
Run it once per leg at dispatch and paste what it prints.
Why a script rather than one more paragraph: the paragraph already existed —
each leg skill opens by telling you to read its runtime file, and the wrong
commands were composed anyway. A script also reads the current file from disk,
while **the skill text in your context is frozen at whatever plugin version was
loaded**; the session that produced those five wrong launches had been handed
old copies, and the corrected text existed in the repo without being what the
lead was holding.
> [!warning] This does not close the stale-context hole, and saying otherwise
> would be the same overclaim twice
> The instruction to run `leg-cmd.sh` lives in **this file**, which is exactly
> the thing that gets injected stale. A session given an old copy never learns
> the script exists and composes from recall as before. Raised by the GPT leg
> of the review of this very change, and it is right: the script fixes "the
> lead did not read the runtime file", not "the lead was handed an old one".
>
> The channel that IS re-read fresh every session is the operator's own
> auto-memory, so the four-leg default and the effort spellings live there too.
> Treat that as the load-bearing copy and this file as the explanation.
**Probe availability first, cheaply — and "cheap" means one real call, never
a listing.** Each family's runtime file has its probe: agy has an AUTH_OK
one-liner (stale logs describe past runs; the probe is the only current
answer); codex has no reliable quota API, so attempt and treat a quota error
as "unavailable this run"; opencode needs no credential, but congestion is its
availability axis — a cheap one-liner answering in seconds means go. A
catalogue is not a probe: `opencode models` kept listing all 27 `opencode-go/`
models, both `opencode-go/muse-spark-*-contributor` entries included, after the workspace's
data-for-training switch was turned off — and every call to those two then
failed with "requires explicit opt in" (measured 2026-09-15). The listing
reflects what exists, not what your permissions let you run; a lead who probes
by enumerating gets a green that the first dispatch turns red. Send one
throwaway prompt and read the reply. The same probe catches the model that
answers with an empty string and no error (`opencode-go/glm-5.3-flash`, three
for three that day) — a listing cannot.
Implementer selection (adapt the tiers to your account's catalogue and your
calibration journal — see
[`docs/calibration-journal.md`](../../docs/calibration-journal.md)):
| Situation | Implementer |
|---|---|
| LOW, mechanical sweep, nobody waiting | free pool — zero quota, runs tests natively; congestion makes turnaround unpredictable, so only when wall-clock is cheap |
| LOW, mechanical sweep, time matters | cheapest capable *paid* tier |
| LOW/MEDIUM, spec is clear | cheapest capable paid tier |
| MEDIUM, needs design judgment | a mid/high paid tier; or a second-pool model whose quota is otherwise idle |
| Reasoning-hard delegated work (a fix round failed on reasoning, not integration) | the frontier tier, maximum effort |
| HIGH risk or ambiguous spec | **the lead implements directly** — delegation adds a supervision layer exactly where supervision is hardest |
| Preferred delegate unavailable | another family. All families down → lead implements |
**Switching families mid-run rewrites the task prompt, not just the
command.** The delegates' test policies differ and are not interchangeable:
one family may only be allowed three pinned test-command spellings (any other
spelling silently kills the run with zero output), another runs whatever the
repo needs, a third needs a per-worktree permission config written before
dispatch. Reusing a task file verbatim across a family switch produces a
silent, output-less death that reads like a code problem and is a prompt
problem. Re-read the target skill's task-prompt section on every switch.
**The sharpest version of that: never hand a delegate a scope it cannot
REACH.** A brief names two things independently — what to look at, and what
the delegate is allowed to run — and it is entirely possible to write a pair
that cannot both be true. The self-contradiction does not announce itself;
the run dies, output-less, looking exactly like a model failure.
Measured 2026-09-07, one session, one lead, three separate legs killed by
three different versions of the same mistake:
- A `nl -ba` read instruction in the preamble. Correct and load-bearing for
one family; in headless mode on another it is an auto-denied shell call, so
the FIRST tool call killed the run before a file was read.
- The same instruction carried to a third family whose config denies `*`
except git — same death, different allow-list.
- **A `git diff <base> HEAD` scope in a brief that forbids shell.** The
delegate did the only thing left: it tried to READ the range, reaching for
`.git/worktrees/<name>/logs/HEAD` — outside the `--add-dir`ed directory,
auto-denied, run over. A git range is not a description of files; it is an
instruction to run git.
The fix is not a longer brief, it is a per-family one: write the scope in
the vocabulary the delegate actually has. Where shell is forbidden, name the
files explicitly — an eight-line list beats a one-line range, and it is the
only form that survives the sandbox. Where a read command is pinned, confirm
it is on THAT family's allow-list before it goes in the preamble.
**The worktree is missing everything the suite needs that git does not
track, and the delegate will not tell you.** The virtualenv, the `.env`, the
built assets, the fixture cache are gitignored, so a pinned test command
naming any of them cannot run in the worktree at all -- and a delegate falls
back to something else and reports that as green (measured: 2511 passed /
137 skipped against a baseline of 2649). Before dispatch, run the pinned
command in the worktree yourself, provision what it needs, and compare every
reported total to your baseline digit for digit. Three rules, each paid for
in [`references/worktree-provisioning.md`](references/worktree-provisioning.md):
- **Re-run the specific tests a round turns on**; matching totals do not
clear them (a mock left unbound in a sandbox without network passed there
and failed here).
- **Never symlink the lead's copy of a shared directory** into the worktree;
a delete goes through the link (645 of 693 `node_modules` entries gutted).
Give each worktree its own install or a hardlink copy.
- **"Provision" is not "copy the real one."** The delegate gets a sanitized
minimum and nothing secret; the lead's own verification run uses the real
file. A suite that cannot start without a real secret is a finding about
the repo, not a reason to ship the secret into a sandbox.
Reviewer — the cross-family rule is absolute (implementer's family never
reviews its own change). Review is the leverage point, so it gets the
strongest tier available:
| Implementer | Reviewer(s) |
|---|---|
| Gemini family | GPT, Claude, or a named free-pool model |
| GPT family | Gemini, Claude, or a named free-pool model |
| named free-pool model (Nemotron/Meta/…; **NOT DeepSeek** — measured 2026-09-16, it is absent from this account's free pool and consent-gated on the paid one) | GPT, Gemini, or Claude — all cross-family by construction |
| stealth free-pool model (family undisclosed) | cross-family is UNVERIFIABLE — any reviewer might secretly share its family. Prefer a named-family implementer when accounting matters; otherwise take two reviewers from two different KNOWN families |
| the lead itself | any family that is not the lead's. Never review the lead's work with the lead's own family — even through a different CLI |
| HIGH risk, any implementer | **two independent reviewers from two families** — measured: two families independently converging on the same root cause was itself the strongest signal the finding was real |
| MEDIUM risk, when a free leg is available | **take the second reviewer anyway.** Measured: on a MEDIUM change, two families each returned 2 real defects with zero overlap. Convergence is the strong signal when it happens; disjoint coverage is the ordinary case, and a zero-quota second leg costs only wall-clock. Run them concurrently against the same frozen commit |
| Re-review of a prescriptive fix round | a mid tier is enough — the changes follow written findings; save the frontier tier for open-ended hunts |
| **Owner standing ruling (2026-08-30, this account)** | **default WIDE, not minimum**: beyond the accounting leg, take the free/cheap extra legs by default — a Laguna (OpenRouter :free) or free-pool leg, plus a cheap GPT tier (e.g. luna) challenge leg — whenever wall-clock allows. The owner asked "why only two legs" twice in one day; the minimum table above is the floor, not this account's default. Distinct briefs per leg, as always |
**When you spend a second or third leg, change the BRIEF, not just the
model.** The table above is the accounting rule, not the coverage rule.
Measured: three legs over one design ruling, each given a deliberately
different job — one walked execution sequences, one was asked to challenge
whether the approach was right at all, one audited for text that was
superseded but still read as live. Their principal findings overlapped zero
percent, and each leg found the only instance of its own class. The roles
worth splitting, in the order they tend to pay: **sequences**, **challenge**
(never phrased as "find defects"), **consistency**, **is-it-still-true**, and
**falsifiability** — can each test here actually FAIL? Give that last one to a
leg whenever the change ADDS a guard or a gate, because a green suite is
equally convincing whether or not the suite is able to go red (measured: on one
three-leg round it was the only lens that found anything, and it found five;
see [`docs/methodology.md`](../../docs/methodology.md) §2).
**A brief is written PER FAMILY. It is not a template (measured 2026-09-07).**
The lenses above vary the question; the PREAMBLE has to vary with the sandbox,
because the four adapters do not agree on what a delegate may run. A lead
wrote one preamble for a 4-leg round and copied it into all four briefs. It
carried terra's proven operating condition — "read files with `nl -ba <path> |
sed -n`, so every citation carries a real line number" — which is right for
codex and wrong everywhere else:
- **agy** auto-denies an unlisted shell command in headless mode and kills the
run with ZERO output. The leg died in about 90 seconds having read nothing.
The runtime file already records this failure shape; the brief walked into it.
- **opencode** was under a config denying `bash: {"*": "deny"}` except five git
reads. It survived only because a denial comes back to that model as a
message and it fell back to its own file tools (`auto-rejecting` count 0) —
luck, not design.
- **codex** and **cursor** were fine, which is exactly why the mistake is easy
to miss: half the round works.
So: write the lens once, the preamble four times. Say in each what that
delegate may actually run, and say that anything else ends the run. And when a
leg comes back empty in under two minutes, suspect the brief before the model —
`grep -c auto-rejecting` and the agy stderr both name the denied command.
**Rewriting a brief mid-round voids that leg's scaffold bracket.** Same
session: the lead fixed the three broken briefs while one leg was still
running, and re-recorded the digests. The before/after comparison for that leg
then proved nothing, because the "before" had been overwritten. The change was
harmless — it removed an instruction the leg had already consumed from stdin —
but the evidence chain was gone. Kill and relaunch, or record in the run log
that the bracket is void for that leg and why. Silently re-recording the digest
turns a proof into a decoration.
Quota economics: when one model is scarce, spend it on **review**, not
implementation — review leverage is higher, and implementation has more
substitutes.
### The brief's premises are the one thing no reviewer checks (measured 2026-09-05)
Every gate in this suite reads a diff or a spec that the LEAD wrote. A false
sentence in the brief is therefore invisible to all of them at once — and the
more legs you run, the more confident the false sentence looks.
Measured, in a repo where price history feeds portfolio valuation: the lead's
plan asserted that one provider flag returns the raw as-traded price. It does
not; that series is already adjusted for splits. Three families reviewed the
change, fifteen findings were verified and fixed across two rounds, the suite
went from 2936 to 2960 green — and every one of those tests encoded the same
wrong premise, because the fixtures were built from the brief. The defect
(every pre-split position undervalued by the split factor) shipped to main and
was found afterwards, by one query against real data:
`select close, adj_close from stock_prices where symbol='AAPL' and time near the split`.
So, for any brief that rests on what an external system returns:
- **State each premise as a claim with a check next to it**, not as background
prose: "X returns Y — verified by <command>, <date>". An unverified premise
goes in labeled as unverified, which at least lets a reviewer aim at it.
- **Run the check before writing the brief**, not after the merge. One query or
one API call against real data costs a minute and is the only gate that reads
the world instead of the diff.
- **A green suite built from a wrong premise is evidence of nothing.** Its
fixtures inherit the error; mutation testing confirms the tests pin the code,
not that the code is right. When the change is about how an outside system
behaves, put one assertion against real data in the acceptance criteria.
- Do this even when the premise is "obviously" true. The measured one had been
stated in a design doc, quoted in three review briefs, and repeated in commit
messages before anyone looked.
### Parallel implement legs (measured 2026-09-05)
Two write legs can run at once — different families, different slices — but
only when the slices share **zero files**, and the way to get there is not to
hope: the lead writes and COMMITS the shared layer first (types, API contract,
the shared engine), and every delegate worktree branches from that SHA. On the
measured run (backend + shared engine committed by the lead; then a Gemini leg
on `mobile/` and a Grok leg on `frontend/` in parallel) both merges were
conflict-free by construction, not by luck. What changes when there are two:
- **Everything per leg is per leg.** Its own worktree, its own `snapshot-refs`
baseline, its own baseline suite run *in that worktree*, its own review legs
(cross-family is decided against THAT leg's implementer: the Gemini slice got
GPT + free-pool + Grok, the Grok slice got GPT + Gemini + free-pool), and its
own fix round. Do not share a review brief across slices — the lenses are the
same, the file lists are not.
- **One waiter per leg**, each backgrounded on its own; a single poll loop
waiting for "both" cannot tell which finished. And read the family's runtime
file for what a finished leg looks like: `cursor-agent` buffers its whole JSON
result until exit, so a 0-byte output file means *running*, not dead — the
lead misread three legs as dead this way and relaunched duplicates.
- **Merge serially, gate once more.** Land the legs one at a time onto the
integration branch (`--no-ff`, so each leg's history stays legible), then run
ALL suites on the merged tree before it touches main — two green legs are not
a green merge, they are two green legs.
- **The parallel brief carries the lead's premises twice as far.** A wrong
factual claim in a shared brief lands in both slices before any reviewer sees
either. Measured: the brief asserted a helper "needs no actions because a
split never changes which symbols are held"; the delegate complied; the
Grok review leg falsified it with a three-line counter-example. When the
reviewer proves the brief wrong, the fix-round brief says so — "this was the
lead's error, not yours" — because the delegate is about to be told to undo
work it was ordered to do.
## Phase 2 — Rounds (bounded)
`ROUNDS_MAX = 3` implementation rounds by default (R1 + two fix rounds); the
user can set it at invocation. Each round:
**A review that did not come from an implement round still has rounds.** When
the user simply asks for a two- or four-leg review of work already in the tree —
often work the LEAD wrote — the same cap and the same stop conditions apply, and
nothing else here changes except who is holding the pen. Say the cap out loud at
the start; without it a review-fix-review loop has no defined end and stops when
the lead happens to feel finished, which is not a criterion. Measured
2026-09-01: a standalone two-leg review of a ~130-line lead-written script ran
two rounds and would have run a third on no principle at all.
1. **Implement/fix** in the worktree (same worktree across rounds, new
commits on top). R2+ task prompts quote each verified finding **verbatim,
with why it is real and what fix is required** — this shape fixed
everything first-try in live runs; vague "address the review" has no track
record.
**Background the WORK, not the launch.** Whatever the host uses to notify
you that a background task finished must be wrapped around the thing that
actually takes the minutes. A foreground CLI (`agy`, `opencode run`,
`codex exec`) already is that thing — hand it over directly and never add
an inner `&`/`nohup … &`, which makes the host task exit at launch and
discards the notification. A launcher that returns immediately (the codex
companion's `task --background`) needs a second backgrounded step that
blocks until the job is terminal; each runtime file names its own waiter.
**A multi-leg round is therefore one backgrounded call PER LEG, not one call
that starts them all.** This is the operative consequence, and it names the
reason a lead reaches for the inner `&` at all: wanting four legs running at
once, `cmd1 & cmd2 & cmd3 &` inside a single foreground call looks like the
obvious way there, and it is exactly the shape the rule above forbids — that
call returns in milliseconds, the host marks it complete, and the legs are
orphaned with nothing tracking them. Measured 2026-09-01, one session: a lead
who had read this section did precisely that three times; every leg finished
30-100 minutes before it noticed, each time only because the user asked. The
same session then dispatched two legs as two separate backgrounded calls and
was woken by the host on the first one.
Measured 2026-08-25, one session: three consecutive rounds finished 14, 20
and 40+ minutes before the lead noticed, every time only because the human
asked — and two of those were delegate refusals the lead had explicitly
asked for and should have acted on at once. The delegate was not slow; the
lead was not listening. In the same session an `agy` leg launched with an
inner `&` reported "completed" in under a second and had actually died.
Do not substitute a hand-rolled poll loop between turns. It is a live task
the user can interrupt, and an interrupted poll is indistinguishable from
the job ending.
**Dispatching a leg to a SUBAGENT inverts that last rule — say so in the
prompt.** A subagent driving an external CLI is a *leaf*: nothing will wake
it when the CLI exits, because the host's completion notification goes to
the session that spawned it, not to the spawned one. So a leaf that ends
its turn to "wait for the monitor" is not waiting — it has silently
abandoned the run, and only a human noticing gets it back. For a leaf the
blocking poll the lead must avoid is exactly the right answer, provided it
lives *inside a single tool call* (`while kill -0 $PID 2>/dev/null; do
sleep 15; done`, generous per-call timeout, another such call immediately
if the first times out) rather than across turns. Spell this out in the
dispatch prompt; the review skills describe how to *launch*, and a leaf
that follows only that stops one step early.
Measured 2026-08-31, one session: across three 4-leg rounds this cost five
manual re-prompts — opencode, codex and cursor legs each ended a turn on
"waiting for the background monitor", and one leg needed two nudges before
it stopped reaching for the host's monitor tool and wrote a real loop. Two
phrases predict the failure and belong in the prompt as explicit
prohibitions: *"I'll report once the notification arrives"* and *"still
running, continuing to wait"*. Neither is a valid end state for a leaf.
**Measured effective 2026-09-01, for SUBAGENT dispatch specifically**: a
4-leg round run through four subagents, each prompt opening with the leaf
paragraph above, took **zero** manual re-prompts — every leg blocked to
completion on its own (160 s to ~14 min). Same lead, same four adapters,
same host as the five-nudge session. Carry the paragraph into the prompt;
do not assume the skills alone reach a leaf.
**But the stronger move is to not create a leaf at all.** A peer lead ran
two 4-leg rounds the same day with zero re-prompts and no leaf paragraph
anywhere, because it launched each CLI directly as a backgrounded job from
its own session: the host's completion notification then comes back to the
session that can act on it, and this failure mode cannot arise. Reserve
subagent dispatch for when you actually need the leg's tool-call traffic
kept out of your context; when you don't, dispatch directly. Do not read
the measurement above as "the paragraph is what makes 4-leg rounds work" —
it is what makes them work *once you have already chosen* the shape that
can strand a leg.
2. **Lead verifies independently** — never from the delegate's self-report.
Order matters when the work comes back uncommitted (some sandboxes cannot
commit in a worktree at all): FIRST inspect the working tree itself
(`git status --short`, `git diff` — a `$BASE...HEAD` range on an
uncommitted tree is empty and reads as a clean scope check, a measured
false green), re-run the tests, THEN the lead stages in-scope paths and
makes the checkpoint commit, and only then do ranged checks,
commit-message hygiene, and **mutation-proof every new regression test**.
**Changing a statement OR A PREDICATE? grep for its copies BEFORE you
edit.** The same sentence usually lives in three places — the implementation
comment, the header/API doc, and the operator-facing log line — and fixing
the one you happen to be in leaves the others contradicting it. Measured:
the same correction was made three times across three review rounds, each
round catching one more surviving copy. Tests never catch this class.
`grep -rn` a distinctive phrase from the sentence you are about to change,
fix every hit in the same edit, and say in the commit how many there were.
**A wrong test predicate copies exactly the same way, and the rule used to
miss it because it only said "statement".** Measured, a later round: a probe
decided its verdict with `"Permissions Violation" not in r`. That is wrong —
a REJECTED LOGIN also lacks that string, so it reported ALLOWED — and the
expression appeared four times in one file plus once in a sibling. Fixing
the two instances review named would have left twelve required-positive
checks with the same hole. Here the rule's own line, "tests never catch this
class", was literally true: every suite was green with all four copies
broken, because the copies WERE the suite.
So before changing any decision expression — a verdict, a guard, a
comparison a test's result turns on — `grep -rn` the expression itself, not
just its surrounding prose. Then prefer collapsing the copies onto one
function over correcting each: the count is what makes the next occurrence
impossible, and correcting N copies leaves N places for the next person to
fix N-1 of.
**A working-tree restore invalidates every verification behind it,
including the commit message you already wrote.** Measured 2026-08-30: a
`git checkout --` (aimed at a concurrent agent's stray edit) also reverted
the lead's own uncommitted fix; the checkpoint commit then landed with a
message describing behavior that was no longer in the tree. Nothing
downstream catches that — review reads the diff, and the diff was honest
about what it contained; only the prose was wrong. So after ANY restore,
stash, or reset in the worktree, re-read the diff against the message,
re-run the suite, and re-run the mutations, all against the **committed**
tree. Related, from the repo side: never `git checkout` to undo your own
uncommitted work while another agent shares the checkout.
**Writing a NEW statement? Name what would make it false.** The grep rule
above catches a sentence that went stale; it cannot catch one that was false
on arrival, or one that was true when written and went false because the
WORLD moved (a vote, "the current patchset", a count of open items). Run
`python3 "$DEV_LEAD/scripts/claim-audit.py" "$WORKTREE" "$BASE...HEAD"` and
answer its question per hit: **if this sentence were false, which test goes
red?** Naming a test that runs nearby is not an answer. It is an attention
cue, not a control: it exits 0 either way, and a silent run is not evidence
that claims were checked. Commit what the audit changed before the target is
frozen, and mutation-proof any test it produced. The three shapes of false
statement, the monotonicity test for a perishable fact, how to read the
count, and the two shapes only a cross-family reviewer catches are in
[`references/claim-audit.md`](references/claim-audit.md).
Mutation mechanics live in
**[`references/mutation-runbook.md`](references/mutation-runbook.md)** —
read it before this step. Every clause in it was paid for with a live
false result: a harness that reported a mutant killed when it was not, a
restore that destroyed the implementation, a stale binary that made
correct code look broken. The three that bite earliest: **commit before
any mutation** (a restore on an uncommitted tree erases the work),
**never `&&`-chain the test run into the verdict echo** (a killed mutant
exits non-zero — that IS the success signal), and **put the marker check
in its own command** (a timeout kills the restore, and the mutant stays).
3. **Adversarial review** per the reviewer table, against the same immutable
`$BASE` — which is `git merge-base <target-branch> HEAD`, NOT the target
branch itself (see [`docs/methodology.md`](../../docs/methodology.md) §7;
the review skills carry the pre-launch
`git diff --stat "$BASE" HEAD` guard). **The reviewed directory
must be FROZEN for the whole run** — a detached worktree at the exact
commit, that nothing else touches. A reviewer reads the working tree, not
your commit: a measured round ran mutation testing in the same worktree
mid-review and the reviewer opened a CRITICAL on a mutated, non-compiling
file. One directory per reviewer, no lead activity inside it. Use the
tested helpers rather than hand-rolling this — it is the repo's
highest-risk shell, and every bug ever found in it was in a hand-rolled
copy:
```bash
# The helpers live in the SUITE's tree; cwd is the TARGET repo. A bare
# `scripts/…` resolves against the target and exits 127 — which lands you
# in the hand-rolled copy this paragraph just warned about.
DEV_LEAD=${DEV_LEAD_ROOT:-$(ls -d "$HOME"/.claude/plugins/cache/dev-lead/dev-lead/* 2>/dev/null | sort -V | tail -1)}
for helper in freeze-target.sh verify-target.sh; do
[ -x "$DEV_LEAD/scripts/$helper" ] || { echo "dev-lead root unresolved (no $helper) — set DEV_LEAD_ROOT to your checkout"; exit 1; }
done
REVIEW_HEAD=$("$DEV_LEAD/scripts/freeze-target.sh" "$REPO" "$SHA" "$FROZEN_DIR")
"$DEV_LEAD/scripts/verify-target.sh" "$FROZEN_DIR" "$REVIEW_HEAD" # before AND after
```
**Triage the immutable target before launching every review round,
including fix rounds.** Run
`"$DEV_LEAD/scripts/triage.py" scope --base "$BASE" --target "$FROZEN_DIR"`
(or `triage.py change <number>` for a Gerrit change; a fix round of your
own change adds `--fix-of`, below), then launch at least
its `min_review_legs` from distinct families using the lenses it lists.
MEDIUM's minimum of two is unconditional here: take the second reviewer
anyway -- with one exception, the fix round of the lead's own change.
**A fix round of your own change adds `--fix-of <the head the previous
round reviewed>` to `scope`** (that head's triage record must exist;
`change` has no such option, so a fix of your own Gerrit change is triaged
with `scope` on its frozen clone), and the record's
minimum becomes the fix's rung (owner ruling, 2026-10-01, after one change
took four rounds and another three):
| the fix's delta | rung | legs |
|---|---|---|
| documentation only (`.md`, `.markdown`, `.rst`, `.adoc`, not an agent instruction file -- `AGENTS.md`, `CLAUDE.md`, `GEMINI.md`, `QUANTA.md`, `SKILL.md` and their variants, `*instructions*` / `*prompt*` files, anything under `.claude/`, `skills/` and the other agent directories README lists; the rules file's `wording_globs`, when set, replaces this default), or only the commit message | wording | none: the lead's own read |
| within `small_delta_lines`, and every fixed finding has a test that failed before the fix and a mutation the lead killed, stated as `--fix-tested '<which tests, which mutations>'` | tested | one cross-family leg |
| anything else, or a delta a `delta_triggers` rule raises to HIGH | full | the range's minimum (at least HIGH's for a trigger) |
A comment inside a code file is not detected as wording: telling a
comment from code per language is a guess, so such a fix is at least the
tested rung; a binary, mode or move change has no lines to size and is the
full rung. `--fix-tested` and `--blocker` are your statements: the record
and `round-check` carry them verbatim, and nothing checks them. The delta is taken between the two reviewed patches, the way
`change` compares patch sets, so an amended or rebased fix is sized by
what it changed and not by the base it moved to. From the third fix round
on, a full round needs `--blocker '<the finding>'` (Convergence, under the
stop conditions). `--fix-of` must name the head of the latest round
recorded for the change; an older head is refused, since the count would
restart. A head amended before its review keeps its record, and removing
that record is how to say it was never reviewed. A `scope` without
`--fix-of` (or a `change`) will not replace a fix-round record. The gate
binds rounds sized with `--fix-of`: triaging a fix as a change of its own
takes the full legs and no gate, so the convergence rule there is yours. `round-check` and `leg-cmd.sh` read the same record,
so a lower minimum here needs no `--no-triage`; the report names the rung.
`leg-cmd.sh … review` reads the reviewed HEAD from `--target`, or
from the caller's cwd when the adapter has no `{TARGET}` slot. `--head` is
only an assertion and must resolve to that same HEAD. Launch cursor-type
adapters from inside the frozen worktree, because they review their caller
cwd. When a record has `base`, a launch's `--base` must match it; otherwise
rerun `triage.py scope --base <this base> --target <frozen dir>` first,
with the same `--fix-of`, `--fix-tested` and `--blocker` on a fix round
(leg-cmd.sh prints that command, quoted, from the record; for a
`change` record it prints `triage.py change <number>`).
Going below the minimum is `--no-triage '<reason>'` only with the owner's
word, and records that exception. After the legs return, run `triage.py
round-check --head "$REVIEW_HEAD" --target "$FROZEN_DIR" --leg
<adapter>[:<family>]=<log> [--expect <verdict-regex>]` for every leg before
treating the round as complete.
A repo that committed CRLF files and later added `eol=lf` shows them as
` M` on every fresh checkout. Both scripts excuse exactly those files --
bytes and mode identical to the commit ([`renorm-only.sh`](../../scripts/renorm-only.sh)) -- and
freeze prints which. The per-leg skills' raw `git status --porcelain`
brackets predate that excuse and will show those files; the scripts are
the authority.
**Tear the frozen directories down only after every leg has actually
returned, and read the leg's own exit line rather than the wrapper's.**
A background launcher usually runs with its cwd inside the frozen
directory; removing that directory out from under a still-open shell
makes the wrapper fail on something unrelated — measured, a `pwd` after
teardown produced `getcwd: cannot access parent directories` and the
harness reported the task as exit 1, while the leg's own line in the
same output read `exit=0` and its report was complete. A leg declared
dead by its wrapper's exit code is the same class of mistake as a leg
declared empty by one grep: the signal you read was not the signal you
wanted.
Findings are
hypotheses: verify each against the code, reject false ones explicitly,
record **every** finding in the run log. **A rejection carries evidence of
the same grade the finding needed** — the `file:line` that refutes it, or
a command actually run. "I judged it wrong" is not a rejection; it is an
unaudited veto at the one step where the lead reviews nobody but itself.
**What the lead fixes directly never gets reviewed, and that is where the
next defect is.** The fix round runs after the legs have returned, so the
lead's own edits — the ones the skill explicitly encourages for small,
precisely diagnosed defects — enter the merge with zero cross-family eyes on
them. Measured: in one round the only BROKEN verdict any leg returned was
about a test the LEAD had written after the delegate finished, and it was
right. So the lead's fixes get a re-review sized by `triage.py scope
--fix-of` (the ladder above): only a documentation fix goes without a leg, and a code
fix within `small_delta_lines` gets one leg only when the mutation standard
you hold the delegate's work to is met and stated. Say in the run log which parts of the final
diff no leg ever saw.
4. **Route**: no verified HIGH findings → merge gate. Verified findings →
next round. Small, precisely diagnosed defects (a fake test, a stray
trailer) the lead fixes directly in-place — a delegation round for a
one-line fix costs more than it protects.
**A fix exercised for real may be pushed before its re-review** (owner
ruling, 2026-10-01): the build, the CI job, the board. It moves the order,
not the review: the re-review still runs at the rung `--fix-of` gives, and
the report says that the push came first and what ran.
**Stop conditions** (report instead of looping):
- Cap reached with verified HIGH findings open → keep the worktree, present
the findings history, let the user decide. Do not merge. (A blocker in a
fix round of the lead's own change gets another round under Convergence
below; the run log says the cap was passed and for which finding.)
- **Convergence: after two fix rounds, findings that are not blockers do not
open another full round** (owner ruling, 2026-10-01: in one change rounds
two and three each found one real defect and later rounds only corner
cases, and nothing said when to stop). Fix them under the lower rungs of
the ladder in step 3, or record them in the commit message as known
limits, and say which. A blocker always gets another round: `triage.py
scope --fix-of` refuses a third full fix round without `--blocker '<the
finding>'`, and records the statement.
- A fix round introduces a **new** HIGH finding (fix churn) → the spec or the
delegate is wrong for this task; lead takes over or stops.
**When the lead IS the implementer, that escalation inverts**: "lead takes
over" is a no-op, and the party making the errors is the one who would take
over. Delegate the fix round, or stop and hand the findings to the user.
- **Round N's findings land inside round N−1's fixes** → stop patching. This is
not the same as fix churn: each fix was correct as far as it went, and the
next reviewer simply found the half of the problem the fix did not reach.
Measured 2026-09-01, one 130-line script: "do not leave a partial file on
error" was fixed without covering SIGKILL, which does not raise; a strict
validator was added for the incoming snapshot and none at all for the
baseline the same function compares it against. Both were second halves,
found one round later, by a different family. When the findings start landing
in the previous round's diff, the change is being grown by accretion — rewrite
the unit from its requirements instead, and review THAT.
- The same finding survives two fix rounds → the task prompt is failing to
transmit it; lead fixes it directly.
- The same finding CATEGORY keeps reopening against approximation-shaped code
→ the review property is unbounded, not the code unfixable. Declare the
approximation's scope in the docstring and re-scope the review to it
(measured: four rounds of one-more-nesting-level findings converged to a
real in-scope catch and then an approve, the round after the boundary was
written). **Write the boundary before the review is FIRED, not after it
comes back** — the lead always knows the boundary; the reviewer is the one
who does not. It applies to more than parsers: any check that answers a
question about the world through a proxy is approximation-shaped and needs
its scope stated.
## Phase 3 — Merge gate
Assemble the verdict from the run log: rounds used, implementer/reviewers per
round, findings (verified/rejected/fixed) with their fate, final test
results, diff stat against `$BASE`.
**Re-verify the target branch's IDENTITY at the gate, not just its
cleanliness.** Measured: the user rebased the main checkout mid-round; merged
versions of earlier commits came back with new SHAs, `--ff-only` refused —
that refusal is the guard working; never switch to `--no-ff` to get past it.
Rebase the branch onto the moved target and re-run the ff. The human owns the
main checkout; assume it moves.
**Which gate is in force is what `roster.py show` prints, not memory and
not the raw JSON field** (`merge_gate.mode`, added 2026-09-23 on the owner's
ruling; `roster.py gate <user|lead> --why "…"` changes it). Absent means
`user`, and so does any roster `check` would refuse: `show` then prints
INVALID and the person's gate, whatever the field says. Read it at the start of Phase 3 — a lead that assumes the
default lands nothing on a machine configured for `lead`, and a lead that
assumes `lead` merges work the person wanted to see. **Neither mode changes
what counts as green**: a verdict with an unanswered review round, a failing
test, an open blocker or an unverified finding goes to the person in both.
A repo's own contract wins where it is stricter (a team contract, the user's
CLAUDE.md).
- **`merge-gate=user` (default)**: present the verdict and the diff summary;
merge (fast-forward), push, and tear down the worktree only after the user
says proceed. The human approves the RESULT — the verdict and the diff —
not the intention; once approved, landing it is the lead's job in that
same run, with no second "may I push?" (rule changed 2026-09-11: the ask
had been answered yes every time and gated nothing the verdict did not).
Push to the ref the project's contract names — a review ref, a topic
branch, or the target branch where the contract allows it — never a
force-push, never someone else's branch — and report the resulting ref,
change or PR number immediately.
- **`merge-gate=lead`** — the standing form of the same grant, from the
roster: on a fully green verdict the verdict IS the approval — merge,
push, tear down, and report what landed and why it qualified. Any
non-green condition falls back to the user gate. Report says the mode was
`lead` and names what made the verdict green, so the person reading it
afterwards can audit the decision they delegated.
- **`auto-merge`** — the per-run version, when the user grants it at
invocation on a machine configured `user`. Same conditions; the grant is
per-run, never remembered. A per-run "ask me anyway" likewise overrides a
configured `lead` for that run: the person can always take the gate back.
- If the session model is not the strongest available, an optional
independent verdict check: spawn a frontier-tier subagent with the run log
and the final diff, asking only "does anything here disqualify a merge?" —
a cheap second judgment exactly at the decision that is hardest to walk
back.
- Delegates never push, in every mode, no exceptions. How that boundary is
held varies: a machine allow-list on agy, a machine config on opencode,
and instruction level on claude, codex, grok and cursor — which is why all
four carry the fail-closed refs snapshot diffed at handoff as the evidence
([workflow.md](../../docs/workflow.md) carries the per-adapter table).
The lead's push above is the only one, and it happens after the
approval, never before a green verdict.
## Run log
Keep one per run (`mktemp -d`, e.g. `$RUN_DIR/run-log.md`), appended as
events happen, not reconstructed afterward: dispatch decision and why; per
round — task file path, delegate, commits, test results, review findings with
verified/rejected/fixed status **and, for each rejection, the evidence that
refutes it**; stop-condition hits; the final verdict. The final report to the
user is written from this log. If the run is interrupted, the log plus the
preserved worktree is the resume state.
Record every delegate dispatch as three separate fields, because they
diverge and the cross-family accounting reads only the last one:
- `runtime_adapter`: claude / codex / agy / opencode / grok / cursor — the
CLI driven
- `served_model`: the model that actually answered — VERIFIED per the
runtime file (some adapters silently substitute tiers), not the id you
passed
- `model_family`: the value from [`data/families.json`](../../data/families.json)
— what the reviewer-pairing rule is checked against. Read it there rather
than from a list in this file: a partial roster here once omitted nine
accounting-valid families, and a lead logging a legitimate Grok or Kimi leg
as `unknown` wrongly concludes a valid cross-family pair is not one, because
`unknown` (stealth models, `accounting_valid: false`) can never satisfy the
rule
## What this is not
Not a way to run more agents for their own sake — a task small enough for one
direct implementation should get one (the implement skills say the same). The
workflow earns its cost when the change is big enough that independent
implementation and cross-family review genuinely reduce risk, or when the
user wants the feature produced while the session works on something else.