Fable 5 + GPT-5.6: The A - Z Build of an Agent Stack That Ships While You Sleep
This is the complete build, in order, with a checkpoint after every piece.
Bookmark these 12 builds before you forget.
Introduction
You have the two most capable models ever made generally available, and you are still using one of them at a time, by hand.
Three things changed in the last month.
- Claude Fable 5 plans across stages, dispatches its own subagents, and verifies its own output. $10 per million tokens in, $50 out.
- GPT-5.6 shipped July 9 as three permanent tiers. Sol at $5 and $30, Terra at half of Sol, Luna at $1 and $6. OpenAI now sells the routing decision as the product.
- Fable 5 spent 19 days of June suspended under an export order. Availability is now a thing that happens to you.
Run casually, these models are an expensive way to generate impressive wrong things.
Run inside a system, they are the closest thing to an employee you can rent. Anthropic's enterprise numbers put Claude Code at about $13 per developer per active day. OpenAI puts Codex at $100 to $200 a month.
This guide builds that system in twelve builds: the actual files, in order, each tested before publishing, with a checkpoint after each so you know a piece works before you stack the next one on it.
It is for anyone with a repo, a terminal, and access to either CLI. The examples are code-flavored because loops grew up around code, but the router, the advisor, and the gate work on invoices and reports the same way.
Read it in order, doing the checks. Each build takes 10 to 20 minutes, about two hours in total. The 30 days at the end are the schedule during which the system earns the right to run without you.
Three principles predict every design decision below:
- Route at boundaries. Effort before model. A mid-session model swap burns a cache discount worth 90 percent. Raising thinking effort on the same model costs nothing extra in plumbing.
- Nothing grades its own homework. Writer, router, advisor, and reviewer are different parties, from different lineages where possible, and the final vote is a bash script.
- Done is a fact about the environment. A passing suite, a ticked task file, a verdict line. Never the model's opinion of itself.
The map, and why the usual one is wrong
Loop engineering is agentic workflow with an explicit stopping condition and retry logic. The harness decides when work is finished, instead of leaving "am I done" to the model.
The standard picture is swyx's Loopcraft stack: five loops nested inside each other, execution inside task inside product inside system inside oversight, each with its own exit. It is the best one anybody has drawn.
I used it for weeks. Then I tried to build against it, and it broke in four places.
1) Problem one: the loops do not nest.
Nesting means one turn of the outer loop equals one complete run of the inner loop. That is not what happens. The system loop does not wait for the product loop to finish. It runs on Sunday whether or not anything shipped.
You can interrupt a token stream by hand without unwinding four layers. It is a picture of containment drawn over a system that does handoffs.
2) Problem two: a loop drawn as endless is a bill.
The original draws the product loop that way. In practice it ends on a budget, on a checkpoint, or on somebody losing patience.
Calling that "none by design" hides the exact place where money leaks out of an agent system: a loop that nobody told how to quit.
3) Problem three: there is no abort.
Every layer has a happy ending and no unhappy one. Real loops need both: how it closes when it works, and how it stops when it cannot.
Every runaway bill I have seen was a loop with a defined exit and an undefined abort.
4) Problem four: verification is missing, and it is the whole game.
Verification is the contract between layers. When a lower loop hands an upper loop a report instead of a fact, the upper loop closes on a lie.
That single gap is why a loop can hit its stopping condition and still be wrong.
This is the version I build against.
The Evidence Ladder
Six rungs, two exits each, and one rule: control flows down as goals, evidence flows up as facts, and no rung may close on a report from the rung below it.

Five laws fall out of the shape.
Every rung has an abort, or it is a bill. If you cannot name how a loop ends badly, you have built a subscription. This is why ralph.sh carries two caps and the swarm carries a cycle cap.
Evidence flows up and control flows down. A rung closes on a fact produced below it, never on a summary written below it.
The factory gate is this law in SQL. Two reviewers said green. The gate still refused, because nobody had produced a passing test row.
A verdict is an opinion. A row is evidence.
Cost per turn climbs about tenfold per rung.
A bug that escapes rung 2 costs ten times more to catch at rung 3, and a hundred times more at rung 4.
That is the entire argument for putting your best verification as low on the ladder as it will go, which is why the gate is a bash script and not a meeting.
Timescale belongs to the model, not to the rung.
Rung 2 took minutes in 2024, takes hours in 2026, and will take days soon. Design against the exit condition, never against the clock. This is where the old picture ages fastest.
Only rung 5 has no exit condition, and that is what human means.
You live there. The ladder exists to earn one sentence: a loop can hit its stopping condition and still be wrong.
Tests pass, the gate goes green, both reviewers sign, and the last commit is still a mistake. Every rung below you exists to make that check smaller. None of them can take it from you.
Prerequisites
1claude CLI (Claude Code) with Fable 5 access2codex CLI with GPT-5.6 access # or the official plugin inside Claude Code3python3, jq, git, gh, make, cron4a repo with a test command that works today56# optional, for mixed fleets (BUILD 7):7openai/codex-plugin-cc # official: run Codex inside Claude Code8claude-model-switch # local proxy: any provider behind Claude Code9CLIProxyAPI # wrap CLI subscriptions as API endpoints
What you are building
1your-repo/2 CLAUDE.md # BUILD 1: Claude-side constitution3 AGENTS.md # BUILD 1: Codex-side constitution4 .claude/5 skills/model-bench/SKILL.md # BUILD 0: prices, API ids, fallbacks. the only file with numbers6 skills/model-router/SKILL.md # BUILD 47 skills/stuck-protocol/SKILL.md # BUILD 58 skills/ship-gate/SKILL.md # BUILD 29 agents/fable-expert.md # BUILD 510 agents/fresh-eyes-reviewer.md # BUILD 611 agents/sol-reviewer.md # BUILD 612 agents/scout.md # BUILD 713 loop/14 ralph.sh # BUILD 3: the heartbeat15 two_lane.sh # BUILD 6: cross-vendor review loop16 PROMPT.md TASKS.md # BUILD 3: the work protocol17 gate/18 verify.sh # BUILD 2: deterministic final vote19 eval_gate.py eval/cases.jsonl # BUILD 2: routing-change gate20 router/21 router.py # BUILD 422 advisor_loop.py # BUILD 523 ~/.codex/24 config.toml # BUILD 0: luna/terra/sol profiles25 prompts/effort.md # /effort score the task, name the seat26 prompts/plan-stop.md # /plan-stop plan, price it, then stop27 prompts/fable-advice.md # /fable-advice cross-vendor expert consult28 prompts/review-hostile.md # /review-hostile clean-context verdict29 prompts/compost.md # /compost failures become laws, weekly30 factory/31 factory_gate.py # BUILD 8: blackboard + completion gate32 factory.sh # BUILD 8: brief -> implement -> review -> gate33 factory.db # BUILD 8: the rows that decide34 swarm/35 swarm.sh # BUILD 9: plan -> dispatch -> score -> replan36 goals.jsonl # BUILD 9: one goal per line, each with its check37 system/38 verify_goals.py # BUILD 10: daily re-verification, forever39 goals/ # BUILD 10: one file per finished thing40 ROUTING.md # BUILD 12: the full routing policy, both harnesses41 progress.log # every build appends here42 # optional (BUILD 7): claude-model-switch proxy on localhost:4000,43 # CLIProxyAPI for wrapping CLI subscriptions as API endpoints
BUILD 0: Configure the Engines
Set these before writing any file of your own. Every number below was verified against the official pricing pages the week of publication.
The bench: every seat, July 2026
The roster the system hires from.
Prices live in one skill file, .claude/skills/model-bench/SKILL.md, loaded on demand before any routing or cost question. No other file hardcodes a number.
Prices moved three times in the six weeks before this was written. A price in an article is wrong by publication. A price in a skill file is one edit.

The split that decides everything downstream:
- Fable 5 leads hard software work. 80 percent on SWE-Bench Pro against Sol's 64.6, and it edges Sol on overall intelligence.
- Sol leads terminal and agent work. 88.8 percent on Terminal-Bench 2.1, and it tops the coding-agent index at roughly a third of Fable's cost per task.
That split is why this system is bi-vendor. Fable judges and plans, Sol reviews and drives terminals, and neither one does the bulk typing.

