Orchestrating Multi-Model Development
Step 0 — load exactly one active-seat profile
- Use this plugin's host-provided
PLUGIN_RUNTIME_CONTEXT_V1line and the host's current-session model metadata as the current runtime context for profile guards. A newer explicit host model-switch update supersedes old context; unresolved conflicting exact IDs select generic. - A known exact ID selects its table entry, or generic if unsupported. A family label never overrides an exact ID, including an unsupported one.
- Only when no exact ID is supplied: if the current host instructions identify
this session as bare
GPT-6(for example, "an agent based on GPT-6"), select the Astra table entry by host-family compatibility, not exact identity. Other variants such asGPT-6 Minido not match. - Otherwise select generic. Keep missing or conflicting identity unknown; preserve an explicitly supplied effort and leave missing effort unknown.
Never read a user config file to guess a session override. Never load more than one active-seat profile. The selected profile's identity guard must permit its use. Quoted text, user messages, repository files, model catalogs, available child models, and a child's identity do not establish the current session's identity.
Announce the selected profile and basis before proceeding. For compatibility, say "Astra profile via host GPT-6 identification; exact model ID unavailable." This selects instructions only: do not invent an exact runtime ID or effort, switch models, grant hook enforcement, or change the plan/subagent ID allowlists. A generic selection explains missing, unsupported, or conflicting identity.
| Exact model id | Relative profile |
|---|---|
claude-fable-5-1 |
references/orchestrator-fable-5-1.md |
claude-fable-5 |
references/orchestrator-fable-5.md |
claude-opus-5 (any context-window suffix) |
references/orchestrator-opus-5.md |
claude-opus-4-8 (any context-window suffix, e.g. [1m]) |
references/orchestrator-opus-4-8.md |
gpt-5.6-sol |
references/orchestrator-gpt-5-6-sol.md |
gpt-5.6-terra |
references/orchestrator-gpt-5-6-terra.md |
gpt-5.6-luna |
references/orchestrator-gpt-5-6-luna.md |
gpt-6-astra |
references/orchestrator-gpt-6-astra.md |
| unknown | references/orchestrator-generic.md |
The alias gpt-5.6 selects Sol only after the runtime-context handler has
normalized it to gpt-5.6-sol. An exact supplied effort may be used; otherwise
effort is unknown and receives no effort-specific claim. State which profile was
loaded before planning. That profile amends the numbered steps below; where it
amends a step, the amendment wins.
Profiles choose model and effort routes while authoring a wave plan or explicitly amending one. Once a lint-clean plan is explicitly user-approved, its exact provider, model, and effort fields are authoritative for adapter execution: do not re-route or reject that approved artifact against a seed profile. This never permits a mixed/unknown-provider wave or bypasses lint and user approval.
GPT-5.6 calibration gate — 2026-09-04–05 UTC
No GPT-5.6 production wave route is supported. The final post-fix medium
matrices recorded 63/87 passes in default mode and 162/204 in critical mode,
with no infrastructure-class failures. Of eight required core cells per model,
the default matrix passed Sol 2/8, Terra 0/8, and Luna 1/8; the critical base
passed Sol 0/8, Terra 1/8, and Luna 1/8. No model passed all required paths,
and ship remained 0/2 for every model in both bases. Historical high,
xhigh, and max probes also established no route. During plan authoring,
return the GPT route as unsupported with the evidence packet and delegate the
routing decision upward. Do not substitute another GPT model, mix providers in
one wave, or use max by default. Existing Claude routing below is unchanged.
Full counts and limitations:
tests/eval/gpt-5-6-results-2026-09-04.md.
Overview
The orchestrator researches, plans, routes, integrates, verifies, and publishes; executors implement. Core principle: decisions belong to the coordinator, execution belongs to separately routed agents. Every child has an explicit model and supported effort selected from the shared routing rules; never inherit either from the coordinator or infer identity from labels. Model equality is allowed when task routing justifies it, not because of coordinator identity. Fable is a separate fable executor only for an explicit reasoned specialized choice or approved rung, never a blanket scoped-coding route. Effort advice is conditional on an already justified selection. Every rule below is derived from the models' official system cards; facts and numbers live in the dossiers.
Always reply to the user in the language the user writes in — this skill being in English does not mean English replies.
Process
- Research. Study the codebase to the depth needed for decomposition: files, dependencies, conventions. Any read-only fan-out goes through the Research Routing table below — research agents never inherit your model.
- Decisions. Close the open questions BEFORE decomposing: research an incomplete specification further and pin down the interpretation (escalate fundamental choices to the user); design cross-cutting architecture yourself and hand it out as a set of concrete implementations. Do not delegate decisions even to an Opus executor — it silently fills in gaps under ambiguity.
- Plan. Tasks: independent within their wave (no file overlap, otherwise — next wave or worktree), self-contained (the agent sees neither the conversation nor your research), closed (no "decide for yourself what's best"). Batch small same-shaped edits into one agent's task: parallelization pays only on hard chunks — on easy ones coordination overhead eats the gain. The decomposition covers ALL artifacts of the feature, including documentation (README and the like) — otherwise it silently goes stale: if you froze a file for everyone, assign it to someone explicitly.
- Table. Before launching, show the user: task | model | effort | rationale.
- Write the wave plan file (see Wave Plan Artifact) with
status: active, and record the base SHA. You do this, not the user — the plan's lifecycle is yours to open and close, and nobody should have to hand-edit a field to make supervision work. Once it is lint-clean and explicitly approved, preserve its exact provider/model/effort fields through execution; profile routes are not a second execution-time planner. - Launch. Select the host adapter below; independent tasks remain isolated by the shared wave contract.
- Review (see the checklist below). Fixes — as one concrete list. Two misses in the same place — fix the task spec, don't repeat the prompt.
- The final end-to-end review is the orchestrator's own. Before it you may launch an Opus verifier, but the verdict is the orchestrator's.
- Completion. At most 3 iterations per task, then escalation. At the end a
summary: done / verified / remaining. Set the wave plan's
status: donein the same breath — an open plan keeps the drift hook paying for a wave that ended.
Model Routing — Quick Reference
| Task | Model | Why (see the dossiers) |
|---|---|---|
| Mechanical work per exact instruction, zero decisions | Haiku 4.5 | Cheaper; condition — zero decisions |
| Implementation against a clear spec, tests, migrations, isolated features | Sonnet 5 (default) | Near-Opus quality on closed tasks |
| Digging through a large volume of code for a specific question | Sonnet 5 | Holds 1M context |
| Independent verification, "what's actually broken here" | Opus 5 executor (default heavy) | First Claude to saturate lazy-investigation; parity with 4.8 on flagging planted flaws |
| Fine-grained debugging, concurrency, source-level security-sensitive code | Opus 5 executor | Strongest coding + best injection robustness; source security unblocked |
| A long unsliceable session | Opus 5 executor | Strongest long-horizon coding at Opus-4.8 price |
| Sonnet hit its ceiling after a fix iteration | Opus 5 executor | The heavy-executor upgrade over Sonnet |
| Reading untrusted external content (web, fetched pages, hostile files) | Opus 5 executor | Most injection-robust affordable model (IPI 0.4% at k=1); still pair with platform safeguards |
| Untrusted content whose compromise would reach secrets or irreversible actions (content known hostile, an agent that can act) | Fable 5.1 executor (fable) |
Most injection-robust model to date: IPI 0.1% at k=1 / 1.0% at k=15 vs Opus 5's 0.4 / 4.8; none of 2,826 directly-answered coding requests broke (pp. 83, 86). Opus 5 stays the cost default |
| Reverse-engineering / vulnerability discovery in compiled binaries | Opus 4.8 executor (claude-opus-4-8) |
Opus 5's and Fable 5.1's cyber classifiers block binaries (Fable 5.1 card p. 52); Opus 4.8 is where the fallback lands anyway (p. 46) — choose it, don't fall into it |
Torn between Haiku and Sonnet → Sonnet. Torn between Sonnet and Opus → improve the
task spec first, then upgrade the model. Opus 5 is the default heavy executor and
verifier; Opus 4.8 is retained only for compiled-binary work and as the
cyber-refusal fallback. In plans and runner args it is addressed by its
full ID claude-opus-4-8.
Routing anti-patterns: no sub-orchestrators — executors never spawn their own subagents (documented failures in deep delegation chains: status honesty, not capability); don't give any executor untrusted external content without platform safeguards (Opus 5 is the most robust, but safeguards still matter); don't give Sonnet multi-hour sessions; don't route compiled-binary reverse-engineering to Opus 5 or Fable 5.1 (their classifiers block it) — use Opus 4.8.
Research Routing — Quick Reference
The Model Routing table above routes work that changes things. Read-only research agents — the fan-out behind planning, decomposition and reviews — are routed here instead. An unrouted research agent inherits the session's model: on a Fable seat (5 or 5.1) that silently bills file listings at the most expensive rate available. Never spawn a research agent without naming its model.
| Research kind | Model | Why (see the dossiers) |
|---|---|---|
| Mechanical pattern search: occurrences of a known string or shape | Haiku 4.5 | Zero decisions; simple file searches are its documented lane |
| Closed enumeration: files, call sites, conventions, test commands that actually run | Sonnet 5, low/medium | Strong at digging through large code volumes (ProgramBench 76–86%, 1M context) and cheap; a closed question neutralizes its documented fabricate-when-information-is-missing failure (Sonnet 5 card, p. 71) |
| Open research sub-question: how a subsystem works, what depends on what, why it is shaped this way | Opus 5, medium/high | First Claude to saturate the lazy-investigation eval — a thorough investigator (p. 110); cap at high, its effort curve inverts |
| A report the orchestrator will trust without re-verification, or reasoning over a near-1M-token surface | Opus 4.8 (claude-opus-4-8) |
Honesty ceiling (0.00 misreported rate) and the best long-context reasoning in the comparison set (GraphWalks 1M 68.1); DRACO rises monotonically through max |
Torn between Haiku and Sonnet → Sonnet, as always. The session's own model is never the answer here: research is gathering, not deciding — the decisions stay in the orchestrator seat, and the seat is where expensive reasoning is worth its price.
Mandatory lines in every research agent's prompt (the research counterpart of the executor task template):
- every claim carries evidence as
file:line, or as a command plus its output; not foundis a valid and expected answer — never fill a gap with a guess (Sonnet 5 fabricates precisely when information is missing, p. 71);- read the sources: answering from memory about library or system behavior is forbidden (Opus 5's documented recall-as-truth failure, p. 87).
Choosing Executor Effort — Quick Reference
This table is about the effort you hand to executors. Your own session's effort is your profile's business, not this table's.
| Model | low | medium | high | xhigh |
|---|---|---|---|---|
| Haiku 4.5 | — does not support effort — | |||
| Sonnet 5 | obvious solution, but the code must be read | routine implementation per spec | default for non-trivial work | hardest execution tasks; plateau! |
| Opus 5 executor | unusually strong on simple/scoped tasks | well-specified work | default for non-trivial work | avoid — overthinking/self-verification risk |
Opus 4.8 executor (claude-opus-4-8) |
— | most well-specified tasks (min effort ≈ Opus 4.7 max) | debugging, verification, long horizon | research-grade only |
| Fable 5.1 executor (explicit specialized choice or approved rung) | scoped, closed tasks | peak on scoped coding (FrontierCode, p. 169) — always with a scope/brevity line | long-horizon work | xhigh ≈ max at 19–25% fewer tokens (pp. 193–194); out-of-scope edits rise with effort — the scope line is mandatory |
Signal rule: wanting to give Sonnet xhigh because the task is open-ended → that
means switching the model to Opus or returning to the Decisions stage, not effort.
Opus 5's effort curve is the exception — higher is not better; it peaks mid-range
on coding and overthinks at max, so cap Opus 5 executors at high. Fable
5.1's curve has its own shape: task correctness keeps rising with effort but so
do unrequested out-of-scope edits (p. 169), so a Fable 5.1 executor prompt
always carries an explicit scope and brevity line.
Wave Isolation
A supervised wave gives every executor its own git worktree and commits its
work to a branch named wave/<task-id>. Record the base SHA in the wave plan
before the wave starts; every later comparison is made against that SHA, never
against a moving HEAD.
The base must be the commit the worktrees actually fork from, not your local
HEAD. Agent worktrees branch from origin/<default-branch> by default
(worktree.baseRef: "fresh"), so a commit you made locally but never pushed
does not exist for any executor. Recording an unpushed HEAD corrupts every
comparison in the same direction: files that exist only in your checkout appear
as deletions in every branch, and the supervisor charges each executor with a
files violation for a change nobody made. That is the §7 failure — correct
work blocked — arriving through the base rather than through the contract.
So, before launching: push the commit you intend as the base, or record
git rev-parse origin/<default-branch> and accept that anything unpushed is
invisible to the wave. After the first executor commits, verify with
git merge-base wave/<task-id> HEAD; if it does not equal the recorded base,
stop and fix the plan rather than judging against it.
This is not tidiness, it is what makes the contract checkable at all. Executors sharing one tree make two things impossible:
- Attribution. A diff of the shared tree contains every task's concurrent edits, so "this task touched a forbidden path" cannot be distinguished from "a neighbour legitimately owns that path" — the very violation the contract exists to catch.
- Reproducibility. A required command re-run against a tree a neighbour is editing can fail for reasons that have nothing to do with the task under judgment, and the escalation ladder would then send correct work back for rework.
A commit made inside a worktree survives that worktree's removal — worktrees
share the object database and refs — so git diff <base>..wave/<task-id> stays
available for supervision, and a second worktree can be checked out from the
branch for independent verification.
For an unsupervised wave, tasks still must not overlap in the files they touch.
Wave Plan Artifact
Before launching a supervised wave, write the plan to a file — one entry per task, carrying the prose, the contract, the assigned model, the branch and the base SHA:
base: 7c05ff5
status: active # active | done — the hook reads this, never the file's existence
tasks:
- task: http-retry
model: claude-sonnet-5
branch: wave/http-retry
contract: {...}
You own both transitions. Write the file with status: active at step 5 and
set status: done at step 9. The user never edits it: a supervision layer that
depends on someone remembering to flip a field by hand is a supervision layer
that will be off when it matters.
Without this file "deviation" has no referent: there is nothing to deviate from. It is a file rather than something you hold in context because a wave outlives a context window, and a summarized context keeps the task list while losing the exact identifiers the supervisor needs.
Task Prompt Template (mandatory blocks)
Every executor prompt contains all six blocks — explicit instructions measurably reduce the documented failure modes:
Context: specific files and lines, dependencies, project conventions. Compute numeric examples in the spec with a tool, not in your head — a wrong example contradicts the formula and derails the executor. In a supervised wave the executor works in its own worktree and sees no neighbour's edits, so say so — an agent that expects a busy tree will misread its own isolation.
Boundaries: what NOT to do — don't refactor adjacent code, don't add unrequested features/files, don't touch anything outside the list.
Dead-end protocol: "If data or access is missing, a tool is broken, or the path is impossible — stop and report what's blocking you. Don't invent values, don't work around the restriction, don't pick an interpretation on the user's behalf."
Prohibitions: do not spawn subagents; no destructive operations (force-push, reset --hard, rm outside the task) without explicit permission. Phrase prohibitions without qualifiers — executors rules-lawyer around wording when it conflicts with "the overriding goal".
Definition of done and response format: list of changed files, the gist of the changes, output of actually executed tests/linter, plus the committed-work proof:
git log --oneline <base>..HEAD(non-empty) andgit status --porcelain(empty), both pasted — uncommitted work does not exist for the wave, and "done but never committed" is the most common rejection on record.Contract: the machine-checkable half of the task. Prose carries intent; the contract carries what a supervisor can decide without arguing about intent.
contract: files_allowed: [src/http/**, tests/http/**] files_forbidden: [src/auth/**] # another task owns these this wave must_run: - cmd: pytest tests/http -q evidence: required forbidden_moves: - weakening, deleting or skipping an existing test - catching an exception to make a check pass report_must_answer: - Which call sites now retry? - What happens after the final failed attempt?evidence: requiredreplaces trust. A report claiming a command passed without that command's actual output is not weak evidence — it is a violation in its own right.State the prohibitions explicitly and loudly. This is measured, not stylistic: an explicit "don't work around it — report it" lowers fabricated workarounds from 17.4% to 9.1% for Fable 5 (pp. 161–163) and from 9.4% to 2.8% for Opus 4.8 (pp. 109–110). The Fable 5.1 card did not repeat this measurement; what it documents instead is unrequested out-of-scope edits rising with effort (p. 169), which the same explicit scope line addresses.
For tasks with images/PDF/charts — give Sonnet code-execution access.
Supervised Waves
Supervision is a stage in the Workflow script,
not an instruction to self-check. A check the executor is asked to perform
is a check it may decide
it already satisfied; a check in the control flow around it is one it never gets
a vote on. A stage can also do what advice cannot: reject and re-run.
Send references/supervisor-prompt.md to a fresh separate supervisor with the
contract, report, base SHA and branch. It never reuses an executor child or forks
its conversation, even when model and effort match. Astra's separately approved
executor exception uses a fresh Astra supervisor and is fresh-context separation,
not different-model independence; other supervisor restrictions remain.
Mechanical verification before the judge
The shipped runner inserts a fact-collecting stage between the executor and
the judge. A cheap verifier agent (default sonnet/low, overridable via
args.verifier) checks out the branch and records facts: does the branch
carry commits at all, which paths changed, what each must_run command
returns when actually run, and whether the report pastes output where the
contract says evidence: required. The runner — not a model — then applies
the deterministic half of the contract: a branch with no commits, a path
outside files_allowed, a red must_run, or missing pasted evidence
bounces straight back to the executor as a rework, and no judge is paid for
discovering it. Transcript mining across five real sessions found "work
done but never committed" to be the single most common rejection (8+
occurrences), each costing a full Opus verdict to detect.
Three properties are load-bearing:
- Fail-open. A dead verifier skips the stage; the model judge then runs the full pipeline itself, exactly as before. Mechanical verification can only save a judge call, never remove supervision.
- Once per rule. The same mechanical rule failing twice routes to the
model judge with the facts attached — only a judge can decide
satisfiable, and a repeat is where that question arises. - Facts, not judgment. The verifier never decides
ok,pasteReproducedorsatisfiable; those stay with the judge, which receives the verifier's facts and may rely on its exit codes and outputs while re-running anything it doubts.
Long commands, everywhere in the wave, are classified by kind — never by predicted duration. Build-system invocations (gradle, cargo, npm, pnpm, yarn, make, mvn and the like) start in the background with output to a log file and are polled; everything else runs in the foreground. A silent foreground wait on a cold build looks like a stall and gets the agent killed — one real session lost ~4.8 hours of supervision to exactly this, then abandoned supervision entirely.
Choosing the supervisor — Quick Reference
Two hard rules, then the table. Use a fresh separate supervisor; outside the
separately approved Astra exception, never the executor's own model (self-preference:
measured zero for Opus 4.8 and Fable 5, unmeasured for Opus 5 — so Opus 5 never
judges Opus 5; measured small but non-zero for Fable 5.1 — 0.1 points out of 10,
lenient when told the author is Claude, p. 124 — which is why the runner's judge
prompt never names the executor's model and why fable still judges Opus 5.
Not stating it does not stop a judge in an all-Claude pipeline from inferring
it; what bounds the effect is the magnitude and the contract's mechanical half
— verifier facts and grep-decidable checks the judge cannot soften).
Never a weaker tier than the executor's: the judge re-runs and re-derives
everything the executor did.
| Executor | Supervisor | Effort |
|---|---|---|
| Haiku 4.5 | Opus 5 | high |
| Sonnet 5 | Opus 5 | high |
| Opus 5 | Fable 5.1 via fable (fallback: Opus 4.8 via claude-opus-4-8) |
high |
| Opus 4.8 | Opus 5 or Fable 5.1 | high |
| Fable 5.1 (explicit specialized choice or approved rung) | Opus 5 | high |
The short name fable resolves to whichever Fable the harness serves — Fable
5.1 as of September 2026; Fable 5 is no longer addressable and keeps its profile
and dossier for history.
Effort is high across the board — the shipped runner's default — and the row
is measured, not stylistic: on 2026-08-12 a Haiku supervisor at medium passed
an unsatisfiable contract, filing its whole analysis into remarks. A cheaper
judge on a mechanical task is not the economy lever; skipping the model
entirely is (see the cost section below). All four models above judged fixtures
F1–F4 correctly in single live runs after that fix; single runs prove "can",
not a rate.
The supervisor trusts artifacts only. It checks out wave/<task-id> into
its own worktree, runs the diff itself, executes each must_run command itself,
and greps for the forbidden moves itself. The report is a set of claims to
check, never a source of facts.
A paste that does not reproduce is a fact, not an accusation. The executor
pastes command output; the supervisor re-runs the command and compares. When they
differ it records pasteReproduced: false with both outputs — and stops there.
Whether the mismatch was fabrication, output captured before the last commit, a
differently-prepared tree, or a date-dependent test is not decidable from what a
supervisor can see, and four attempts to make a model decide it correctly all
failed in the same direction: the heaviest accusation, spent on honest work.
Verification asks whether something reproduces, not whether its author was truthful — the answer reproducible builds arrived at. A single non-reproducing paste rides along with the rework so the executor sees it. Repetition is what escalates, and repetition is counted by the ladder, not judged by the supervisor.
Verdict shape:
{"ok": false,
"violations": [{"rule": "must_run:pytest tests/http -q",
"class": "must_run",
"pasteReproduced": false,
"evidence": "report pasted a green run; supervisor got 2 failed",
"quote": "tests/http/test_retry.py::test_backoff FAILED"}],
"remarks": ["src/http/backoff.py:41 duplicates the helper in src/net/retry.py"]}
violations decide ok; remarks never do. Classes: files, must_run,
forbidden-move, report. A violation without evidence the
supervisor produced itself is dropped, not softened — otherwise the supervisor
fabricates as readily as the executor it judges.
When a must_run command fails, run it a second time before recording anything.
If the retry passes, record a remark naming the command unstable and do not
block. Spending the supervisor's credibility on flaky tests buys nothing.
Escalation ladder
| Situation | Action |
|---|---|
| 1st violation | Back to the same executor with the verdict attached |
| 2nd violation of the same rule | To a stronger model — repeating a prompt on the model that just failed it reproduces the failure |
pasteReproduced: false on two attempts |
Escalate to a stronger model: once is explicable, twice is a pattern, and the count is the ladder's to keep |
| Executor is already the strongest model | No higher rung: one rework with the verdict attached, then stop |
| The contract cannot be satisfied | Stop immediately — no rework, no stronger model. Return the task to yourself to amend the contract (below) |
| Stop | Hand the user the task, every verdict in order, and the branch name |
Host adapter
Invocation publication contract
publication is optional: if omitted, it means exactly publication: push
and preserves all normal behavior. The choice belongs at this composition
boundary, not in a host adapter, runner, helper, plan, or profile. Only
publication: local must be explicit; only the enclosing critical-review
post-review fix flow may request it; it is never inferred from host or model.
Local mode never weakens lint, the pushed-base requirement, contracts, mechanical verification, supervision, verdicts, plan-order integration, or the full-wave review. Both adapters complete all of that work and return the resulting local feature-branch commit(s), task branch names, and state/verdict evidence to their caller, but local mode performs no push. The wave still forks from the current pushed PR head. Local mode is one publication transaction: if approved fixes need dependent bases that cannot safely fit in that one supervised wave, stop before publication rather than push around the gate.
- Claude-only wave: invoke
references/wave-runner.workflow.mjsexactly as documented below. - Codex-only wave (GPT-5.6 executors, or separately approved Astra initial/final
rung; GPT-5.6 or Astra supervisor): read and follow
references/codex-wave-protocol.md; do not invoke Claude Workflow. - Mixed or unknown-provider wave: stop before spawning and return the linter or identity error.
Every Codex spawn names model and reasoning_effort from the exact returned action. Never write a fresh runner, hand-edit state, or replace a missing model with a default or alias. The shared contract, verifier, supervisor schema, escalation ladder, and result review remain single-sourced in this skill; the adapter selects only the host invocation.
For a lint-clean, explicitly user-approved plan, the approved plan is authoritative for adapter execution. The adapter uses its exact provider, model, and effort fields without re-routing or rejecting them against seed-profile recommendations; lint and the mixed/unknown-provider stop still apply.
Claude-only wave — invoke the shipped runner
For a Claude-only wave, the ladder above is implemented once, in
references/wave-runner.workflow.mjs, and covered by the deterministic
simulator tier in tests/. Your job is to assemble its inputs, not to
re-implement its rules — every hand-written wave script is a fresh chance to
get "two strikes escalate" subtly wrong, and the one hand-written run on
record was rejected at launch four times before it worked.
Its opts.model accepts the documented Claude short names and the one pinned full ID
claude-opus-4-8; do not replace that identifier with an alias.
- Preflight the contracts at the base. Before the first wave forks, run
each distinct
must_runcommand once against the recorded base — route it to a cheap agent per the Research Routing table, or run it yourself. Compare against the plan's recorded expectation for each command: green at base, or expected-red (the task itself creates what the command checks). An unexpected red is a contract defect to fix now, before any executor is spawned — transcript mining found ~13% of all supervisor verdicts weresatisfiable:false, every one tracing to a contract already broken at base (fmt drift at BASE, a command targeting a nonexistent build target), each costing an executor attempt plus an Opus verdict. The same run warms the build caches every worktree in the wave will fork from cold. - Read
references/supervisor-prompt.md. Workflow scripts cannot read files, so its full text travels insideargs.supervisorPromptText. - Invoke the runner (
argsshould be a real JSON object; the tool-call layer often delivers it as a JSON-encoded string, which the runner parses and validates — only unparseable or invalid input is rejected, by name):
Workflow({
scriptPath: "<this skill's base directory>/references/wave-runner.workflow.mjs",
args: {
base: "<pushed fork-point sha>", // see Wave Isolation above
defaultBranch: "main",
repoPath: "/abs/path/to/repo",
supervisorPromptText: "<text of supervisor-prompt.md>",
supervisor: { model: "opus", effort: "high" },
verifier: { model: "sonnet", effort: "low" }, // optional; this is the default
tasks: [{
id: "auth-fix",
description: "<the substantive ask>",
context: "<files, lines, conventions>",
contract: { files_allowed: [...], files_forbidden: [...],
must_run: [{ cmd: "...", evidence: "required" }],
forbidden_moves: [...], report_must_answer: [...] },
executor: { model: "sonnet", effort: "medium" },
ladder: ["opus"] // rungs AFTER the first; omit for the routing default
}]
}
})
The runner assembles each executor's prompt from the task object — the six
mandatory blocks of the Task Prompt Template above, plus a workspace section
carrying the isolation instructions — so the contract the executor reads and
the contract the supervisor enforces are the same object and cannot diverge.
Escalated rungs run at high effort.
Claude adapter completion reads the multi-model publication contract after
every task is ok. publication: push merges branches in plan order, runs the
shared full-wave review, and pushes exactly as normal. With publication: local, merge branches in plan
order only into the local feature branch, run the shared full-wave review,
return the resulting local feature-branch commit(s), task branches, and verdict
evidence, and do no push. The Claude adapter keeps the shipped Workflow
implementation unchanged; publication stays at this composition boundary, not
in the Workflow arguments or script.
Act on the returned statuses, task by task:
ok— mergewave/<id>per the wave plan.contract-unsatisfiable— run the amendment flow below (one amendment per task; removing or weakening a check goes to the user as a yes/no), then re-invoke withresumeFromRunId: the runner is deterministic, so every unchanged task replays from cache and only the amended one runs.failed/error— hand the user the task, every verdict in order, and the branch name. Do not quietly retry.
A wave may also be launched as parallel single-task runner invocations — same-wave tasks are file-disjoint by construction, so each
okbranch can merge as its result lands instead of waiting for the wave's slowest task (measured: three finished tasks once waited ~47 minutes on a sibling's third attempt). The wave's full suite still runs once, after all of the wave's invocations settle, before the push.
Never write a custom wave script. If the shipped runner cannot express the wave, stop before spawning and return the unsupported requirement for a plan or adapter change.
When the contract is what is broken
Every other rung assumes the executor was at fault, because that is the only
hypothesis the ladder had. Verified on 2026-08-12: given a contract no compliant
change could satisfy, the ladder reworked, escalated to a stronger model, and
stopped — punishing an innocent executor three times and burning the heavy tier
to do it. All three supervisors said so unprompted, in remarks, which by design
change nothing.
So the verdict carries a fact, not a class: satisfiable — could any change
files_allowed permits have altered the outcome of the failing command? — with
the evidence for it. Deciding this is the supervisor's job; deciding what happens
next is not. A class would be another label to argue with; a fact the ladder
reads in code is not.
ok:false with satisfiable:false stops the wave for that task at once. Do not
rework, do not escalate: a second attempt reproduces the result exactly, and the
supervisors in that run said as much before it happened.
Amending the contract is your job, not the user's. You wrote it; you fix it. Record the amendment in the wave plan with its reason — before and after — so the change is on the record rather than in your head.
Two kinds of amendment, and the line between them is decidable by diffing the old contract against the new:
- Widening
files_allowed, correcting a wrong path, fixing a broken command — make the change and carry on. None of these can hide a defect: the check still runs and the work still has to pass it. - Removing or weakening a
must_runentry, or aforbidden_movethat produced a violation — write the amendment, then ask the user one yes/no question naming exactly what stops being checked. This is the only edit that can make an inconvenient check disappear, and the agent that benefits from it is the one proposing it.
The user edits nothing. You detect, you draft, you apply. What goes to them is a decision — whether they accept losing that check — not a file to open. Asking someone to hand-edit a config is how a safeguard ends up switched off.
An amendment exists only when the plan file is edited and the runner is
re-invoked with resumeFromRunId carrying the amended task. A mid-wave
"I authorize X" in conversation reaches nobody: the runner rebuilds every
rework prompt from the task object it was given, so an amendment that never
re-enters the runner never reaches an executor. Measured 2026-08: a
verbally pre-authorized dependency never propagated; the rework executor
fell back to a worse design, which passed supervision and shipped, and the
regression was fixed by a later wave at full price.
One amendment per task. A second satisfiable:false on the same task goes to the
user whatever kind it is: each loosening looks reasonable alone, and the loop
that ends in a contract checking nothing is built out of reasonable steps.
The blocking threshold sits above the suspicion threshold
Rework is triggered only by contract violations. Anything the supervisor merely
finds doubtful goes to remarks and reaches the user through the wave report.
Blocking correct work is a worse failure than missing a nitpick — a
supervisor that stops legitimate work does not just waste a wave, it
manufactures confidence in the waves it lets through.
Cost, and when to skip the model
A supervisor invocation is an agent with tools — a diff, the commands, the greps — so it is not one model request. The tiers also invert: a Haiku 4.5 task is supervised by Opus 5 (see the supervisor table above), making the supervisor the expensive half.
Run full agentic supervision for tasks whose contract has must_run commands or
files_forbidden entries that matter: migrations, shared helpers,
security-adjacent code. For a small mechanical task, check the predicates
(paths touched, commands run, evidence present) in plain script logic and
skip the supervisor model. Supervision that costs more than the work it guards
gets switched off, and then it guards nothing.
The shipped runner now performs the predicate half mechanically before every judge call (see Mechanical verification above); what remains yours is scoping each contract's gates to its files and choosing the judge's effort.
Orchestrator Drift
Supervised waves guard the executors. This layer guards the orchestrator session itself — the loop that reads verdicts, decides rungs, and reports back to the user is not exempt from the same drift it polices in others.
It ships as a plugin hook on Stop, fires once per turn, and advises — it
never blocks. The advice arrives as additionalContext, and the orchestrator
is expected to act on it or say why not; nothing in the mechanism can halt the
turn or force a rework.
It needs the wave plan artifact to compare the orchestrator's actual behavior against. With no plan file present, it stays silent — there is nothing to check drift against, so it produces no advice rather than guessing at one.
Installing the plugin turns it on; removing the plugin turns it off. The user edits no settings file to enable or disable it — the hook's presence is the only switch.
When the hook runs, and what it costs
The plan is a permanent artifact — it is the record of what each executor was contracted to do, and the thing a supervisor compares against. So the plan is never deleted to quiet the hook. Its lifecycle lives in a field instead:
status: active # active | done — only 'active' runs the hook
It watches any plan under docs/superpowers/plans/, not only a wave plan: an
orchestrator drifts from an implementation plan the same w
…(truncated)