Two seats people misread.
Opus 4.8 is not the old flagship gathering dust. It is the automatic fallback under Fable and the recommended default for complex agentic coding. Your system inherits it whether you plan for it or not.
Sonnet 5 at introductory pricing is the best value on the board. Close to Opus 4.8 in capability at a fraction of Fable's input price, which is why it is the default executor everywhere in this build. That rate expires August 31, and the skill file already knows it.
That skill file is the only file in the system that contains a number:
1---2name: model-bench3description: The current price sheet, API model IDs, and fallback chain for every4 model this system can hire. Load before any routing decision, cost estimate,5 budget question, model comparison, or when the user asks what something costs.6---78# Model bench910Single source of truth for prices and API identifiers. Nothing else in this repo11hardcodes a price, so when the labs move their rates you edit this file and12nothing else. Verified 2026-07-13. Prices move. Re-verify before you budget.1314## Anthropic (Messages API, /v1/messages)1516Effort: output_config {"effort": "low|medium|high|xhigh|max"}. Adaptive thinking17is always on for Fable 5 and cannot be disabled. max_tokens caps thinking PLUS18response text, so set it large (start near 64k) on high and above, or the model19runs out of room mid-thought.2021| Model | In/out per Mtok | Cache read | Seat |22|---|---|---|---|23| claude-fable-5 | $10/$50 | $1.00 | conductor, planner, advisor, judge. Falls back to opus-4-8 automatically |24| claude-opus-4-8 | $5/$25 | $0.50 | conductor under retention rules |25| claude-sonnet-5 | $2/$10 (to Aug 31, then $3/$15) | n/a | default coding executor |26| claude-haiku-4-5 | $1/$5 | $0.10 | scouts, subagents, mechanical work |27281M context on Fable, Opus 4.8, Sonnet 5, priced FLAT at long context, which is29why long reads route here. Max output 128K.3031## OpenAI (Responses API, /v1/responses)3233| Model | In/out per Mtok | Cached | Seat |34|---|---|---|---|35| gpt-5.6-sol | $5/$30 | $0.50 | cross-vendor reviewer, terminal work |36| gpt-5.6-terra | $2.50/$15 | $0.25 | Codex daily driver (test vs Luna first) |37| gpt-5.6-luna | $1/$6 | $0.10 | mechanical work, quiet ticks |3839Long context above the threshold roughly DOUBLES input price, unlike Anthropic's40flat 1M tiers. First family with explicit cache breakpoints; writes cost 1.25x.4142## Open weights (bulk workers)4344| Model | In/out per Mtok | Cache hit |45|---|---|---|46| deepseek-v4-flash | $0.14/$0.28 | $0.0028 (roughly 98 percent off) |47| deepseek-v4-pro | $0.435/$0.87 | n/a |48| kimi-k2.7-code | $0.95/$4.00 | $0.19 |4950## Discounts that stack51 - cache reads cost a tenth of fresh input at both labs (about 90 percent off)52 - cache writes pay for themselves after one read (5m) or two (1h)53 - batch APIs are 50 percent off both directions54 - batch + cache read on a repeated prefix lands near 95 percent off55 - Anthropic's newest tokenizer makes about 30 percent more tokens for the same56 text, so effective cost sits above sticker. Budget on effective.5758## How to answer a cost question59 1. Estimate input and output separately. Agents read about 100 tokens for every60 1 they write, so input dominates and cache hit rate decides the bill.61 2. Apply the cache read rate to the repeated prefix, not the base rate.62 3. Multiply Anthropic figures by about 1.3 for the tokenizer.63 4. Quote a range, name the assumptions, say which line of this file you used.64 5. If the number exceeds the repo's daily cap, say so BEFORE running anything.6566Never present a price you did not read from this file. A model not listed here is67not on the bench, and adding one is a routing change that goes through the gate.
Seven facts that change how you build:
- Cached input is 90 percent off at both labs. One timestamp in your system prompt burns it on every call. Stable prefix, append-only history, always. Agents read roughly 100 tokens for every 1 they write, so this discount is most of your bill.
- Cache writes cost extra but pay back fast. Anthropic's 5-minute write bills 1.25x and breaks even after a single read. GPT-5.6 is OpenAI's first family with explicit cache breakpoints and priced writes. Architect around the cache the way a systems programmer architects around a memory hierarchy.
- The newest Anthropic tokenizer makes roughly 30 percent more tokens for the same text. Effective price sits above sticker. Budget on effective.
- A safety refusal from Fable 5 is not an error. The call succeeds and the work lands on Opus 4.8, by design, in under 5 percent of sessions. Read what happened, not the exit code alone, and configure the fallback chain before you need it.
- Do not default to the middle tier. Independent testing found some Luna or Sol setting always beat Terra on the cost-quality frontier. Test Terra against Luna on your own traffic before paying for it.
- Batch APIs halve anything that can wait overnight, and the discount stacks with caching: repeated prefixes in batched work run near 95 percent off. The overnight burner in BUILD 7 exists to exploit it.
- Availability is an operational risk, not a hypothetical. Fable 5 lost 19 days in June. Every model reference here has a fallback: fable-5 falls to opus, sol falls to terra, and every fallback gets logged.
The second meter: a seat is not an API key
Every number above is priced in dollars per million tokens. That is the right currency if you pay per call.
It is the wrong one if you run this on a $200 Codex Pro seat or a Claude Max seat.
On a seat the meter is a five-hour window and a weekly window, evaluated together, and one request counts against both.
You can be flush on the week and still locked out for four hours, because a single message ate the short one.
Same doctrine. Different currency. Three settings decide how much of a window one message can take:

Fast mode is the expensive one, because it multiplies a number that just got bigger.
GPT-5.6 runs far longer per message than 5.5 did. Mostly a gift. It also makes burn unpredictable.
- Theo reports burning over $200,000 of tokens on Sol
- He has watched one 5.6 message take 15 percent of a five-hour window
- At the multiplier, that is 40 percent of the window in a single message
Speed is not free. What it charges you for is the tokens you were going to burn anyway. Re-read the multiplier on the speed docs before you trust it: it is published per model, and it moves.
Ultra is the subtler trap, because the interface files it where the effort levels live and it is not one of them.
- Max is depth. One model, one problem, more time on a single chain of reasoning.
- Ultra is width. The work fans out to four agents, then gets synthesized.
- Different axes. Ultra is not "more than max."
Pointed at a task that does not truly split, ultra buys four agents duplicating one investigation.
It is worth roughly 3.1 points on Terminal-Bench 2.1, 88.8 to 91.9, for a fleet's worth of burn. Off until the boundaries between the sub-problems are real.
Why Sol and Terra are both correct defaults
A field report runs Sol for nearly everything. The bench above makes Terra the Codex daily driver. Both are right, and the meter is the reason.
- The rate card says Sol's output costs twice Terra's
- The seat says you already paid
- So the only live question is how many turns it takes to reach green
- A stronger model at a lower effort usually needs fewer
Rate is not cost. Cost is rate times turns-to-green, and the second term is the one that moves.
Which is BUILD 4's law arriving from the other direction: upgrade the seat before you touch the dial. On a seat that reads as Sol at high on the $200 tier, Sol at low below it. Measure before you believe either.
Set the Codex tiers now:
1# ~/.codex/config.toml2model = "gpt-5.6-terra" # daily driver3model_reasoning_effort = "medium"4# service_tier = "fast" # LEAVE COMMENTED OUT. fast mode bills 2.5x5 # credits. run /fast status to confirm you6 # are not already in it.78[profiles.fast] # mechanical work. NOT "fast mode": this9model = "gpt-5.6-luna" # profile is a cheaper model, not a 2.5x10model_reasoning_effort = "low" # meter. two different things, one word.1112[profiles.deep] # planning, evil bugs, reviews13model = "gpt-5.6-sol"14model_reasoning_effort = "high"
CHECK 0: both CLIs authenticate, and your test command exits 0 on the current repo.
BUILD 1: The Constitutions
These models follow laws and optimize around tips, so every line needs a number, a never, or a command that checks it.
Create CLAUDE.md:
1# CLAUDE.md23## NEVER (exceptions require asking first)4- Never switch models mid-session. Routing happens at session and5 subagent boundaries only. Mid-task swaps burn the cache.6- Never review your own diff. Review comes from a fresh context or a7 different lineage. Devin's reviewer catches 2 bugs per agent PR8 precisely because it shares nothing with the writer.9- Never edit, weaken, or delete a test to make it pass. Automatic FAIL.10- Never report done from self-assessment. Done = gate/verify.sh passed.11- Never take a fourth advisor consult. Three misses = BLOCKED, human's turn.12- Never merge a routing or prompt change that eval_gate.py BLOCKED.13- Never run any loop without both caps set: MAX_ITERS and BUDGET_USD.14- Never assume a frontier model is up. Fallbacks: fable-5 -> opus,15 sol -> terra. Log every fallback to progress.log.16- Never spawn a subagent you were not asked for. Children inherit the17 parent's model AND effort, so an eager fleet inherits the expensive seat.1819## DISPATCH (first match wins)20| # | Task | Seat |21|---|---|---|22| 1 | plan / architect / migrate | fable-5 plans, sonnet executes |23| 2 | extract / format / tests / docs | haiku or luna |24| 3 | context over 60k tokens | Anthropic 1M flat-priced tier |25| 4 | review of agent-written code | sol via codex, native harness |26| 5 | ambiguous | difficulty score: 0-1 cheap, 2 sonnet, 3+ frontier |27| 6 | still unsure | run cheap once, verify, escalate once on fail |2829## DONE30- Every task carries a machine-checkable done_when before work starts.31- A fresh-context reviewer judges spec against diff, nothing else.32- gate/verify.sh holds the final vote. Two maker/checker33 disagreements on one item -> stop, queue for a human.
Create AGENTS.md at the repo root, the same laws in Codex's dialect. Keep it near 100 lines, a table of contents, not an encyclopedia:
1# AGENTS.md23## Commands4| Purpose | Command |5|---|---|6| Test | make test (this command is the definition of done) |7| Lint | make lint |89## Session protocol101. Read progress.log and TASKS.md first. 2. ONE unchecked task.113. Implement, run the FULL suite, commit only green, descriptive message.124. Tick the task, append one line to progress.log, stop.13Tests define done. Never weaken, skip, or delete one. Wrong test =14mark the task BLOCKED and say why.1516## Model policy17Default: terra, medium effort. Mechanical: profile fast (luna, low).18Planning and evil bugs: profile deep (sol, high).19Effort before model. Profile picked at session start, never mid-task.20Fast mode OFF (2.5x credits). Ultra OFF. Neither one is an effort level.21Only spawn a subagent when I ask for one. Children inherit this session's22model AND reasoning level, so an eager spawn at high effort is a whole23fleet at high effort.2425## Stuck26Signals: same error twice; two no-progress steps; a test surviving two27distinct fixes. Then /fable-advice. Max 3 consults, then BLOCKED.2829## Review30/review-hostile in a FRESH session before any merge, or the cross-vendor31lane from the Claude side. Never merge your own unreviewed work.
One doctrine must live on both sides, or whichever harness has the laxer habits wins every time you switch tools.
CHECK 1: for every line, ask whether the model could comply 80 percent and claim success. If yes, rewrite it with a number or a never. wc -l CLAUDE.md under 60.
BUILD 2: The Gate
A bash script must hold the final vote before anything else exists, because every later build assumes it.
Create gate/verify.sh for your stack:
1#!/usr/bin/env bash2set -e3npm run typecheck --if-present4npm test --if-present5npm run lint --if-present
Create gate/eval_gate.py, the seatbelt for every future routing or prompt change. It runs 50 to 500 held-out cases through the current config and the proposed one, with deterministic checks only:
1for case in cases: # {prompt, must_include, must_not_include, max_words}2 hits += passes(case, run_config(case.prompt))3verdict = "SHIP" if new_score >= old_score - 0.02 else "BLOCKED"
Then make the gate unskippable as a skill, .claude/skills/ship-gate/SKILL.md:
1---2name: ship-gate3description: Run the eval gate before any routing, model, or prompt change ships.4 Use when the user asks to change routing rules, swap a model tier, edit a system5 prompt, adjust effort levels, or merge anything touching router, prompts, or6 model config.7---89# Ship gate1011Any change to routing or prompts is a quality gamble until the gate says12otherwise. This skill exists so the gamble never ships silently.1314 1. Locate eval/cases.jsonl. If it does not exist, STOP and tell the user to15 build 50 to 500 representative cases first, from real traffic. Do not16 improvise cases yourself.17 2. Run: python3 eval_gate.py eval/cases.jsonl, current config vs proposed.18 3. BLOCKED: report both scores, list which cases regressed, do not apply the19 change, suggest the smallest revert that clears the gate.20 4. SHIP: apply, and append one line to progress.log with both scores.2122Hard rules:23 - Never override a BLOCKED verdict, even if asked nicely. Escalate to the human24 with the failing cases attached.25 - Never edit the cases file in the same session as a routing change. The gate26 and the change must not share an author.27 - Cost numbers are not a defense against a quality drop. The gate wins.
The gate uses deterministic checks only, because it must never inherit the who-verifies-the-verifier problem. A routing change without an eval gate is a cost experiment you are running on your customers.
CHECK 2: ./gate/verify.sh exits 0 today, and eval_gate.py prints SHIP on its demo. If verify.sh fails now, fix it before anything else. The system stands on this script.
BUILD 3: The Heartbeat
The model brings intelligence. The loop brings discipline.
- Fresh context per iteration kills context rot
- The repo carries all memory
- Two caps turn a runaway agent into a ten dollar lesson
This is the exact shape behind Anthropic's 16-loop run that built a 100,000-line C compiler for about $20,000, with no orchestrator model anywhere.
Create loop/PROMPT.md:
1Read progress.log and TASKS.md. Pick exactly ONE unchecked task.2Implement it. Run the tests. If green: commit with a descriptive3message, tick the task, append one line to progress.log, stop.4If the same task fails twice, mark it BLOCKED with the error and stop.5Create a file named DONE only when every task is ticked AND the full6suite passes. Never edit a test to make it pass.
Create loop/ralph.sh:
1#!/usr/bin/env bash2set -u3MAX_ITERS="${MAX_ITERS:-25}" # cap one: iterations4BUDGET_USD="${BUDGET_USD:-10}" # cap two: dollars5MAX_FAILS=3; fails=0; spent=0; i=067while [ ! -f DONE ] && [ "$i" -lt "$MAX_ITERS" ]; do8 i=$((i + 1))9 # fresh session every iteration: no memory except the repo itself10 claude -p "$(cat PROMPT.md)" --max-turns 30 \11 --output-format json > out.json 2> err.log \12 && fails=0 || fails=$((fails + 1))13 [ "$fails" -ge "$MAX_FAILS" ] && exit 2 # circuit breaker14 cost=$(jq -r '.total_cost_usd // 0' out.json)15 spent=$(awk -v a="$spent" -v b="$cost" 'BEGIN{printf "%.4f", a+b}')16 awk -v s="$spent" -v c="$BUDGET_USD" 'BEGIN{exit !(s>c)}' && exit 3 # cap17 sleep 218done19[ -f DONE ] && echo "done in $i ticks, \$$spent" || echo "cap hit, \$$spent"
Swap the claude line for codex exec and the identical loop drives GPT-5.6.
The exit map is deliberate: 0 done or quiet, 2 circuit breaker, 3 budget. Every alarm in BUILD 12 keys off these.
The cost line reads the session's own JSON cost report, so the cap is enforced by the harness against real spend, not an estimate.
Count the stops in that prompt. Every branch ends in one, and that is the design, not a tic.
This generation runs much longer per message than the last one did. Mostly a gift. Occasionally a bill, because a model that no longer needs encouragement to continue will carry a task four steps past the point where you wanted to look at it.
The loop solves that structurally: one task per tick, hard stop at the end.
Interactively you have to say it out loud. Two prompts do the work:
- "write the plan, then stop and show me before you build any of it"
- once the plan is good: "build it, test it, open the PR, handle the first round of review comments, then stop"
A model that runs long is only dangerous when nobody told it where the edge was.
Long tasks fail the way context fails, so iterations stay short and the environment does the remembering. Fable 5 and the Codex models now compact their own context mid-run, so resist adding clever memory plumbing; the right amount shrinks every quarter.
CHECK 3: two tiny real tasks in TASKS.md, one hand-run tick at BUDGET_USD=2. Confirm one commit, one ticked task, one log line, and that the loop exits instead of starting a second task.
BUILD 4: The Router That Earns Its Job (model, then effort)
The most rigorous routing benchmark of 2026, LLMRouterBench, found many routers, including commercial ones, fail to reliably beat picking the best single model. So the router is guilty until proven innocent on your own traffic.
The decision rule, before any code. Add a router only when both of these are true:
- The cheap and frontier tiers show roughly a five-fold capability-per-dollar gap on your traffic
- The eval set from BUILD 2 exists to police it
If one well-chosen model beats your router on those evals, delete the router and bank the simplicity.
Once it clears the bar, router/router.py runs three layers, cheapest decision first. Note that the tiers name seats, never prices: the rates come from the bench skill at runtime, so a price change never touches your router.
1TIERS = { # seats, not numbers2 "cheap": "gpt-5.6-luna", # or claude-haiku-4-53 "mid": "claude-sonnet-5",4 "frontier": "claude-fable-5", # or gpt-5.6-sol5}6PRICES = load_bench(".claude/skills/model-bench/SKILL.md") # one source of truth78RULES = [9 (kind in {"extract", "format", "summarize"}, "cheap"),10 (kind in {"plan", "architect", "migrate"}, "frontier"),11 (context_length > 60_000, "mid"),12]13tier = layer1_rules(task) or layer2_classifier(task)14if tier: return call(TIERS[tier], task)15return cascade(task) # cheap first, verify, escalate ONCE on fail
The middle layer scores difficulty on markers you can read: why, debug, race, deadlock, refactor, security, plus code in the prompt, a prior failed attempt, and more than one subsystem touched.
Zero or one point goes cheap. Two goes mid. Three or more goes frontier. The cascade's verifier is deterministic and escalates exactly once.
Wire the same decisions into the harness so they fire without being invoked, .claude/skills/model-router/SKILL.md. Note that the skill does not restate the rules, it reads them, the same single-source-of-truth trick the bench uses for prices:
1---2name: model-router3description: Route each task to the right seat and effort before starting work.4 Use at session start, before spawning subagents, when the user asks which model5 to use, or when a task mixes planning and execution.6---78# Model router910Read ROUTING.md and apply it. Do not improvise a policy, and do not restate one11here: this file would rot, and the policy file is the one under the eval gate.1213Three things this skill enforces on top of that file:1415 1. Route at boundaries only. Session start and subagent spawn. A mid-task swap16 invalidates the cache and re-bills the context at ten times the cached rate.17 2. At session end, append the cheap-tier share to progress.log. Savings compound18 only once most traffic goes cheap, so that number is the one to watch.19 3. Any change to ROUTING.md ships through the ship-gate skill. No exceptions.
The decision order mirrors cost: a free check first, a scored guess second, a paid experiment last.
The money is the traffic split, not the cleverness.
- Send 70 percent of work to a tier a tenth the price and the bill drops about two thirds
- Production reports cluster between 40 and 85 percent saved, and the spread is almost entirely the split
- Savings compound only after most traffic goes cheap
Which is why the cheap share in progress.log is the one number this system makes you watch.
Effort is the second dial
Effort is routing one level down: the same skill, applied to how long the model thinks instead of which model runs.
Every tool buries the dial on a different default. High in Claude Code. Medium in Codex. Hidden in most apps. So people leave one setting on for everything, and either overpay or underthink.

Default to high and treat max as a last resort, not a flex.
- Anthropic's own effort docs put the sweet spot at high, and warn that max tips into overthinking
- One public benchmark of 26 coding tasks found high roughly tripled the quality of low
- The same benchmark found xhigh cost more than twice as much for a gain that rarely earned itself back
The model matters at least as much as the dial. Fable 5 on lower effort often beats older models running at xhigh, so reach for the better model before the higher setting.
Two settings get mistaken for higher rungs of this ladder. Neither is on it.
- Max is depth. One model, one problem, more time.
- Ultra is width. One task fanned out to four parallel agents, then synthesized.
Different axes. Ultra is not "more than max," and pointing it at a task that does not truly split buys four agents duplicating one investigation.
The ladder does not port across versions. The same word buys a different amount of thinking on a new model than it did on the old one.
Moving a familiar task to a new seat, start one rung below the setting you trust. Climb only if the output asks.
One trap belongs to swarms specifically.
Subagents inherit the parent's model and the parent's effort. A fleet spawned from a max-effort conductor is a max-effort fleet, and it drains a window in a single message.
Pin them down in their own frontmatter, and know which harness you are in when you do, because the pin is only as good as the spawner that reads it.
- Claude side. The frontmatter below is the control.
- Codex side. 5.6 spawns children at the parent's own model and reasoning level, and it spawns eagerly. The parent's dial is the fleet's dial.
On the Codex side the only real controls are the setting you opened the session with, and a line in AGENTS.md that says do not spawn unless asked. Verify which one you have before trusting either:
1name: scout2model: haiku3effort: low # subagents inherit the parent. a max-effort swarm4 # empties a window in one message. pin them down.
Set it per tool: /effort in Claude Code and the Codex TUI, a flag like codex -e high, a line in config, or the effort field on the API.
Model routing decides who thinks. Effort routing decides how long, and the second dial is cheaper to turn because nothing else in the setup changes and the cache stays warm. Spend effort where the loop branches. Everywhere else it is a tax.
Track the one number weekly:
1grep '^route' progress.log | awk -F'\t' \2 '{r++; if($3!="frontier")c++} END{printf "cheap share: %.0f%%\n", c/r*100}'
ROUTING.md, the policy both harnesses read
Everything above condenses into one file at the repo root.
1# ROUTING.md23Routing is four decisions, and the order is the point:4 1. WHERE: which harness runs the work5 2. WHEN: at which boundary the decision is made6 3. WHO: which model takes the seat7 4. HOW HARD: which effort level that model runs at8Most teams only argue about 3. The money is in 2 and 4.910## 1. WHERE: harness before model11A model post-trained inside a harness runs out of distribution anywhere else.12Measured spread: 20.2 percent native vs 7.7 percent foreign, same weights.1314| Work | Harness | Why |15|---|---|---|16| writing code | the model's native harness | writers degrade out of distribution |17| reviewing code | the reviewer's native harness | a weak review is worse than none |18| reading, scouting | anywhere | reads are cheap and verifiable |19| mechanical transforms | anywhere | if the check is deterministic, harness barely matters |2021LAW: remap reader and reviewer lanes freely. The writer lane stays harness-native22until your own eval set says otherwise. That override must be a measurement, not23a preference.2425## 2. WHEN: route at boundaries, never inside them26A mid-session swap throws away the prompt cache. Cached input costs a tenth of27fresh input, so a needless swap re-bills the whole context at ten times the price28and buys nothing.2930Legal boundaries, the ONLY places a routing decision may happen:31 - session start32 - subagent spawn33 - a new goal in the swarm34 - a review handoff to the other lineage35 - a fallback fired by an outage or a safeguard3637Illegal everywhere else. No per-turn routing. No adaptive swapping mid-task.38The cascade gets exactly ONE escalation, and it opens a fresh boundary.3940LAW: pick the seat when the boundary opens, then stay put until the next one.4142## 3. WHO: the decision procedure (first match wins, log the result)4344Layer 1, rules. Free. Always check first.4546| Signal | Seat |47|---|---|48| extract / format / summarize / classify / tests | Haiku or Luna, effort low |49| plan / architect / migrate / root-cause | Fable plans, Sonnet executes |50| context over 60k tokens | Anthropic 1M tier (flat-priced) |51| reviewing agent-written code | the other lineage, native harness |52| goal splits into independent pieces | swarm: Fable writes and scores |5354Layer 2, the score. One point each:55 - contains why / debug / race / deadlock / refactor / security / optimize56 - touches more than one subsystem57 - a prior attempt already failed58 - the change is irreversible or user-facing59 0-1 -> cheap tier, low effort. 2 -> Sonnet, medium. 3+ -> frontier, high.6061Layer 3, the cascade. Run cheap, verify deterministically, escalate exactly ONCE.62Never twice. A second failure is a spec problem, and a bigger model will not fix63your spec.6465Seats and fallbacks live in .claude/skills/model-bench/SKILL.md. No prices here.6667## 4. HOW HARD: effort routing68Effort is routing one level down. Cheapest dial in this file: nothing else69changes and the cache stays warm.7071| Effort | Use it for |72|---|---|73| low | typos, renames, sorting, extraction, EVERY subagent by default |74| medium | routine code from a clear spec, summaries, standard writing |75| high | teardowns, migrations, nasty bugs, all conductor work (DEFAULT) |76| max | only the problem high already failed to crack |77| ultra | NOT ON THIS LADDER. width, not depth: 4 agents in parallel. off by default |7879 1. Default to high. The sweet spot sits there; max tips into overthinking.80 2. Better model beats higher dial. Upgrade the seat before the setting.81 3. Subagents INHERIT the parent's model AND effort. A fleet spawned from a82 max-effort conductor runs at max and empties a window in one message. Pin83 every subagent in its own frontmatter: effort: low. Where the spawner84 ignores the pin, the parent's dial IS the fleet's dial. Check which you have.85 4. Max is depth. Ultra is width. Ultra only when the sub-problems are genuinely86 independent, never as a default, and not at all while the harness over-spawns.87 5. The ladder does not port across versions. On a new model, start one rung88 below the setting you trusted on the old one.89 6. Fast mode is not an effort level either. It buys latency at 2.5x credits on90 tokens you were burning anyway. Off.9192The dial lives in four places: /effort in Claude Code and the Codex TUI, codex -e high,93model_reasoning_effort in config, the effort field on the API. Claude Code94defaults high, Codex defaults medium, most apps hide it entirely, which is why95teams accidentally run one level for everything.9697## 5. Fallbacks: assume the frontier is down98Frontier availability is not guaranteed, and a safeguard can reroute a call to99Opus 4.8 mid-run, in under 5 percent of sessions, without erroring.100 - Every seat has a named fallback. No exceptions.101 - Every fallback is logged. A silent reroute is a bug, because you may be102 reading output from a model you did not pick.103 - NEVER iterate on output from a model you did not choose.104105## 6. The number that decides whether any of this worked106Blended cost is your traffic split, nothing else. 70 percent cheap cuts the bill107by roughly two thirds. 10 percent saves pocket change. Router accuracy is worth108far more than router cleverness.109110Track it with the one-liner in BUILD 4. Below 50 percent, savings have not111started compounding. Fix the split before touching anything else here.112113## 7. The gate on this file114LAW: no change to this file ships without eval_gate.py passing on 50 to 500115held-out cases. A BLOCKED verdict is final and cannot be overridden in-session.116The gate and the change must not share an author.117118## 8. When to delete this file119If a single well-chosen model beats this whole stack on your evals, delete the120router and keep the simplicity. The most rigorous 2026 routing benchmark found121many routers, commercial ones included, fail to reliably beat picking the best122single model.123Measure first. Route second. Delete gladly.
CHECK 4: router.py runs offline and prints a decision, a reason, and the split. Your notes hold the 5x gap measurement, or a dated note saying the router is not yet justified.
BUILD 5: The Advisor Inversion
The classic pattern puts the smart model in charge and burns frontier tokens on bulk execution. Invert it.
Anthropic's advisor results: Haiku with an Opus-class advisor more than doubled its score on a hard browsing benchmark, 41.2 against 19.7 alone, at 85 percent lower cost per task than the mid tier.
Execution is bulk. Advice is grams.
The harness decides when the driver is stuck, never the driver, .claude/skills/stuck-protocol/SKILL.md:
1---2name: stuck-protocol3description: Escalate to the fable-expert advisor when progress stalls. Use when4 the same error appears twice, when two consecutive steps change no files while5 errors persist, when a test fails after two distinct fix attempts, or when the6 user says the agent is going in circles.7---89# Stuck protocol (the advisor inversion)1011You are the cheap driver and that is a feature: execution is bulk, advice is12grams. But small models are overconfident, so you do not get to decide you are13fine. Check the deterministic signals instead.1415Stuck signals (any one fires the protocol):16 1. The same error string on two consecutive steps.17 2. Two consecutive steps with zero files changed while errors persist.18 3. The same test failing after two genuinely different fix attempts.1920Procedure:21 1. Count consults this session. At 3, STOP: mark the task BLOCKED with the22 error attached, summarize for the human, move on. Never a fourth consult.23 2. Build the brief, nothing else goes in it:24 GOAL: one line25 ATTEMPT x2: the last two attempts, 200 characters each26 ERROR: the exact error, 300 characters27 3. Spawn fable-expert with the brief. It returns guidance, not code.28 4. Apply the guidance in the very next step, before anything of your own.29 5. Append one line to progress.log: consult #N, what it said, whether it worked.3031Hard rules:32 - Never ask the expert to do the work. If the reply contains a full33 implementation, take the idea and discard the code.34 - Never pad the brief with your reasoning history. The expert's value is clean35 context; a long brief poisons it.36 - If the guidance fails, that counts as one of your two attempts toward the next37 consult. Three failed consults means the task is above this session's pay38 grade, and saying so is the correct output.
The expert seat, .claude/agents/fable-expert.md:
1---2name: fable-expert3description: Frontier advisor for stuck moments. Receives a compact brief from4 stuck-protocol, returns guidance under 600 tokens, never does the work itself.5model: claude-fable-5 # swap to opus if retention rules bite6effort: high7tools: Read, Grep, Glob # may read, may never edit8---910You are the expert consultant, not the contractor. A cheaper model is driving and11has hit a wall. Your value is judgment in grams, and clean context: you know12nothing about how the driver got here, which is exactly why your read is sharper.1314You receive a brief: GOAL, the last two ATTEMPTs, and the ERROR. If the brief15names specific files, you may read them. You may not edit anything.1617 1. If the brief is missing something you need, reply with ONE line naming what18 is missing. Do not guess around a hole.19 2. Diagnose before prescribing: one or two sentences on what is actually wrong.20 3. Then reply in this format and nothing after it:21 GUIDANCE:22 1. (most likely fix: what to try, and why it should work)23 2. (fallback if 1 fails)24 3. (only if there is a genuinely distinct third path)25 CONFIDENCE: high | medium | low26 4. Under 600 tokens. No code blocks over 10 lines. Give the idea, not the27 implementation. If your reply contains a full solution, you failed the role.28 5. If the task itself is misconceived, say so in item 1 and recommend what to29 tell the human.3031Operator note: if your org cannot accept Mythos-class retention, change the model32line to opus. The role works the same; the advisor result was first proven with33Opus-class advisors.
And the same lane in reverse for Codex sessions, ~/.codex/prompts/fable-advice.md:
1claude --model claude-fable-5 -p "You are an expert advisor.2 A cheaper model is driving and is stuck. BRIEF: <goal, two3 attempts, error>. Reply GUIDANCE: up to 3 numbered items,4 under 600 tokens. Do not do the work."
Small models are overconfident, so the harness watches behavior instead of asking.
The brief stays tiny, because the expert's value is clean context and a long brief poisons it.
Each consultation runs a few hundred tokens of guidance. That is why the economics collapse: execution millions ride the $1 tier, and judgment arrives by the gram at $50 per million.
Cognition published the failure first. The ceiling is set by the driver, not the advisor, and the pattern only paid off after their driver improved a generation.
The open problem is a driver noticing it is stuck. Which is why the signals above are behavioral, and why they live in the harness.
CHECK 5: advisor_loop.py passes its assertions: the driver fails twice the same way, one consult fires, the task completes, consults stay under the cap.
BUILD 6: The Two-Lane Bench
Nothing grades its own homework is a law, and this build enforces it across vendors.
Same weights in a foreign harness drop hard. One 2026 measurement showed 20.2 percent native versus 7.7 percent third-party.
So each reviewer runs in its own home. Claude reviews in Claude Code. Sol reviews through the Codex CLI.
The in-house judge, .claude/agents/fresh-eyes-reviewer.md:
1---2name: fresh-eyes-reviewer3description: Clean-context code reviewer. Invoke after any agent-written change.4 Shares zero history with the writer, reads the diff itself, may not edit.5model: haiku # swap to opus when the diff is load-bearing6effort: medium7tools: Read, Grep, Glob, Bash8---910You have no memory of how this change was written, and that is the point. Do not11ask for the author's reasoning. Do not trust any summary. Evidence only.1213 1. git diff HEAD~1 (or the range given) and read every hunk.14 2. Read the FULL contents of each touched file, not just the hunks. Bugs hide in15 the unchanged lines around a change.16 3. Run the test suite named in the README or CLAUDE.md.17 4. Hunt the three classic agent failures:18 laziness: partial implementations, TODOs, handled-for-one-case-only19 self-grading: tests weakened, assertions deleted, suites skipped20 drift: changes outside the stated task's scope21 5. Verdict, in exactly this format:22 VERDICT: PASS or FAIL23 BUGS: numbered, each with file:line and a one-sentence reason24 RISK: one sentence on the riskiest thing this change touches2526Hard rules: you may not edit files, you may not re-run the writer's task, and an27empty BUGS list with VERDICT: FAIL is invalid. If tests cannot run, that is an28automatic FAIL with the reason.
The cross-vendor judge, .claude/agents/sol-reviewer.md, is plumbing that runs one command and relays the verdict verbatim. Sol is the reviewer; this file gets it into its own harness:
1---2name: sol-reviewer3description: Cross-vendor reviewer. Sends the latest commit to GPT-5.6 Sol through4 the Codex CLI, in Sol's native harness, and relays the verdict untouched.5model: haiku # plumbing only. Sol is the reviewer.6effort: low7tools: Bash, Read8---910You are plumbing, not the reviewer. Models from one family share blind spots; this11lane exists because Sol does not share Claude's.1213 1. Check the CLI: codex --version. If missing, output VERDICT: FAIL with reason14 "codex CLI not installed" plus the setup path (or the official plugin:15 /plugin marketplace add openai/codex-plugin-cc, /plugin install, /codex:review).16 2. Run the review at high effort, in Sol's own harness:1718 codex --profile deep exec "Review the latest commit (git show HEAD) as a19 hostile senior engineer who has never met the author. Read every touched20 file IN FULL, not just the hunks. Hunt: partial implementations, weakened21 or deleted tests, changes outside the stated task scope. End with exactly22 'VERDICT: PASS' or 'VERDICT: FAIL' followed by numbered findings, each with23 file:line and a one-sentence reason."2425 3. Save the full output to .review_sol_<short-hash>.md.26 4. Relay upward: the VERDICT line first, then the findings VERBATIM. Do not27 summarize away findings, do not add reassurance, do not argue with Sol.28 If the verdict is FAIL, the parent decides what to fix. You decide nothing.2930Hard rules: never edit files, never re-run the writer's task, never substitute31your own review. If codex errors mid-run, report it as FAIL with stderr attached32rather than quietly retrying more than once.
For CI, loop/two_lane.sh drives the whole exchange, three rounds max:
1claude -p "Implement: $TASK. Run the tests. Commit when green."2review=$(codex exec "Review HEAD ... VERDICT: PASS or FAIL plus findings.")3grep -q "VERDICT: PASS" <<< "$review" && exit 04claude -p "A reviewer from another model family found these issues.5 Fix every one, rerun tests, commit: $review"
Inside Claude Code you can skip the script entirely.
OpenAI ships an official Codex plugin for Claude Code, live since March 2026 and at roughly twenty thousand GitHub stars within nine weeks. That is the market telling you cross-vendor review went mainstream. Install once:
1/plugin marketplace add openai/codex-plugin-cc2/plugin install codex@openai-codex3/codex:setup
Three moves inside any Claude session:
- /codex:review for a standard pass
- adversarial mode, which attacks your design decisions on purpose
- background handoff to Codex
Use adversarial on anything load-bearing. Use handoff when the work is terminal-heavy, where Sol is strongest.
A growing number of builders now run GPT-5.6 Sol inside Claude Code, through the proxy from BUILD 7, and prefer it to Codex for daily work.
It demonstrates the thesis: rent the weights, own the harness.
The catch is the native-harness law, since Sol in Claude Code runs outside its home. Confirm it on your own eval set. If your evals say the foreign harness wins, your evals outrank the benchmark.
Three properties make the reviewer worth having:
- Clean context. It arrives with none of the writer's history.
- Different lineage. It does not share the writer's blind spots.
- Native harness. It judges at full strength.
A softened verdict is a broken verifier, so the relay is verbatim. A third FAIL pages a human instead of taking a fourth swing.
CHECK 6: run two_lane.sh, or /codex:review on your last commit. A VERDICT lands in a .review file, and a FAIL routes back to the writer lane.
BUILD 7: Optional Fan-Outs (install when the condition appears)
Scout swarm. Install when research or codebase archaeology eats more than 30 minutes of your day. Spawn three in parallel on separate slices:
1---2name: scout3description: Cheap parallel reader for research fan-outs. Spawn several at once4 for codebase surveys, dependency audits, log archaeology, doc research.5 Read-only by doctrine; reads fan out, writes stay single-threaded.6model: haiku7effort: low # inheritance guard (BUILD 4): a swarm that inherits8 # max effort empties a window in one message9tools: Read, Grep, Glob, Bash10---1112You are one of possibly many readers running in parallel. Survey your slice and13report back small and sharp. Only your final message returns to the parent, so14keep it under 400 words and make every word earn its place.1516 1. Narrow your assignment to one slice yourself (one directory, one subsystem,17 one question), and say what you narrowed to.18 2. Reads only: Read, Grep, Glob, and read-only shell (ls, git log, git show, rg).19 Never edit, install, or delete. If the task needs a write, report it, do not20 do it.21 3. Breadth first, then depth on the two or three hottest spots.2223Report format, nothing else:24 FINDINGS: up to 10 bullets, each one fact with its evidence (file:line, commit25 hash, or URL)26 GAPS: what you could not determine and why, so the parent knows what is still dark27 HOTSPOT: the single file or function that most deserves a deeper look, one line28 on why2930Do not editorialize, do not propose implementations, do not pad. A scout who31returns 300 useful words beats one who returns 3,000 mixed ones, because32everything you emit lands in the parent's context window and the parent is paying33for it.
Reads fan out because parallel readers compound. The word cap exists because every report lands in a context window the parent pays for.
Writes never fan out. A 2026 equal-budget study found single agents match multi-agent setups on reasoning when tokens are held constant. This lane is for reading only.
Overnight burner. Install when TASKS.md holds ten or more small verifiable items. The overnight-burner skill sets up the files, confirms both caps, verifies the test command runs, then launches ralph.sh. Morning interface: git log, progress.log, tick marks.
Plan split. Install when planning tokens dominate a session's bill. One setting in Claude Code runs an Opus-class model for the plan and Sonnet for execution. Plan on the frontier by the gram, execute in bulk.
Trio bench. Install when a gnarly change deserves three perspectives. Two roles read and one writes, and the fourth pane is a trap. The plumbing sits below.
The plumbing for mixed fleets
Two open-source pieces turn a two-vendor setup into an any-vendor one. Install them only when a condition calls for a third provider.
claude-model-switch (open source, Rust, runs on localhost:4000). A local proxy between Claude Code and any Anthropic- or OpenAI-compatible endpoint.
- Remaps Claude Code's three internal tiers (haiku, sonnet, opus) to whatever models your provider offers
- Switches providers without restarting, via config reload
- Ships as a Claude Code plugin with slash commands
1claude-model-switch init # points Claude Code at the proxy2claude-model-switch add openrouter sk-or-xxx3claude-model-switch add glm \4 --haiku glm-4.5-air --sonnet glm-4.7 --opus glm-55claude-model-switch use glm # per session, never mid-task6claude-model-switch orchestrate start --preset trio # planner/coder/reviewer
The trio preset is BUILD 7's bench made physical: three tmux panes, each role on a different provider, each addressable (orchestrate send coder "implement milestone 1"), with mid-session role reassignment if a provider degrades.
CLIProxyAPI (open source). The same trick, pointed the other direction.
It wraps the OAuth logins of ChatGPT Codex, Claude Code, Gemini, and Grok as OpenAI-, Claude-, and Gemini-compatible API endpoints.
Translation: subscription seats you already pay for become routable API targets for scripts like ralph.sh and two_lane.sh, with no separate API keys. Community forks extend it to Factory and Amp, and wrappers like ccs add multi-account switching.
The law that governs both, from BUILD 6's data: a remapped model runs in a foreign harness.
Remap the reader and reviewer lanes freely. They are cheap and verifiable.
Keep the lane that writes code on a model native to its harness, until your own eval set proves otherwise.
CHECK 7: every installed fan-out has its trigger condition written next to it. Installing any of this speculatively is how stacks bloat.
BUILD 8: The Factory (completion becomes a database fact)
Everything so far proves work in the moment. The gate, the verdict, and the eval set all fire during the run.
Once more than one agent touches a project across more than one session, you need proof that survives the run: who worked what, in what order, and whether the last review cycle cleared.

The pattern comes from the pi-factory demo (github.com/xpriment626/pi-factory). The load-bearing idea is one sentence
A thread is a trace. A row is evidence. The gate reads rows.
progress.log is a diary. The blackboard is a ledger. SQLite is the ledger because it is queryable after everyone stops talking.
Create factory/factory_gate.py. It holds four tables (tickets, briefs, evidence, verdicts), a record command each agent calls as it works, and the completion gate. The gate's failure conditions map to the work itself:
1checks = [2 (tickets == 0, "No tickets were recorded."),3 (done != tickets, "Not all tickets are done."),4 (first_brief is None, "No architecture brief was recorded."),5 (first_brief > first_code, "Implementation evidence predates the brief."),6 (code_ev == 0, "No implementer code evidence was recorded."),7 (build_ok == 0, "No passing build command evidence."),8 (test_ok == 0, "No passing test command evidence."),9 (latest("architect") != "green", "Latest architect verdict is not green."),10 (latest("reviewer") != "green", "Latest reviewer verdict is not green."),11]
That list encodes the whole doctrine.
- Order is enforced. A brief that postdates the first code evidence is a violation, which makes plan-before-build a checkable fact instead of a habit.
- Both judge seats must be green on the same latest cycle. One stale approval cannot carry a newer diff.
- Completion with zero passing test rows is impossible, no matter how confident the transcript sounds.
Create factory/factory.sh, which wires the seats you already built into the run order and records rows between every step:
1G ticket "kanban board" "columns render, drag persists" # planner rows2G brief "$BRIEF" # BEFORE any code3../loop/ralph.sh && G evidence code pass "loop completed" # BUILD 3 works4npm test && G evidence test pass "npm test green"5G verdict 1 architect green "layout matches brief" # Claude seat6G verdict 1 reviewer green "tests pass, scope clean" # Sol seat, via codex7python3 factory_gate.py gate factory.db # rows decide
Nothing new gets hired.
Fable cuts tickets and writes the brief, read-only. The BUILD 3 loop implements. Claude and Sol each return a verdict from their own harness. The cycle repeats up to three times until both go green.
The factory is the org chart for employees you already have.
A real run, not a mockup. The second gate call is the entire argument for this build:
1$ factory_gate.py record demo.db ticket "kanban-board" "columns render, drag persists"23$ factory_gate.py gate demo.db4GATE: REFUSED5 - Not all tickets are done.6 - No architecture brief was recorded.7 - No implementer code evidence was recorded.8 - No passing build command evidence.9 - No passing test command evidence.10 - No review cycle was recorded.11 exit: 11213... the agents work. every step writes a row ...1415$ factory_gate.py record demo.db brief "Stack: node+sqlite. /tasks CRUD."16$ factory_gate.py record demo.db evidence code pass "src/board.js written"17$ factory_gate.py record demo.db evidence build pass "npm run build exit 0"18$ factory_gate.py record demo.db done "kanban-board"19$ factory_gate.py record demo.db verdict 1 architect green "layout matches brief"20$ factory_gate.py record demo.db verdict 1 reviewer green "scope clean"2122$ factory_gate.py gate demo.db23GATE: REFUSED24 - No passing test command evidence.25 exit: 12627$ factory_gate.py record demo.db evidence test pass "npm test 33/33"2829$ factory_gate.py gate demo.db30GATE: COMPLETE (1/1 tickets, cycle 1 green x2)31 exit: 0
Both reviewers said green. The architect confirmed the layout matched the brief. The reviewer confirmed the scope was clean.
And the gate still refused, with one line: no passing test command evidence. Nobody had run the tests.
A verdict is an opinion. Two opinions are still not a fact. One evidence test pass row later, the same gate returns COMPLETE and exits 0.
Models argue for their work convincingly, and a transcript captures the argument rather than the truth. Rows cannot be argued with.
When the gate refuses, it names the exact missing artifact. That turns "the run failed" into "produce a passing test row." A task, not a mystery.
Install condition: more than one writer session per day, review cycles that span days, or a need to prove after the fact what happened. For a solo repo with one nightly loop, BUILD 3 is enough and this is bloat.
CHECK 8: reproduce the screenshot. Record everything except a test row, and the gate must refuse with exactly one reason. Add the row and it returns COMPLETE. A gate that passes without it is a transcript reader.
BUILD 9: The Swarm (Fable plans, the fleet executes, Fable scores)
One driver with an advisor covers a task. A goal that splits into four independent pieces wants four workers.
But a fleet only holds its direction if one model plans every goal and scores every result. That is the whole reason this build exists, and the reason swarms usually fail without it.
Rung 3 made literal. Fable writes the goals, the fleet executes them, Fable scores each result against its own check, and the next cycle re-plans only the misses.
Its abort is the cycle cap. When the cap trips with misses outstanding, that is a spec problem going to a human, not a reason to run a fourth cycle.
Create swarm/swarm.sh. Three settings carry the doctrine:
1FLEET=4 # writes never collide: one worktree each2WORKER_MODEL=claude-sonnet-5 # or haiku / luna / kimi for pure execution3WORKER_EFFORT=low # NEVER inherit the conductor's effort4CONDUCTOR=claude-fable-5 # or opus-4-8 if retention rules bite
Those three lines are explicit because the default is not.
A fleet that inherits picks up the conductor's model and the conductor's effort both. A Fable-at-high conductor spawning four Fable-at-high workers to do mechanical work is the most expensive possible way to do the cheapest possible thing.
Set them. Never let them default.
Cross-harness fan-out is legal, and it is not the exception to ROUTING.md's first law that it looks like.
- A Fable conductor that shells out to codex exec runs Sol inside Codex, natively. That is the law obeyed, not bent.
- What breaks the law is a Claude-side subagent wearing a GPT model string, or the reverse.
The harness travels with the worker, not with the conductor.
The conductor writes goals, never code, and each goal carries its own check command, so done stays a fact:
1{"id":"strip-legacy-auth","spec":"remove the v1 auth path from routes/","check":"npm test -- tests/auth"}2{"id":"migrate-sessions","spec":"move session store to the new adapter","check":"npm test -- tests/session"}
Then dispatch, score, and replan:
1# dispatch: one git worktree per goal, capped at FLEET in parallel2claude --model "$WORKER_MODEL" --effort "$WORKER_EFFORT" -p "Execute this spec3 exactly. Do not expand scope. Run the tests. Commit only when green."45# score: the conductor grades each goal against its OWN check command.6# the worker's confidence is not evidence. the exit code is.7bash -c "$chk" && echo "$id" >> passed.txt || misses=$((misses+1))89# replan: next cycle sees passed.txt and re-plans only what missed
Seat by seat:
- Sonnet 5 for coding execution
- Haiku or Luna for pure mechanical work
- An open-weight worker like Kimi when the job is repetitive and the weights are free
- Sol when the work is terminal-heavy
- Opus 4.8 in the worker seat, only when a subagent needs to reason
Reserve Fable for the two jobs no cheap model can do: writing the goals, and grading them.
A fleet is a cost strategy, never an intelligence strategy.
Multi-agent runs burn 3 to 10 times the tokens for equivalent work, and a 2026 equal-budget study found single agents match them on reasoning when tokens are held constant.
A swarm earns its keep only when the goals are independent and mostly mechanical. Writes stay single-threaded per worktree, merges happen serially, and a human takes the last commit.
CHECK 9: run the swarm on a goal that splits three ways. A broken goal must come back FAIL in results.tsv and get replanned in cycle 2, never declared done.
BUILD 10: Rung 4 (nothing that passed once goes unwatched)
Everything so far verifies work while it is being made. Nothing so far notices when finished work stops being true six weeks later. A goal you verify once is an assumption with a timestamp.
Rung 4 has two halves, and its abort matters as much as its exit: if no proposal clears the gate this week, the system does not change, and that is a success.
Half one, standing goals. Every finished thing graduates into an invariant with a predicate, re-verified daily, forever. Create one file per finished thing in goals/:
1predicate: npm test -- tests/auth 2>&1 | tail -1 | grep -q passing2born: 2026-07-133status: satisfied4last-pass: 2026-07-135on-violation: wake me. do not auto-fix.
Then system/verify_goals.py runs the lot and exits 1 if any invariant broke, naming the goal, the date it last held, and the policy you set:
1held = subprocess.run(["bash","-c",predicate], timeout=60).returncode == 02g["status"] = "satisfied" if held else "VIOLATED"3# a timeout is a violation, not a pass: an expensive predicate is a broken one
Predicate rules are strict on purpose: a shell command, exit 0 means the invariant holds, cheap and read-only.
Adjectives are banned. If a shell cannot check it, a model cannot either.
Non-code goals work the same way. test -s reports/$(date +%Y-%m)-review.md is a fine standing goal for a monthly report.
Half two, compost. Once a week, read the exhaust the system already produced: BLOCKED tasks, failed gate runs, refused factory gates, reverted PRs, violated goals.
Then propose at most three changes. A new law for the constitution. A fix to a skill that keeps failing the same way. Or a standing goal you were missing.
Propose only. You sign.
1claude --model claude-fable-5 --effort high --allowedTools "Read,Grep,Glob" \2 -p "Read this week's exhaust: BLOCKED lines in progress.log, GATE REFUSED3 entries, VIOLATED goals, PRs closed unmerged. Extract AT MOST 3 proposals:4 a new law (quote the incidents), a skill fix (same failure repeating), or a5 standing goal we lacked. Propose only, do not edit anything. Clean week is a6 valid answer, say so and stop."
This rung closes only when the evals and the judges say the system got better. Which means the system needs a memory of its own failures to improve against.
The compost run is what turns a script into an institution.
The standing goals are what make finishing safe. The sentinel detects, the normal pipeline fixes, and nothing rots in silence.
CHECK 10: verify_goals.py demo names the broken invariant with its last-pass date and leaves the healthy one alone. Then write one real standing goal for the last thing you finished.
BUILD 11: Rung 5 (your seat, and why it never empties)
Rung 5 has your name on it for a mechanical reason, not a sentimental one.
A loop can hit its stopping condition and still be wrong. Tests pass, the gate goes green, both reviewers sign, and the last commit is still a mistake.
Every build below this one exists to shrink that risk. None of them erases it.
Your standing duties, all cheap:
- Read the queue with coffee: BLOCKED tasks, GATE REFUSED lines, VIOLATED goals, the cheap-tier share. Ten minutes.
- Check the last commit before it merges. Not every diff, the last one, the one the system was most confident about.
- Sign or reject the three compost proposals. That is the only way laws enter the constitution.
Three plays run on a cadence rather than on demand, because they cost real money and pay in direction rather than diffs:
Play one, the project feedback loop, monthly.
Point Fable at what you already shipped, read-only, and have it write a detailed improvement plan. Then hand execution of that plan to Opus 4.8 or Sol.
The expensive model does the part that compounds, judgment about what to change, and never the part that does not, typing.
1claude --model claude-fable-5 --effort high --allowedTools "Read,Grep,Glob" \2 -p "Review this project end to end. Write an improvement plan ranked by3 leverage: what is fragile, what is over-built, what is missing, what should4 be deleted. Do not write code. Output tasks with acceptance criteria." \5 > plans/$(date +%F)-improvement.md
Play two, the behavior analysis, monthly.
Feed it your own session and project history across both harnesses, and ask it to map how you build and where you stall.
This is the only report in the system whose subject is you. It is usually the one that changes the most.
Play three, the second-brain audit, quarterly. Point it at your notes, docs, and backlog, and ask what your own thinking says is worth building next and what is worth deleting. Treat the output as a proposal, exactly like compost.
The rungs below optimize execution, and execution is the cheap part now. Direction is the scarce input, and direction is what a frontier model is worth paying for. Buy judgment by the gram, execution by the ton, and keep the last signature.
CHECK 11: run play one on your own repo. If the plan names nothing you already knew was fragile, your context files are too thin, which is a BUILD 1 problem.
BUILD 12: Ops
Agents burn 10 to 100 times the tokens of a chat call, and input dominates at roughly 100 to 1.
Stack the four levers from BUILD 0 (split, cache, batch, compaction) and teams report landing 70 to 90 percent below their unoptimized baseline.
For sanity: $13 per developer per active day is the Claude Code enterprise average, and 90 percent of users stay under $30.
One line of discipline instead of a new subsystem: a task class may auto-merge only after 20 logged runs at a 95 percent pass rate, and a single drop below 90 percent revokes it loudly.
1awk -F'\t' '$2=="fix-lint"{r++; if($3=="pass")p++}2 END{printf "%d runs, %.0f%%\n", r, (r?p/r*100:0)}' progress.log
Weekly spend, from the per-tick costs ralph.sh already logs:
1grep '^cost' progress.log | awk -F'\t' \2 -v d="$(date -d '7 days ago' +%F)" \3 '$2>=d{s+=$3} END{printf "week: $%.2f\n", s}'
Compute the metabolism before you cron. Daily cost is ticks times average tick cost.
The seat that handles the quiet no-work tick decides the bill. Cents on the cheap tier, dollars on a frontier model at high effort, for the identical "nothing to do" answer.
Running inside your limits
These models burn tokens fast, and how you run them decides how much of a day's work lands before you hit a wall.
First, know which wall.
- On an API key, the wall is the bill. progress.log already watches it.
- On a subscription seat, the wall is a five-hour window and a weekly window judged together. Neither one appears anywhere in the log above.
Watch the meter you are actually on: the usage panel in Codex settings, /usage on the Claude side, or a monitor like ccusage or codexbar parked in the corner of the screen.
A limit you never read is a limit you discover by hitting it, with four hours left on the clock and nothing to run.
Six levers, cheapest first. Most act at rungs 0 through 2, where the tokens burn:
- Trim CLAUDE.md and AGENTS.md to the essentials. Every single prompt reads them, plus every skill and tool you have enabled. Turn off what you are not using; an unused MCP server is a tax on every message.
- Drop the effort when you do not need the top of the ladder. Default medium or high. Save max for problems that need it. And confirm fast mode is off while you are in there, because it multiplies whatever you land on.
- Give the model clear stop points. These models run long by design. Have them finish the plan and check in before executing, which is plan mode doing its real job.
- Keep subagents on lower effort. They inherit the parent's model and its dial both, and a swarm at max empties a window in one message. The cheap fix is a law in both constitutions: do not spawn unless asked.
- Keep the expensive model off the bulk. Whether Fable sits on top as a low-token conductor or on call as an advisor, it earns its price at decision points, never while typing the thousandth line.
- Read what one message costs, then tune the five above against that number. This is the only lever that tells you which of the other five is your problem.
Together these levers set how long you can run the best models before the limit stops you.
Cron, when Week 2 starts:
1# task loop: the daily tick, both caps set20 7 * * 1-5 cd /path/to/repo/loop && BUDGET_USD=5 ./ralph.sh >> ../progress.log 2>&134# system loop: nothing that passed once goes unwatched530 7 * * * cd /path/to/repo && python3 system/verify_goals.py goals/ >> progress.log 2>&167# system loop: failures become laws, once a week, proposals only80 9 * * 1 cd /path/to/repo && codex --profile deep exec \9 "$(cat ~/.codex/prompts/compost.md)" >> proposals/$(date +\%F).md 2>&1
The runbook. Each alarm, and what to do about it:

The 30-day schedule. Do not skip graduations; each unlocks the next.

The Command Deck
Every loop here is reachable from a keystroke. Slash commands live in ~/.codex/prompts/ on the Codex side, where the filename becomes the command, and in .claude/skills/ on the Claude side. The CLI clips are the headless versions that cron and CI run.
Codex slash commands

/plan-stop and /effort pay for themselves fastest. Both spend a few hundred tokens to stop you spending a few hundred thousand.
These models run long, so the checkpoint you want comes before the spend. /plan-stop returns the plan, the done-when commands, the blast radius, the cost, and the one question it would ask a human. Then it stops.
Drop both files in ~/.codex/prompts/, where the filename becomes the command.
effort.md becomes /effort, the routing dial made explicit:
1# /effort - pick the effort level before you spend it23Do not answer the task yet. Route it first.45Score it. One point each:6 - contains a why, a debug, a race, a deadlock, a refactor, a security concern,7 or an optimize8 - touches more than one subsystem9 - a previous attempt already failed10 - the change is irreversible or lands in front of users1112Then map:1314| Score | Effort | Seat |15|---|---|---|16| 0-1 | low | codex -e low, or profile fast (Luna). Never put a frontier seat on it. |17| 2 | medium | Terra at medium is the seat. |18| 3+ | high | the default for real work, and where the sweet spot sits. |1920Only if high has ALREADY been tried and failed: recommend max, and say plainly21what the extra thinking is expected to buy. The base rate you are arguing22against is roughly double the cost for a gain that usually does not repay.2324Two reminders before recommending an upgrade:25 1. A better model at lower effort usually beats a weaker model at max. Change26 the seat before the dial.27 2. If this task spawns subagents, state their effort explicitly (low unless28 proven otherwise). Subagents inherit the parent, and a fleet at max empties a29 context window in one message.3031Output exactly:32 EFFORT: low | medium | high | max33 SEAT: <model>34 WHY: <one line, naming the points that scored>35 SUBAGENTS: <effort to pin, or none>36Then stop. The human runs the task at the level you named.
A live answer looks like this, and it costs a few hundred tokens to avoid a few hundred thousand:
1EFFORT: high2SEAT: gpt-5.6-sol3WHY: contains "why", touches auth and sessions, prior attempt failed4SUBAGENTS: low
plan-stop.md becomes /plan-stop, the checkpoint before the spend:
1# /plan-stop - plan the work, then stop23These models run long. That is a feature when the plan is right and an expensive4way to be wrong when it is not. This command buys the checkpoint before the spend.56Plan the task. Do not edit a single file. Do not run a build. Do not start.78Produce:9 GOAL: <one line>10 ASSUMPTIONS: <what you take for granted; if these are wrong, so is the plan>11 STEPS: <numbered, each one commit's worth of work>12 DONE_WHEN: <the exact shell command that proves each step landed>13 BLAST RADIUS: <files and systems touched; name auth, payments, migrations, prod14 config explicitly>15 COST: <rough tokens or dollars, and which seat runs each step>16 UNKNOWNS: <what you would ask a human if you could ask exactly one question>1718Then stop and wait.1920Rules while planning:21 - Ambiguity goes in UNKNOWNS. Do not resolve it by guessing and proceeding. A22 guess that survives into execution costs a hundred times more than a question.23 - If the plan needs a credential, endpoint, or convention that is not written24 down in this repo, stop and say so. Never invent one.25 - If any DONE_WHEN is not a shell command, rewrite that step until it is. If a26 shell cannot check it, neither can a reviewer.27 - If the blast radius touches the never-list in AGENTS.md, say so at the top and28 recommend queueing for a human.2930You are paid for the plan here, not the diff. A short honest plan with real31unknowns beats a confident plan that quietly assumed the wrong thing.
fable-advice.md and review-hostile.md become /fable-advice and /review-hostile. Both are the Codex-side mirrors of files you already wrote: the advisor brief from BUILD 5, and the hostile-reviewer contract from BUILD 6. Same rules, same caps, pointed the other way across the vendor line.
compost.md becomes /compost, rung 4's weekly institution-builder:
1# /compost - turn this week's failures into next week's laws23Read this week's exhaust, all of it, and nothing else:4 - BLOCKED lines in progress.log (tasks that beat three advisor consults)5 - GATE: REFUSED entries (a run that could not prove it was done)6 - VIOLATED standing goals (something finished stopped being true)7 - circuit-breaker and budget exits (codes 2 and 3)8 - PRs opened by the loop and closed unmerged (the human silently disagreed)9 - any task class whose pass rate dropped below 90 percent1011Extract AT MOST three proposals. Three is a hard cap, not a target. A clean week is12a valid finding, and saying so is more useful than manufacturing work.1314Each proposal is exactly one of:15 1. A NEW LAW for CLAUDE.md or AGENTS.md. Quote the incidents it would have16 prevented. A law with one incident behind it is a coincidence; wait for the17 second.18 2. A SKILL FIX, when the same failure repeats in the same place. Name the skill,19 the pattern, and the smallest edit that breaks it.20 3. A STANDING GOAL you were missing, when something rotted silently. Write the21 predicate as a shell command, or do not propose it.2223Output:24 WEEK: <dates>25 EXHAUST: <counts: blocked, refused, violated, reverted>26 PROPOSAL n: <law | skill fix | standing goal>27 EVIDENCE: <the incidents, quoted>28 CHANGE: <the exact text or predicate to add>29 COST OF NOT DOING IT: <one line>30 VERDICT: <what you would do first if you could only do one>3132Hard rules:33 - Propose ONLY. Do not edit CLAUDE.md, AGENTS.md, any skill, or any goal file.34 The human signs, or it does not happen.35 - Do not propose a law that softens a gate, raises a budget, or relaxes a never.36 Those are the system asking to be allowed to fail more comfortably.37 - Empty exhaust: say "clean week" and stop. Do not go looking for work to justify38 the run.
CLI clips
Keep these in a scratch file. They are the entire system reachable from a terminal:
1# the daily tick, capped in both dimensions2BUDGET_USD=5 MAX_ITERS=20 ./loop/ralph.sh34# plan first, spend later (the single highest-leverage habit here)5codex -e high exec "$(cat ~/.codex/prompts/plan-stop.md)67TASK: migrate the session store off the legacy adapter"89# cross-vendor review of the last commit, in Sol's native harness10codex --profile deep exec "$(cat ~/.codex/prompts/review-hostile.md)"1112# the mirrored lane: Codex stuck, Claude advises13claude --model claude-fable-5 --effort high -p "$(cat ~/.codex/prompts/fable-advice.md)"1415# the goal loop: Fable plans, the fleet runs, Fable scores16CYCLES=3 FLEET=4 WORKER_EFFORT=low ./swarm/swarm.sh "split the auth migration"1718# the system loop: nothing that passed once goes unwatched19python3 system/verify_goals.py goals/ # exit 1 names what rotted20python3 gate/eval_gate.py eval/cases.jsonl # exit 1 blocks the routing change2122# rung 3, the factory: rows decide, not transcripts23./factory/factory.sh "build the kanban and notes app per PRD.md"24python3 factory/factory_gate.py gate factory.db2526# the weekly institution-building run27codex --profile deep exec "$(cat ~/.codex/prompts/compost.md)"2829# the one number that says whether any of this worked30grep '^route' progress.log | awk -F'\t' \31 '{n++; if($3!="frontier") c++} END{printf "cheap share: %.0f%%\n", c/n*100}'
The full policy lives in ROUTING.md from BUILD 4, which both harnesses read.
The Rules (print this)
- Laws, not tips: a number, a never, or a command that checks it.
- The model is rented. The loop is yours. You live on rung 5.
- A loop with no abort is a bill. Two caps on every one: iterations and dollars.
- Evidence flows up, control flows down. No rung closes on a report from below it.
- A loop can stop and still be wrong. Check the last commit before it merges.
- Route at boundaries. Effort before model. The cache votes against switching.
- Default to high. Max is depth, ultra is width, fast mode is a 2.5x tax, and subagents inherit the parent's seat and dial both.
- The router is guilty until it beats the best single model on your evals.
- Savings are the traffic split. Watch the cheap share, not the rate card.
- Execution is bulk, advice is grams. Cap the consults at three.
- Nothing grades its own homework. Fresh context, other lineage, native harness.
- A thread is a trace. A row is evidence. The gate reads rows.
- Never edit a test to pass it. Never merge past a BLOCKED gate.
- A goal you verify once is an assumption with a timestamp.
- One graduation at a time. Every month, delete something.
Closing
Thirty days from now, if you did the checks:
- A loop ships boring work unattended, behind a deterministic gate
- A cheap driver phones a frontier expert only when it stalls
- Every meaningful diff gets judged by a model that did not write it
- One number in progress.log tells you whether the economics compound
The models were never the hard part. A system that stays honest when you stop watching is the hard part, and it is why rung 5 still has your name on it.
Start tonight with the twenty minutes that proves it. BUILD 2's verify.sh. One hand-run tick of BUILD 3. One review from the other lineage on your last commit.
The first time a reviewer that shares no context with the writer finds a real bug in work you were sure was done, you will not need convincing about the rest.
Build the gate tonight. Reply with what it caught on the first run.
Disclaimer
Written from the author's research notes and verified sources, with drafting and fact-checking assistance from Claude. All prices and model behaviors were checked against official pricing and documentation pages in the week of publication; they change, so verify before budgeting.
This article was written by the author's notes as mentioned in the aforementioned paragraph and edited with Claude Opus 4.7.
If you want to add any other corrections, please add it in the comments.





