CoalTipple — routing contract
THE FRAME — ALWAYS-ON, THREE OUTCOMES. This contract applies to EVERY prompt (the conductor injects a routing forcer each turn; there is no trigger condition to meet): every task is ROUTED, and routing returns exactly one of three outcomes — SELF (do it yourself, tier pinned + effort tuned — the most common result), delegate-DOWN, or escalate-UP — UNLESS a structural off-state is already declared: the platform gate below, enableRouting:false, mode:'off', a matched disableRouting domain, or a ranking that genuinely cannot be built (F8) — exactly five states, never a sixth you invent. Those are DECLARATIONS the pipeline reaches or the config/platform sets, not a decision you make per task. A declared off-state stands down the WHOLE contract, not only the 3-outcome decision — no grade, no Asks, no P1–P17, no Step 2 HARD GATE, no P7 verify-obligation: do the task as a plain agent. "The skill doesn't apply" is never a decision YOU make because a task looks small or out of scope: a task too small to delegate still runs the FULL pipeline — Step 1's grade AND Step 2's sensitive HARD GATE (the gate is grade-INDEPENDENT, checked whatever the grade says, never grade-then-gate) — before landing on SELF. Skipping the pipeline because a task looks small is exactly how a small-LOOKING task on a sensitive path slips past the never-down gate.
[!CAUTION]
PLATFORM GATE -- actuates on Claude Code ONLY. Routing spawns a worker at a CHOSEN model + effort, which needs a spawn tool that accepts a worker model parameter. Works: Claude Code (Agent/Task takes model). NOT SUPPORTED: Antigravity -- invoke_subagent's Model field DOES pick a per-spawn tier (proven live 2026-08-04), but it selects a GOOGLE model regardless of the Claude parent's vendor, and no effort knob exists anywhere in the schema -- CT's never-down gate, qualityBar staircase, and Claude alias floor don't map onto that shape. On a platform with no agent-selectable worker model at all, do NOT pretend to delegate-down or escalate-up -- the spawn cannot change tier; routing is a silent no-op. Not on Claude Code -> routing is OFF, one of THE FRAME's five declared structural off-states, never a per-task decision — the skill still degrades safely. (Reviewed monthly.)
Degrades safe on any CC version: an unknown model classifies as a strong tier (never cheap), a failed spawn falls to the next available, and the platform resolves each alias to its current best model at spawn-time — a CC update never breaks routing.
You are main (depth-0): you decide routing + spawn everything yourself. Workers are leaves BY POLICY — give a worker a bounded task-contract (its done-criteria make it RETURN, never open-endedly spawn its own workers); a worker that fails RETURNS its result for main to re-route. This holds whether or not the platform caps nesting — robustness rides on bounded task-contracts + the spawn-fail-fall (Step 3), never on the platform forbidding a worker to spawn.
This contract is English so any model can read it; user-facing output follows the user's language (translate prose, technical terms verbatim — see Always).
The ledgers — the countable rails (TRANSCRIBE these; never re-count from prose)
Six lists own the COUNT + MEMBERSHIP of the asks, the absolute prohibitions, the fail routes, the on-disk files, the config keys, and when each reference loads. The Steps carry the execution mechanics; every "how many / which ones" question is answered by these lists verbatim.
Asks — exactly 3, numbered (+ ONE standing offer)
- ASK 1 — FABLE (real money), conditional (Step 2). Fires when the RESOLVED worker is a fable-family model (
isFableModel — a substring match catching the alias fable AND any pinned concrete Fable release id, version-independent by design) AND fableConsent is not already true. Options: once (this spawn only) · always (persist fableConsent: true to the project config) · no (do not climb into fable — F7 caps the route at the rung below). One worker, one ask, no cost math. Never fires when routing lands on SELF or a non-fable tier.
- ASK 2 — FAN-OUT (more than one worker in one move — the ULTRACODE rung, or any parallel delegation; Step 2). Before spawning SEVERAL workers at once, state the plan (how many × tier × effort, capped at
maxConcurrentSubagents) and get a yes. A single worker is NOT this ask.
- ASK 3 — MEMORY ANCHOR (at most once per session; Memory anchor section). Fires only when ALL hold: about to delegate context-DEPENDENT work · no anchor exists (
contextFiles empty AND no platform CLAUDE.md/AGENTS.md the worker would inherit) · memoryOffer is auto · not yet offered this session. Options: Create / Choose / Skip (Skip persists memoryOffer: 'off' — never re-asks).
- OFFER — SELF ERROR-REPORT (an offer, not a consent gate): when CoalTipple itself misbehaves → STOP, summarize, OFFER to file an issue (P16; Self error-report section).
- NOT asks (add nothing to the count): a SINGLE non-fable worker spawn — standing consent is your
mode config, and its transparency is the visible [model·effort] spawn label (Step 3.2), not a question · the qualityBar climb (automatic, bounded by maxTotalAttempts) · the self-update ask/reminder (the conductor's SessionStart channel, governed by updateMode — never fired by this file).
Absolute prohibitions — P1–P17 (the complete set)
A "never" inside a Step and not listed here is that step's sequencing mechanics, not an additional absolute.
- P1 — Sensitive work is NEVER delegated DOWN — whatever the grade, under every
mode, and under a quota/limit-hit too (wait for reset / stay on a capable main / hand back — never fall cheaper; a vetted built-in beats hand-rolling). Sensitivity is a property of the PATH + the task's MEANING, never of the edit type — a "comments-only" / "just a typo" edit on an auth/crypto/payment path is still sensitive. The gate forbids DOWN only: SELF stays allowed (Step 2 HARD GATE).
- P2 — Never read "no keyword flag fired" as "not sensitive" — the flags are English-only; the gate fires on MEANING in ANY language, and YOU are the sensitive-gate authority on a non-English task (Step 2).
- P3 — A
modelTiers pin never lowers the sensitive floor — a KNOWN-weaker alias pinned into a high tier does not satisfy the gate (a pin proves availability, never capability); only an UNKNOWN, post-cutoff pin is trusted there (Step 2).
- P4 — Never spawn a fable-family worker without consent (ASK 1); on "no", never climb into fable for this task, block the RESOLVED model name (not merely the literal
fable), and never hardcode opus as the cap — the cap is the rung below fable, READ from the ranking (Step 2 · F7).
- P5 — Never route on a broken ranking, and never enumerate the live model list — the ranking is the alias floor + pins; genuinely unbuildable → routing OFF, do it yourself (Step 0 · F8).
- P6 — Workers are LEAVES: never spawn an open-ended worker — a bounded task-contract's done-criteria makes it RETURN, and a worker never spawns its own workers (Step 3).
- P7 — Never eyeball a verify — RUN the objective check, and never let a worker grade its own output (Step 3.4; binds a SELF result too — Step 2).
- P8 — Never let a cheap main QA an escalated expert's work — the strong worker owns the hard task AND its integration (Step 3.5).
- P9 — Never delegate the final user-facing deliverable (translation / summary / research prose) to a cheaper tier while
preserveVoiceForUserFacing is true — bulk MECHANICAL work (renames, formatting, codegen) stays delegable (Step 2).
- P10 — Never escalate/climb on high-by-SIZE-alone mechanical bulk (size is the EFFORT knob, difficulty is the TIER knob) — EXCEPT a whole-repo audit / bug-scan / security-review, which is capability-driven breadth: never delegate THAT down-to-cheap, and if YOU are the floor tier never SELF it — escalate UP (Step 1).
- P11 — fast-mode is never a routing rung — attach it only on an explicit human latency request, and only if
fastModeOnLatencyRequest (Step 2).
- P12 — Never fan out several workers near a budget/quota edge for marginal, context-light mechanical work the main can do inline — a worker that dies on the limit returns NOTHING (Step 2 budget gate · F9).
- P13 — With a stakes signal present or CoalBoard convening this turn, never escalate independently and never double-prompt — CB leads, CT becomes its tier-lever; no CoalBoard installed / not fired this turn → this rule is INERT (Step 2).
- P14 — On a platform whose spawn tool takes no worker-model parameter, never pretend to route (the platform gate above).
- P15 — A side-effecting step (bash mutation, external call, commit) is never delegated and never retried — retry = doing it twice (
references/damage-control.md).
- P16 — A self error-report is OFFERED, never auto-submitted — and never includes unapproved code or paths (Self error-report section).
- P17 — Never clobber an existing memory-anchor file — load + append/crystallize only (
references/damage-control.md, write-policy).
Fail routes — F1–F11 (the complete set)
A recovery move inside a Step or a reference and not listed here is that step's mechanics, not an additional route.
- F1 — a spawn ERRORS instantly (model unavailable / disabled / out of quota / gone) → add that model to
blocked → resolveWorker(ranking, desiredTier, {blocked, floorTier}) → spawn the returned worker → repeat on each fresh error (Step 3.3).
- F2 —
resolveWorker returns null (everything blocked down to the floor) → STOP: a SENSITIVE task hands back or waits for reset — never below its floor (P1); a non-sensitive task → do it yourself if you can clear the bar, else hand back honestly (Step 3.3 · damage-control).
- F3 — verify lands BELOW the bar but near it, attempts remain → climb ONE rung, cheapest lever first (EFFORT → VERSION → TIER; never re-try the same model) (Step 3.4; binds a SELF result too — Step 2).
- F4 — verify FAR below the bar, or out of
maxTotalAttempts → jump straight to the top tier; at the top and still failing → hand back to the user (Step 3.4; binds a SELF result too — Step 2).
- F5 — the failure mode is un-verifiable (crypto timing, numerical precision, security — plausible-but-wrong PASSES your check) → JUMP to the reliable tier, never climb through plausible-wrong rungs (Step 2).
- F6 — a worker silent past
subagentTimeoutSeconds → treat it as failed and re-route (damage-control).
- F7 — fable consent-declined ("no") → do NOT climb into fable: STAY at the current tier (a climb case), or land on the rung below fable (a grade-5
reasoning start — the same resolveWorker fall, blocking the RESOLVED name); the cap holds even if qualityBar is unmet. An UNAVAILABLE fable is not a decline — it just falls (F1), no ask (Step 2).
- F8 — ranking missing / corrupt / incomplete → rebuild on the spot per
references/lock.md; genuinely unbuildable → routing OFF, work normally (Step 0).
- F9 — near a session/quota limit BEFORE spawning → collapse to inline-self (the proactive budget gate;
resolveWorker is the reactive fall once a spawn is blocked) (Step 2).
- F10 — a delegation dies mid-run → the worker's proposal is in
proposed/, finished subtasks in state.json → resume the remainder, never redo done work (damage-control).
- F11 — you KNOW from the start the task exceeds your ceiling → escalate immediately, don't burn an attempt (Step 3.4).
Files — the on-disk set (a near-twin pair, declared once)
A dot-FILE is CONFIG, a dot-DIR is STATE — the pair exists at both levels; do not read one as a typo of the other. This is the complete set a ROUTE touches. The conductor's own update-check stamp (~/.claude/coal/coaltipple/update-check) belongs to the SessionStart self-update channel, not to this contract.
- Config (read on every route): global
~/.claude/.coaltipple.json (unchanged) overlaid by the FIRST-FOUND project config — <gitroot>/.claude/coal/coaltipple.json → .agents/coal/coaltipple.json → .gemini/coal/coaltipple.json → LEGACY <gitroot>/.claude/.coaltipple.json — every bare ".coaltipple.json" in this file means this MERGED pair (merge rules in Always). <gitroot> = the nearest ancestor holding .git; no .git anywhere above → <gitroot> is just the starting directory itself (findGitRoot, config-load.mjs).
- State:
~/.claude/coal/coaltipple/ (global dir) holds ranking.json — READ at spawn-time only (a SELF route never reads it), WRITTEN only on a rare rebuild, atomically (F8) · <gitroot>/.claude/.coaltipple/ (project dir) holds proposed/ (the delegation sandbox workers write proposals into) + state.json (the subtask journal) — written only during a delegation (F10).
- Writes driven by asks: ASK 1's "always" persists
fableConsent: true into the PROJECT config FILE (the resolved path per the Config bullet's read-order above — edit the key directly; scripts/ ships only with the repo source, not the installed plugin, so node scripts/configure.mjs --project --fableConsent true is a convenience for a source checkout, never the only path) · ASK 3 may create/choose a memory-anchor file (the user's own file — append, never clobber, P17) and persists contextFiles / memoryOffer.
- A SELF route writes NO CoalTipple artifact — the edit lands in the user's files directly;
proposed/ is the DELEGATION recovery net, not self's (self recovery = your platform's normal undo / git).
Config — the knob set (defaults; full reference = README Configure / config-schema.mjs) — all 24 config keys in 23 rows (updateMode·updateCheckDays share one).
| key |
default |
gates |
enableRouting |
true |
master switch (false = routing + conductor silent) |
mode |
auto |
allowed DIRECTIONS: delegation = down-only · escalation = up-only · off; P1 overrides every mode (Step 2) |
qualityBar |
60 |
the acceptance bar a result must clear (Steps 1 · 3) |
delegateMinLines |
120 |
the delegate-down floor (Step 2) |
maxTotalAttempts |
2 |
staircase budget before jump-to-top (F4) |
subagentTimeoutSeconds |
150 |
stall reap (F6) |
maxConcurrentSubagents |
4 |
fan-out cap (ASK 2) |
requireTaskContract |
true |
the Step 3.1 contract |
qaOnMerge |
standard |
Step 3.4 verify rigor (strict / standard / off) |
fastModeOnLatencyRequest |
true |
P11 |
preserveVoiceForUserFacing |
true |
P9 |
keywords |
the factory groups |
grade floors + sensitive / preserveVoice flags (Step 1 lists the factory numbers) |
hotKeywords |
[] |
legacy flat list — merges as a grade-4 sensitive group |
sensitivePaths |
auth · crypto · payment · billing · migration · secret · token · password · session · security |
path fragments forcing grade ≥4 + sensitive; a config UNIONS with these, never replaces them |
excludePaths |
node_modules · .git · dist · vendor · build · .next · coverage |
dirs never counted toward grading breadth |
disableRouting |
[] |
per-domain routing OFF: coding / text / math / research, or 'all' (Step 2) |
contextFiles |
[] |
the memory anchor (ASK 3) |
memoryOffer |
auto |
the anchor offer switch (ASK 3) |
fableConsent |
false = ask |
ASK 1's standing consent (project-persisted) |
modelTiers |
{} |
human pins over the alias floor (Step 0; P3) |
language |
auto |
the user-facing output language (Always) |
updateMode · updateCheckDays |
ask · 14 |
the conductor's self-update channel (SessionStart — not a rail of this file) |
gitRecoveryBoundary |
auto |
git commits as an extra recovery boundary (damage-control) |
References — when each loads — these two are the complete reference set.
| reference |
when |
references/lock.md |
on-demand — ONLY to REBUILD a missing/corrupt/incomplete ranking (F8), or for pin/fall/safety depth; a normal route never opens it |
references/damage-control.md |
MANDATORY at its moments — a delegation failing mid-run (F2 detail · F6 · F10) · the memory-anchor flow (ASK 3) · a self error-report (the OFFER) |
Grants & denials (CLASSIFY-BLOCK — declared)
| class |
step it powers |
grant |
on denial |
| read |
the merged config, the ranking, memory-anchor files, state.json/proposed/ (F10 resume) |
Read |
an unreadable file is NOT the same as an absent one — never fall back to schema defaults silently: an unbuildable ranking → routing OFF (F8); an unreadable config → treat as mode:'off' (one of THE FRAME's five declared states, never a sixth); an unreadable journal → say the F10 resume net is not active this run, before delegating |
| write |
ASK 1's fableConsent persist, ASK 3's memory-anchor file, the delegation journal (proposed/, state.json) |
Write/Edit |
report the write failed, never claim it applied — a failed "always" persist means you WILL be asked again next spawn, say why (never let a silent re-ask read as a config bug); a failed journal write means F10's resume net does not exist this run |
| spawn |
delegate-DOWN / escalate-UP (Step 3) |
Agent/Task (or Bash for a claude -p dispatch — its OWN grant) |
a refused GRANT is not F1's unavailable-MODEL fall — say the delegation could not run and why, before landing on SELF; a silent SELF here is byte-identical to the routine SELF outcome and hides that routing never happened |
A denial reaches the WORKER as a visible message and propagates NO further — not to the dispatcher, not as a catchable condition. Every row above states a branch or an explicit death; a step that dies says so in the output. Never report a denied step as done, skipped, or clean. No network row — the routing pipeline fetches nothing (the self-update check is the conductor's SessionStart channel, not this contract — Asks ledger, NOT-asks bullet). Grant denials are a SEPARATE mechanism from the F1–F11 fail-routes and P1–P17 prohibitions above (permission-level, not a routing decision) — both ledgers' counts are unchanged.
Step 0 — the Lock (a valid ranking gates any SPAWN)
Routing that will SPAWN needs a tier ranking — the ranking is READ at spawn-time (Step 3), never per prompt: a SELF route reads nothing, and an idle session pays nothing. It is dead simple — no introspection, no model-list enumeration, no refresh cadence:
- The ranking IS the alias floor + your pins. Tiers = the Claude aliases
haiku < sonnet < opus < fable → low / mid / heavy / reasoning (fable = the top rung above opus — a real-money spawn, consent-gated before use; ASK 1, Step 2), overlaid with your modelTiers pins (merged config). The platform resolves each alias to its current best model AT SPAWN-TIME, so the floor never goes stale. When a route will spawn, read the GLOBAL shared ranking ~/.claude/coal/coaltipple/ranking.json (a cheap file read). You do NOT enumerate the live model list.
- Unknown model →
heavy, never cheap. Any model you do not recognize from the floor/pins is a STRONG tier (the safe over-provision). Routing keys off this tier STRUCTURE, not an exact list — a vendor 5→10-model shuffle never breaks it.
modelTiers pins are the one human override for a model released after your cutoff (you cannot see it): the user names it in the merged config and it wins front-of-tier.
- A failed spawn FALLS (
resolveWorker, Step 3 · F1): availability is knowable ONLY at spawn-time, never from a catalog.
- If a ranking genuinely cannot be built → routing OFF: just work normally (F8). The skill never breaks — it routes, or routing is off.
→ Rebuild recipe (only when ranking.json is missing / corrupt / incomplete — rare), the pin/fall mechanics, and why every degradation mode is safe: read references/lock.md. A normal route just reads the file; it never rebuilds.
Step 1 — Grade the task (deterministic, not self-assessment)
Grade 1–5 by the rubric — size + sensitive-path + keyword, never "it feels easy" (a cheap model is overconfident):
| Grade |
Signal |
| 1 |
read / search / summarize — the no-signal base: no file edited, no content produced (sizeUnits), no keyword match |
| 2 |
editing even ONE file floors the grade here at minimum · ≤1 file, <500 lines, not sensitive |
| 3 |
2–3 files / 500–1000 lines |
| 4 |
>3 files / ≥1000 lines / sensitive path (auth, crypto, payments, migrations) — forces ≥4 even at 1 file |
| 5 |
concurrency / mutex / race / crypto-logic / timing-attack / proof — forces 5 regardless of size |
Every signal is a FLOOR and the grade is the HIGHEST floor fired — deterministic, no tiebreak judgment (the rubric composes size + sensitive-path + keyword floors with max, as grade.mjs does): a 1-file edit (floor 2) whose prompt matches a grade-4 group grades 4 — the per-grade floor conditions are the table above, not restated here.
Size = content size (general-purpose): code counts lines/files · text/translation/research/docs count words/chars (same thresholds). Difficulty (keyword/sensitive) ALWAYS overrides size — a tiny-but-hard task (1 line of crypto, a short legal-contract translation) = high grade.
Keyword groups are config-tunable (keywords, merged config) — honor them. The FACTORY groups and their grade floors (the shipped values; a config overrides PER GROUP — it may add words or RAISE a built-in group's grade, never lower it below the factory floor, and never strip a built-in sensitive/preserveVoice flag): concurrency 5 · crypto 5 (sensitive) · security 4 (sensitive) · coding 4 (sensitive — its words are the migration / schema-change / payment / billing / rate-limit class, NOT "any code": a group fires on its WORDS/intent, never on the task's general domain, so an ordinary code edit matches nothing) · audit 4 · math 5 · knowledge 3 · domain 4 (sensitive) · creative 2 (preserveVoice); legacy hotKeywords merges as a grade-4 sensitive group. A prompt matching a group floors the grade to the group's number and inherits its flags; an omitted group keeps the built-in. The conductor's 0-token hint reflects only the built-ins, so YOUR read of the merged config is the authority. Match a group by the task's INTENT in ANY language — a bug-scan / audit / security-review matches audit by MEANING, not a literal English word (the English words are only examples + the conductor hint).
Read WHY the grade is high — it sets the DIRECTION (the grader's reasons tell you which):
- High by DIFFICULTY (sensitive path / hot keyword) → escalate-UP: you need a stronger tier for correctness.
- High by SIZE alone (
reasons show only size: N, no sensitive/keyword — a 40-file rename, 48 CRUD handlers, bulk codegen/translation) → a high-EFFORT signal, NOT high-tier → delegate-DOWN to a cheap tier + high effort. The size threshold inflates the grade to stop you under-routing big complex work; once you see the work is MECHANICAL, route it DOWN. Never escalate-up / climb-the-staircase on bulk-mechanical work just because breadth bumped the grade (P10) — bulk is the EFFORT knob, difficulty is the TIER knob.
- EXCEPTION — a whole-repo audit / bug-scan / security-review / code-review is high-by-DIFFICULTY, not size (P10).
If the conductor injected a hint, respect it.
qualityBar (config 0–100) — the acceptable-quality bar: grade picks the starting tier (Step 2); qualityBar is the line the result must clear, or routing climbs the ladder (Step 3). Default 60.
- Plain meaning: "accept work of quality ≥ qualityBar%".
90 = critical, must be exact · 60 = competent/serviceable (default) · 40 = cheap-and-cheerful.
- 0 = anything passes → stay at the starting tier, never climb. 100 = almost nothing passes below the top → climb to the top (except a task the lower tier already does perfectly — trivial → no climb). The extremes emerge from the climb mechanism; nothing is hardcoded.
Step 2 — Route (decide)
HARD GATE, checked before the grade or anything else counts — sensitive = NEVER delegate-DOWN, and this does NOT depend on your grade (the single most important safety rule): if the task touches anything sensitive/security — crypto, timing-attack, constant-time, auth, authorization, payment, secret, token, password, session, security (the grader + conductor flag these, plus any keywords group you marked sensitive) — then delegate-DOWN is FORBIDDEN, whatever number you graded it. Do NOT gate this on grade ≥4/5: a weaker main UNDER-grades sensitive work and slips past a grade-gated rule. The keyword is the gate, not your grade (P1). Sensitivity keys on the PATH + the task's MEANING, never on the edit type — a "comments-only" / "just a typo" edit on an auth/crypto/payment path is still sensitive; DOWN is forbidden, SELF is fine. For sensitive work: escalate UP, keep it on main, or use a vetted built-in (crypto.timingSafeEqual) — never delegate-down, never hand-roll security on a cheaper tier (a plausible-but-wrong timing leak PASSES your tests). "Keep it on main" presumes your OWN current tier already clears the sensitive SAFE FLOOR — heavy, fixed regardless of grade (the fable bullet below: "the cap holds at its safe floor tier heavy, never below"). Placing yourself on that floor is SELF-IDENTIFICATION, not the barred live-model enumeration (P5) — you already know your own name/family; match it against the SAME alias floor Step 0 defines (haiku < sonnet < opus < fable). If you genuinely cannot place yourself there, don't assume the floor clears — escalate instead, the same fail-toward-safe posture Step 0 uses for an unrecognized WORKER model. This is NOT the grade table's per-grade CEILING, a different and higher number at grade 5 (strongest/fable; the two only coincide at grade 4, where both happen to be heavy). SELF is the allowed DESTINATION once you clear the FLOOR — it never requires reaching the CEILING. If your own tier sits below heavy, staying SELF is not on the table: escalate (spawn UP) at least to heavy — same as "forces escalation off the current cheap tier" below (Step 2, tier-ceiling section), the SAME rule seen from underneath, not a second rule. Holds under a QUOTA / limit-hit too: if the safe tier is quota-blocked, a sensitive task WAITS for reset, stays on a capable main, or hands back — it does NOT fall to a cheaper tier (the limit-hit fallback floors sensitive work at its safe-minimum — see Damage control). A modelTiers pin cannot lower this floor (P3): a KNOWN-weaker alias (haiku/sonnet) pinned into a high tier never satisfies the gate — skip it and take the next known-strong model in that tier (resolveWorker enforces this on the fall); only an UNKNOWN, unseeable pin (a model released after your training cutoff) is trusted there. (fable is a KNOWN top rung — it qualifies for a sensitive slot by capability, and never-down still holds.)
Unknown path/meaning (you genuinely can't yet tell if it's sensitive) → ASSUME sensitive until confirmed otherwise: inspect the file's imports/callers (both directions) or ask the user; a HARD GATE with an unresolved default fails toward the safe side, never toward speed.
The keyword flags are ENGLISH-ONLY, so on a NON-ENGLISH task YOU are the sensitive-gate authority (P2): a Thai/CJK/Arabic/Cyrillic prompt — OR a Latin-script one (Spanish/French/German/Portuguese/Indonesian) — meaning "constant-time compare" / "auth bypass" matches NO English literal, so no flag fires and the grade reads trivial; a grade-gated reading would let the never-down backstop vanish. Do NOT rely on the flag for a non-English prompt: apply the never-down gate by the task's MEANING / intent. The conductor's resident routing contract reminds you to grade sensitivity by MEANING in ANY language, and it adds a per-turn nudge ONLY when it detects a non-Latin SCRIPT (Thai/CJK/…) — a Latin-script non-English prompt trips neither the keyword flags nor that script nudge, so YOUR read of the intent is the real gate. The gate fires on MEANING; the keyword is only its English fast-path.
mode (config, default auto) sets the DIRECTIONS you may route — honor it (the sensitive HARD GATE overrides it):
auto (default) = route BOTH directions per grade.
delegation = budget mode — delegate-DOWN allowed, escalate-UP SUPPRESSED (miss the bar → do it yourself / hand back, don't climb).
escalation = quality mode — escalate-UP allowed, delegate-DOWN SUPPRESSED (never push work down to save tokens).
off = routing OFF — do it yourself (the conductor also short-circuits its forcer on mode:"off").
- Override: a SENSITIVE task obeys the hard gate under ANY mode; escalate-UP for sensitive work is always permitted even in
delegation mode (safety beats the budget knob).
Per-domain disableRouting (config) — routing OFF for a matched domain: if the graded task's domain (coding / text / math / research) is in disableRouting, OR the list includes 'all', routing is OFF for THIS task — do it yourself. Map the task to its domain by nature (code→coding; translation/docs/prose→text; proofs→math; citation/claim→research). (The conductor only short-circuits on 'all'; per-domain matching needs the graded domain, so it is YOUR check.)
Double-hook stand-down (CoalBoard may fire the same turn — P13): a stakes signal (security/crypto/migration/money) present, or CoalBoard already convening, means do NOT escalate independently — defer to CB: it leads, you become its tier-lever (its lenses run at the tier you'd have picked), one consent, never double-prompt. At the top tier you can't climb further anyway, so yield there too — CB's go-wide is the only escalation left. No CoalBoard installed / fired this turn → this rule is INERT: the sensitive hard-gate above governs alone (escalate UP as usual — CB is an optional sibling, never assumed).
Two knobs: TIER (coarse, burns Opus quota) × EFFORT (fine, cheap, low→max). Escalate cheapest-first, EFFORT → VERSION → TIER: raise EFFORT (same model, think/iterate harder), then VERSION (a stronger model of the SAME tier, e.g. Opus 4.6→4.8 — cheaper + closer than a tier jump), then TIER. Exhaust each lever before the next; never skip to a higher TIER while a stronger same-tier version is untried (Opus quota is scarce; once hit it is hard-blocked).
| Grade |
Move |
Tier ceiling (EFFORT is set by OUTPUT SIZE — the rubric below — never by the grade) |
| 1–2 |
delegate-down (only if large — see floor) else do it yourself |
low |
| 3 |
do it yourself or delegate |
mid — exhaust EFFORT, then VERSION, before raising tier |
| 4 |
escalate-up (if difficulty-driven) / delegate-down (if size-driven + mechanical; but if YOU are the floor this is SELF, no "down" exists) |
heavy · sensitive → never down |
| 5 |
escalate-up / do it yourself |
strongest |
- The tier column = the CEILING, not the START — climb the staircase to it: escalate ONE rung at a time and verify each (Step 3); a lower rung often clears the bar (cheap tiers are more capable than they look). Reach the ceiling only when a lower rung FAILS verify; jump straight to the top on far-below / out-of-attempts (
maxTotalAttempts) OR when the failure mode is subtle / un-verifiable — crypto (timing leaks), numerical precision, security — where a cheaper tier's plausible-but-wrong output would PASS your check (F5). Rule: climb when verify is your safety net; JUMP to the reliable tier when verify can't catch the failure. Don't pre-jump to the top just because the grade is high (that over-provisions TIER). (A sensitive/hard grade still forces escalation OFF the current cheap tier — climb at least one rung; delegating sensitive DOWN is already forbidden above.)
- Fable costs real money — ASK 1 gates it (Step 2). On "no": do NOT climb into fable — stay at the current tier: the rung below fable (the top NON-fable tier — opus/
heavy today, but READ it from the ranking, never hardcode opus, since a future model landing between opus and fable would become the new cap). Declining the money CAPS the climb here even if qualityBar is unmet — run the worker at that tier and accept its result, don't climb further (F7). That rung is exactly where a blocked fable re-resolves (resolveWorker, the SAME fall an unavailable fable takes), so a grade-5 reasoning START with no rung-below drops there too: block the RESOLVED model (the exact name that tripped the ask — a pinned concrete Fable release id, NOT merely the literal fable, which would re-resolve + re-serve the pin) and keep declining while isFableModel stays true. Sensitive work keeps its never-down floor (the cap holds at its safe floor tier heavy, never below). A blocked/unavailable fable spawn falls the same way with no ask.
- EFFORT tracks OUTPUT-SIZE/iteration, NOT difficulty — the two knobs are independent: TIER is the correctness lever (difficulty → which model); EFFORT is the size lever (how much to produce/iterate). Reserve max for genuinely LARGE / deep work (a formal proof, a big refactor). Burning max on a tiny hard fn is the over-provisioning trap — the TIER already bought correctness; effort only sizes the work.
- Effort rubric (by OUTPUT SIZE):
low = a lookup / ≤30 lines / 1-2 sentences · medium = a function + tests / a short proof / a paragraph · high = a multi-case proof / a multi-file change / a long document · max = a formal proof all-cases / a big refactor / an exhaustive sweep.
- Effort is the ALWAYS-ON lever (it pays even when TIER cannot move): at the top tier, or with only one tier available (cheaper tiers off, or a single-model user), you cannot escalate TIER but STILL optimize by scaling EFFORT per task (low for easy, high for hard). A self-executing top main IS still routing: it pins the tier and tunes effort. "Pins the tier" on SELF means a CEILING on any worker you might spawn from here, never an instruction to self-downgrade — you are already whatever model you are and cannot change that; the pin only bounds a future spawn.
- Delegate-down FLOOR (most important): spawning a worker has a high fixed overhead (~tens of thousands of tokens — measured). Delegate down only for a task big enough that doing it yourself would cost far more than the overhead (≥
delegateMinLines, default 120). Small-to-medium → do them yourself (offloading loses tokens). delegateMinLines is a CODE-line proxy — cheap-per-line work (translation, formatting, plain text) burns far fewer tokens/line, so it needs proportionally MORE than its raw line count before delegating pays. Judge real effort vs overhead, not the line count.
- Budget gate before you fan out (the proactive limit-hit guard) · spawning is the biggest spend you control: a worker pays off only when the task clears the floor AND there is clear BUDGET HEADROOM. Near a session/quota limit, or right after a limit-hit, the cheapest correct route is often NO worker: collapse to inline-self (F9). Above all, do NOT fan out SEVERAL workers at once for marginal-value, context-light, mechanical work (a multi-file grep/compare, a small audit, a consistency sweep) the main can do inline (P12) — N spawns × ~tens-of-thousands near a budget edge IS the limit-hit, and a worker that dies on the limit returns NOTHING (partial findings unrecoverable).
resolveWorker is the REACTIVE fall once a spawn is blocked; THIS is the proactive gate before you spawn at all.
- If YOU are the floor tier (cheapest — e.g. main is Haiku): no tier exists below you, so delegate-DOWN is off the table. A big size-driven mechanical task (the one the grade table routes "delegate-down" for a higher main) has NO down-target → it collapses to SELF: do it yourself and CALL it self (a "delegate-down to myself" label is the self-contradiction that then slips the work UP to a costlier tier). Cheap/mechanical work you can do → do it yourself. Work beyond your competence (hard / sensitive / too-big-context) → escalate UP (don't keep a hard task on the floor just because "it isn't sensitive" or you hold the context). At the floor: self-for-cheap, up-for-hard.
- Context floor (the worker starts FRESH) — distinguish two kinds of "big context":
- Accumulated CONVERSATION context (files read, decisions, the user's intent, the back-and-forth) — NON-transferable; a fresh worker can't see it. Work whose correctness depends on it → keep on main (compressing it into a contract is lossy + expensive). This is the real context-floor.
- On-disk / re-readable INPUT (a codebase, a large document, logs) — a worker CAN read it itself from a path in the contract. A large on-disk input is NOT a reason to keep work on main — it's a reason to pick a worker with a big-enough context VARIANT (Step 0): choose TIER by difficulty, VARIANT by input size, independently. (Exception: if you ALREADY hold the input in a large-context main, keeping it there is free.)
- ULTRACODE (max effort + multi-agent fan-out) = the top rung, for genuinely broad + independent work only; workers run a cheap tier, you orchestrate (cap concurrent at
maxConcurrentSubagents, default 4 — they share one rate limit). Spawning more than one worker in one move is ASK 2 — state the plan (how many × tier × effort) and get a yes first. fast-mode is not a routing choice (P11) — attach it only on an explicit human latency request, and only if fastModeOnLatencyRequest (default true); never as a routing rung.
- Voice: when
preserveVoiceForUserFacing (default true), never delegate the final user-facing deliverable to a cheaper model (P9) — INCLUDING a translation, summary, or research write-up (the prose IS the deliverable), even when bulky (reviewing it to protect voice + terminology costs about as much as redoing it). Bulk MECHANICAL work (renames, formatting, codegen) is not "deliverable prose" and stays delegate-able.
- A SELF result faces the same bar (P7 binds you too), UNCONDITIONALLY —
qaOnMerge does not gate it (qaOnMerge only tunes a DELEGATED worker's merge rigor, per its own config help text; off skips a worker's merge-verify but never skips your own): verify your own output with the same domain-appropriate check as a worker's (Step 3.4), sized to the change — a code edit runs the build/tests, a text/comment edit re-reads the diff for completeness + correctness; eyeballing your own work is still not verifying. Below qualityBar → the staircase applies to YOU: raise effort, then escalate (F3). SELF work edits the user's files in place — no .claude/.coaltipple/proposed/ sandbox (Files ledger).
Step 3 — If you delegate
- Task contract (required when
requireTaskContract, default true): give the worker goal + constraints + interface + done-criteria. A thin brief makes a cheap worker build the wrong thing with confidence. Keep it compact.
- Spawn at the chosen tier/effort — lead the spawn's description/label with the chosen model + effort so the human sees what is running (e.g.
[haiku·low] scaffold 48 handlers, [opus·max] formal proof, [sonnet·high] refactor auth; CC renders the description as the task chip). Require a compact diff/summary, not whole files. The worker MUST be BOUNDED — never spawn an open-ended worker that can loop (P6): the done-criteria (item 1) makes it terminate (a "keep improving" brief runs forever). subagentTimeoutSeconds (default 150) catches a STALL (F6); the done-criteria prevents the LOOP.
- Spawn-fail-fall — the never-fail availability loop (drives
resolveWorker; F1–F2): a spawn can ERROR instantly (0-token) because the model
…(truncated)
1---2name: coaltipple3description: Model/effort router, ALWAYS-ON via its conductor hook: every prompt is routed, and routing returns exactly one of THREE outcomes — SELF (do it yourself at a pinned tier/effort; the routine result — routing that lands on self is still routing), delegate-DOWN (a large cheap mechanical task goes to a cheaper tier to save tokens), or escalate-UP (a task beyond the current model's competence goes to a stronger tier for quality). Sensitive/security work is NEVER delegated down, whatever its size. Invoke manually when weighing where a task should run. Claude Code only -- the platform's spawn tool must accept a worker model parameter (Antigravity etc. cannot actuate this); support reviewed monthly.4---56# CoalTipple — routing contract78**THE FRAME — ALWAYS-ON, THREE OUTCOMES.** This contract applies to EVERY prompt (the conductor injects a routing forcer each turn; there is no trigger condition to meet): every task is ROUTED, and routing returns exactly one of three outcomes — **SELF** (do it yourself, tier pinned + effort tuned — the most common result), **delegate-DOWN**, or **escalate-UP** — UNLESS a structural off-state is already declared: the platform gate below, `enableRouting:false`, `mode:'off'`, a matched `disableRouting` domain, or a ranking that genuinely cannot be built (F8) — exactly five states, never a sixth you invent. Those are DECLARATIONS the pipeline reaches or the config/platform sets, not a decision you make per task. **A declared off-state stands down the WHOLE contract, not only the 3-outcome decision — no grade, no Asks, no P1–P17, no Step 2 HARD GATE, no P7 verify-obligation: do the task as a plain agent.** **"The skill doesn't apply" is never a decision YOU make because a task looks small or out of scope:** a task too small to delegate still runs the FULL pipeline — Step 1's grade AND Step 2's sensitive HARD GATE (the gate is grade-INDEPENDENT, checked whatever the grade says, never grade-then-gate) — before landing on SELF. Skipping the pipeline because a task looks small is exactly how a small-LOOKING task on a sensitive path slips past the never-down gate.910> [!CAUTION]11> **PLATFORM GATE -- actuates on Claude Code ONLY.** Routing spawns a worker at a CHOSEN model + effort, which needs a spawn tool that accepts a worker **model** parameter. **Works: Claude Code** (`Agent`/`Task` takes `model`). **NOT SUPPORTED: Antigravity** -- `invoke_subagent`'s `Model` field DOES pick a per-spawn tier (proven live 2026-08-04), but it selects a GOOGLE model regardless of the Claude parent's vendor, and no effort knob exists anywhere in the schema -- CT's never-down gate, qualityBar staircase, and Claude alias floor don't map onto that shape. On a platform with no agent-selectable worker model at all, **do NOT pretend to delegate-down or escalate-up** -- the spawn cannot change tier; routing is a silent no-op. Not on Claude Code -> routing is OFF, one of THE FRAME's five declared structural off-states, never a per-task decision — the skill still degrades safely. (Reviewed monthly.)1213> **Degrades safe on any CC version:** an unknown model classifies as a strong tier (never cheap), a failed spawn falls to the next available, and the platform resolves each alias to its current best model at spawn-time — a CC update never breaks routing.1415You are **main** (depth-0): you decide routing + spawn everything yourself. **Workers are leaves BY POLICY — give a worker a bounded task-contract (its done-criteria make it RETURN, never open-endedly spawn its own workers); a worker that fails RETURNS its result for main to re-route.** This holds whether or not the platform caps nesting — robustness rides on bounded task-contracts + the spawn-fail-fall (Step 3), never on the platform forbidding a worker to spawn.1617> This contract is English so any model can read it; user-facing output follows the user's language (translate prose, technical terms verbatim — see **Always**).1819## The ledgers — the countable rails (TRANSCRIBE these; never re-count from prose)2021Six lists own the COUNT + MEMBERSHIP of the asks, the absolute prohibitions, the fail routes, the on-disk files, the config keys, and when each reference loads. The Steps carry the execution mechanics; every "how many / which ones" question is answered by these lists verbatim.2223### Asks — exactly 3, numbered (+ ONE standing offer)2425- **ASK 1 — FABLE (real money), conditional (Step 2).** Fires when the RESOLVED worker is a fable-family model (`isFableModel` — a substring match catching the alias `fable` AND any pinned concrete Fable release id, version-independent by design) AND `fableConsent` is not already `true`. Options: **once** (this spawn only) · **always** (persist `fableConsent: true` to the project config) · **no** (do not climb into fable — F7 caps the route at the rung below). One worker, one ask, no cost math. Never fires when routing lands on SELF or a non-fable tier.26- **ASK 2 — FAN-OUT (more than one worker in one move — the ULTRACODE rung, or any parallel delegation; Step 2).** Before spawning SEVERAL workers at once, state the plan (how many × tier × effort, capped at `maxConcurrentSubagents`) and get a yes. A single worker is NOT this ask.27- **ASK 3 — MEMORY ANCHOR (at most once per session; Memory anchor section).** Fires only when ALL hold: about to delegate context-DEPENDENT work · no anchor exists (`contextFiles` empty AND no platform `CLAUDE.md`/`AGENTS.md` the worker would inherit) · `memoryOffer` is `auto` · not yet offered this session. Options: **Create** / **Choose** / **Skip** (Skip persists `memoryOffer: 'off'` — never re-asks).28- **OFFER — SELF ERROR-REPORT (an offer, not a consent gate):** when CoalTipple itself misbehaves → STOP, summarize, OFFER to file an issue (P16; Self error-report section).29- **NOT asks (add nothing to the count):** a SINGLE non-fable worker spawn — standing consent is your `mode` config, and its transparency is the visible `[model·effort]` spawn label (Step 3.2), not a question · the qualityBar climb (automatic, bounded by `maxTotalAttempts`) · the self-update ask/reminder (the conductor's SessionStart channel, governed by `updateMode` — never fired by this file).3031### Absolute prohibitions — P1–P17 (the complete set)3233A "never" inside a Step and not listed here is that step's sequencing mechanics, not an additional absolute.3435- **P1 — Sensitive work is NEVER delegated DOWN** — whatever the grade, under every `mode`, and under a quota/limit-hit too (wait for reset / stay on a capable main / hand back — never fall cheaper; a vetted built-in beats hand-rolling). Sensitivity is a property of the PATH + the task's MEANING, never of the edit type — a "comments-only" / "just a typo" edit on an auth/crypto/payment path is still sensitive. The gate forbids DOWN only: SELF stays allowed (Step 2 HARD GATE).36- **P2 — Never read "no keyword flag fired" as "not sensitive"** — the flags are English-only; the gate fires on MEANING in ANY language, and YOU are the sensitive-gate authority on a non-English task (Step 2).37- **P3 — A `modelTiers` pin never lowers the sensitive floor** — a KNOWN-weaker alias pinned into a high tier does not satisfy the gate (a pin proves availability, never capability); only an UNKNOWN, post-cutoff pin is trusted there (Step 2).38- **P4 — Never spawn a fable-family worker without consent (ASK 1)**; on "no", never climb into fable for this task, block the RESOLVED model name (not merely the literal `fable`), and never hardcode `opus` as the cap — the cap is the rung below fable, READ from the ranking (Step 2 · F7).39- **P5 — Never route on a broken ranking, and never enumerate the live model list** — the ranking is the alias floor + pins; genuinely unbuildable → routing OFF, do it yourself (Step 0 · F8).40- **P6 — Workers are LEAVES: never spawn an open-ended worker** — a bounded task-contract's done-criteria makes it RETURN, and a worker never spawns its own workers (Step 3).41- **P7 — Never eyeball a verify — RUN the objective check, and never let a worker grade its own output** (Step 3.4; binds a SELF result too — Step 2).42- **P8 — Never let a cheap main QA an escalated expert's work** — the strong worker owns the hard task AND its integration (Step 3.5).43- **P9 — Never delegate the final user-facing deliverable (translation / summary / research prose) to a cheaper tier** while `preserveVoiceForUserFacing` is true — bulk MECHANICAL work (renames, formatting, codegen) stays delegable (Step 2).44- **P10 — Never escalate/climb on high-by-SIZE-alone mechanical bulk** (size is the EFFORT knob, difficulty is the TIER knob) — EXCEPT a whole-repo audit / bug-scan / security-review, which is capability-driven breadth: never delegate THAT down-to-cheap, and if YOU are the floor tier never SELF it — escalate UP (Step 1).45- **P11 — fast-mode is never a routing rung** — attach it only on an explicit human latency request, and only if `fastModeOnLatencyRequest` (Step 2).46- **P12 — Never fan out several workers near a budget/quota edge for marginal, context-light mechanical work** the main can do inline — a worker that dies on the limit returns NOTHING (Step 2 budget gate · F9).47- **P13 — With a stakes signal present or CoalBoard convening this turn, never escalate independently and never double-prompt** — CB leads, CT becomes its tier-lever; no CoalBoard installed / not fired this turn → this rule is INERT (Step 2).48- **P14 — On a platform whose spawn tool takes no worker-model parameter, never pretend to route** (the platform gate above).49- **P15 — A side-effecting step (bash mutation, external call, commit) is never delegated and never retried** — retry = doing it twice (`references/damage-control.md`).50- **P16 — A self error-report is OFFERED, never auto-submitted** — and never includes unapproved code or paths (Self error-report section).51- **P17 — Never clobber an existing memory-anchor file** — load + append/crystallize only (`references/damage-control.md`, write-policy).5253### Fail routes — F1–F11 (the complete set)5455A recovery move inside a Step or a reference and not listed here is that step's mechanics, not an additional route.5657- **F1** — a spawn ERRORS instantly (model unavailable / disabled / out of quota / gone) → add that model to `blocked` → `resolveWorker(ranking, desiredTier, {blocked, floorTier})` → spawn the returned worker → repeat on each fresh error (Step 3.3).58- **F2** — `resolveWorker` returns `null` (everything blocked down to the floor) → STOP: a SENSITIVE task hands back or waits for reset — never below its floor (P1); a non-sensitive task → do it yourself if you can clear the bar, else hand back honestly (Step 3.3 · damage-control).59- **F3** — verify lands BELOW the bar but near it, attempts remain → climb ONE rung, cheapest lever first (EFFORT → VERSION → TIER; never re-try the same model) (Step 3.4; binds a SELF result too — Step 2).60- **F4** — verify FAR below the bar, or out of `maxTotalAttempts` → jump straight to the top tier; at the top and still failing → hand back to the user (Step 3.4; binds a SELF result too — Step 2).61- **F5** — the failure mode is un-verifiable (crypto timing, numerical precision, security — plausible-but-wrong PASSES your check) → JUMP to the reliable tier, never climb through plausible-wrong rungs (Step 2).62- **F6** — a worker silent past `subagentTimeoutSeconds` → treat it as failed and re-route (damage-control).63- **F7** — fable consent-declined ("no") → do NOT climb into fable: STAY at the current tier (a climb case), or land on the rung below fable (a grade-5 `reasoning` start — the same `resolveWorker` fall, blocking the RESOLVED name); the cap holds even if `qualityBar` is unmet. An UNAVAILABLE fable is not a decline — it just falls (F1), no ask (Step 2).64- **F8** — ranking missing / corrupt / incomplete → rebuild on the spot per `references/lock.md`; genuinely unbuildable → routing OFF, work normally (Step 0).65- **F9** — near a session/quota limit BEFORE spawning → collapse to inline-self (the proactive budget gate; `resolveWorker` is the reactive fall once a spawn is blocked) (Step 2).66- **F10** — a delegation dies mid-run → the worker's proposal is in `proposed/`, finished subtasks in `state.json` → resume the remainder, never redo done work (damage-control).67- **F11** — you KNOW from the start the task exceeds your ceiling → escalate immediately, don't burn an attempt (Step 3.4).6869### Files — the on-disk set (a near-twin pair, declared once)7071A dot-FILE is CONFIG, a dot-DIR is STATE — the pair exists at both levels; do not read one as a typo of the other. This is the complete set a ROUTE touches. The conductor's own update-check stamp (`~/.claude/coal/coaltipple/update-check`) belongs to the SessionStart self-update channel, not to this contract.7273- **Config (read on every route):** global `~/.claude/.coaltipple.json` (unchanged) overlaid by the FIRST-FOUND project config — `<gitroot>/.claude/coal/coaltipple.json` → `.agents/coal/coaltipple.json` → `.gemini/coal/coaltipple.json` → LEGACY `<gitroot>/.claude/.coaltipple.json` — every bare "`.coaltipple.json`" in this file means this MERGED pair (merge rules in **Always**). **`<gitroot>` = the nearest ancestor holding `.git`; no `.git` anywhere above → `<gitroot>` is just the starting directory itself** (`findGitRoot`, `config-load.mjs`).74- **State:** `~/.claude/coal/coaltipple/` (global dir) holds `ranking.json` — READ at spawn-time only (a SELF route never reads it), WRITTEN only on a rare rebuild, atomically (F8) · `<gitroot>/.claude/.coaltipple/` (project dir) holds `proposed/` (the delegation sandbox workers write proposals into) + `state.json` (the subtask journal) — written only during a delegation (F10).75- **Writes driven by asks:** ASK 1's "always" persists `fableConsent: true` into the PROJECT config FILE (the resolved path per the Config bullet's read-order above — edit the key directly; `scripts/` ships only with the repo source, not the installed plugin, so `node scripts/configure.mjs --project --fableConsent true` is a convenience for a source checkout, never the only path) · ASK 3 may create/choose a memory-anchor file (the user's own file — append, never clobber, P17) and persists `contextFiles` / `memoryOffer`.76- **A SELF route writes NO CoalTipple artifact** — the edit lands in the user's files directly; `proposed/` is the DELEGATION recovery net, not self's (self recovery = your platform's normal undo / git).7778### Config — the knob set (defaults; full reference = README Configure / `config-schema.mjs`) — all 24 config keys in 23 rows (`updateMode`·`updateCheckDays` share one).7980| key | default | gates |81|---|---|---|82| `enableRouting` | true | master switch (false = routing + conductor silent) |83| `mode` | auto | allowed DIRECTIONS: `delegation` = down-only · `escalation` = up-only · `off`; P1 overrides every mode (Step 2) |84| `qualityBar` | 60 | the acceptance bar a result must clear (Steps 1 · 3) |85| `delegateMinLines` | 120 | the delegate-down floor (Step 2) |86| `maxTotalAttempts` | 2 | staircase budget before jump-to-top (F4) |87| `subagentTimeoutSeconds` | 150 | stall reap (F6) |88| `maxConcurrentSubagents` | 4 | fan-out cap (ASK 2) |89| `requireTaskContract` | true | the Step 3.1 contract |90| `qaOnMerge` | standard | Step 3.4 verify rigor (strict / standard / off) |91| `fastModeOnLatencyRequest` | true | P11 |92| `preserveVoiceForUserFacing` | true | P9 |93| `keywords` | the factory groups | grade floors + `sensitive` / `preserveVoice` flags (Step 1 lists the factory numbers) |94| `hotKeywords` | [] | legacy flat list — merges as a grade-4 sensitive group |95| `sensitivePaths` | auth · crypto · payment · billing · migration · secret · token · password · session · security | path fragments forcing grade ≥4 + sensitive; a config UNIONS with these, never replaces them |96| `excludePaths` | node_modules · .git · dist · vendor · build · .next · coverage | dirs never counted toward grading breadth |97| `disableRouting` | [] | per-domain routing OFF: `coding` / `text` / `math` / `research`, or `'all'` (Step 2) |98| `contextFiles` | [] | the memory anchor (ASK 3) |99| `memoryOffer` | auto | the anchor offer switch (ASK 3) |100| `fableConsent` | false = ask | ASK 1's standing consent (project-persisted) |101| `modelTiers` | {} | human pins over the alias floor (Step 0; P3) |102| `language` | auto | the user-facing output language (Always) |103| `updateMode` · `updateCheckDays` | ask · 14 | the conductor's self-update channel (SessionStart — not a rail of this file) |104| `gitRecoveryBoundary` | auto | git commits as an extra recovery boundary (damage-control) |105106### References — when each loads — these two are the complete reference set.107108| reference | when |109|---|---|110| `references/lock.md` | on-demand — ONLY to REBUILD a missing/corrupt/incomplete ranking (F8), or for pin/fall/safety depth; a normal route never opens it |111| `references/damage-control.md` | MANDATORY at its moments — a delegation failing mid-run (F2 detail · F6 · F10) · the memory-anchor flow (ASK 3) · a self error-report (the OFFER) |112113## Grants & denials (CLASSIFY-BLOCK — declared)114115| class | step it powers | grant | on denial |116|---|---|---|---|117| read | the merged config, the ranking, memory-anchor files, `state.json`/`proposed/` (F10 resume) | `Read` | an unreadable file is NOT the same as an absent one — never fall back to schema defaults silently: an unbuildable ranking → routing OFF (F8); an unreadable config → treat as `mode:'off'` (one of THE FRAME's five declared states, never a sixth); an unreadable journal → say the F10 resume net is not active this run, before delegating |118| write | ASK 1's `fableConsent` persist, ASK 3's memory-anchor file, the delegation journal (`proposed/`, `state.json`) | `Write`/`Edit` | report the write failed, never claim it applied — a failed "always" persist means you WILL be asked again next spawn, say why (never let a silent re-ask read as a config bug); a failed journal write means F10's resume net does not exist this run |119| spawn | delegate-DOWN / escalate-UP (Step 3) | `Agent`/`Task` (or `Bash` for a `claude -p` dispatch — its OWN grant) | **a refused GRANT is not F1's unavailable-MODEL fall** — say the delegation could not run and why, before landing on SELF; a silent SELF here is byte-identical to the routine SELF outcome and hides that routing never happened |120121A denial reaches the WORKER as a visible message and propagates NO further — not to the dispatcher, not as a catchable condition. Every row above states a branch or an explicit death; a step that dies says so in the output. Never report a denied step as done, skipped, or clean. No network row — the routing pipeline fetches nothing (the self-update check is the conductor's SessionStart channel, not this contract — Asks ledger, NOT-asks bullet). Grant denials are a SEPARATE mechanism from the F1–F11 fail-routes and P1–P17 prohibitions above (permission-level, not a routing decision) — both ledgers' counts are unchanged.122123## Step 0 — the Lock (a valid ranking gates any SPAWN)124125Routing that will SPAWN needs a tier ranking — **the ranking is READ at spawn-time (Step 3), never per prompt: a SELF route reads nothing, and an idle session pays nothing.** It is **dead simple — no introspection, no model-list enumeration, no refresh cadence:**126- **The ranking IS the alias floor + your pins.** Tiers = the Claude aliases `haiku < sonnet < opus < fable` → `low / mid / heavy / reasoning` (**fable = the top rung above opus — a real-money spawn, consent-gated before use; ASK 1, Step 2**), overlaid with your `modelTiers` pins (merged config). The platform resolves each alias to its current best model AT SPAWN-TIME, so the floor never goes stale. When a route will spawn, read the GLOBAL shared ranking `~/.claude/coal/coaltipple/ranking.json` (a cheap file read). **You do NOT enumerate the live model list.**127- **Unknown model → `heavy`, never cheap.** Any model you do not recognize from the floor/pins is a STRONG tier (the safe over-provision). Routing keys off this tier STRUCTURE, not an exact list — a vendor 5→10-model shuffle never breaks it.128- **`modelTiers` pins are the one human override** for a model released after your cutoff (you cannot see it): the user names it in the merged config and it wins front-of-tier.129- **A failed spawn FALLS** (`resolveWorker`, Step 3 · F1): availability is knowable ONLY at spawn-time, never from a catalog.130- **If a ranking genuinely cannot be built → routing OFF: just work normally** (F8). The skill never *breaks* — it routes, or routing is off.131132→ Rebuild recipe (only when ranking.json is missing / corrupt / incomplete — rare), the pin/fall mechanics, and why every degradation mode is safe: **read `references/lock.md`.** A normal route just reads the file; it never rebuilds.133134## Step 1 — Grade the task (deterministic, not self-assessment)135136Grade 1–5 by the rubric — **size + sensitive-path + keyword**, never "it feels easy" (a cheap model is overconfident):137138| Grade | Signal |139|---|---|140| 1 | read / search / summarize — **the no-signal base: no file edited, no content produced (`sizeUnits`), no keyword match** |141| 2 | **editing even ONE file floors the grade here at minimum** · ≤1 file, <500 lines, not sensitive |142| 3 | 2–3 files / 500–1000 lines |143| 4 | >3 files / ≥1000 lines / **sensitive path (auth, crypto, payments, migrations) — forces ≥4 even at 1 file** |144| 5 | concurrency / mutex / race / crypto-logic / timing-attack / proof — **forces 5 regardless of size** |145146**Every signal is a FLOOR and the grade is the HIGHEST floor fired — deterministic, no tiebreak judgment** (the rubric composes size + sensitive-path + keyword floors with max, as `grade.mjs` does): a 1-file edit (floor 2) whose prompt matches a grade-4 group grades 4 — the per-grade floor conditions are the table above, not restated here.147148**Size = content size (general-purpose):** code counts lines/files · text/translation/research/docs count words/chars (same thresholds). **Difficulty (keyword/sensitive) ALWAYS overrides size** — a tiny-but-hard task (1 line of crypto, a short legal-contract translation) = high grade.149150**Keyword groups are config-tunable (`keywords`, merged config) — honor them.** The FACTORY groups and their grade floors (the shipped values; a config overrides PER GROUP — it may add words or RAISE a built-in group's grade, never lower it below the factory floor, and never strip a built-in `sensitive`/`preserveVoice` flag): **`concurrency` 5 · `crypto` 5 (sensitive) · `security` 4 (sensitive) · `coding` 4 (sensitive — its words are the migration / schema-change / payment / billing / rate-limit class, NOT "any code": a group fires on its WORDS/intent, never on the task's general domain, so an ordinary code edit matches nothing) · `audit` 4 · `math` 5 · `knowledge` 3 · `domain` 4 (sensitive) · `creative` 2 (preserveVoice)**; legacy `hotKeywords` merges as a grade-4 sensitive group. A prompt matching a group floors the grade to the group's number and inherits its flags; an omitted group keeps the built-in. The conductor's 0-token hint reflects only the built-ins, so YOUR read of the merged config is the authority. **Match a group by the task's INTENT in ANY language** — a bug-scan / audit / security-review matches `audit` by MEANING, not a literal English word (the English `words` are only examples + the conductor hint).151152**Read WHY the grade is high — it sets the DIRECTION (the grader's `reasons` tell you which):**153- **High by DIFFICULTY** (sensitive path / hot keyword) → escalate-**UP**: you need a stronger tier for correctness.154- **High by SIZE alone** (`reasons` show only `size: N`, no sensitive/keyword — a 40-file rename, 48 CRUD handlers, bulk codegen/translation) → a high-**EFFORT** signal, NOT high-tier → **delegate-DOWN to a cheap tier + high effort.** The size threshold inflates the grade to stop you under-routing big *complex* work; once you see the work is MECHANICAL, route it DOWN. Never escalate-up / climb-the-staircase on bulk-mechanical work just because breadth bumped the grade (P10) — bulk is the EFFORT knob, difficulty is the TIER knob.155 - **EXCEPTION — a whole-repo audit / bug-scan / security-review / code-review is high-by-DIFFICULTY, not size (P10).**156157If the conductor injected a hint, respect it.158159**qualityBar (config 0–100) — the acceptable-quality bar:** grade picks the *starting* tier (Step 2); `qualityBar` is the line the result must clear, or routing climbs the ladder (Step 3). Default 60.160- **Plain meaning:** "accept work of quality ≥ qualityBar%". `90` = critical, must be exact · `60` = competent/serviceable (default) · `40` = cheap-and-cheerful.161- **0** = anything passes → stay at the starting tier, never climb. **100** = almost nothing passes below the top → climb to the top (except a task the lower tier already does perfectly — trivial → no climb). The extremes emerge from the climb mechanism; nothing is hardcoded.162163## Step 2 — Route (decide)164165**HARD GATE, checked before the grade or anything else counts — sensitive = NEVER delegate-DOWN, and this does NOT depend on your grade (the single most important safety rule):** if the task touches anything sensitive/security — crypto, timing-attack, constant-time, auth, authorization, payment, secret, token, password, session, security (the grader + conductor flag these, plus any `keywords` group you marked `sensitive`) — then **delegate-DOWN is FORBIDDEN, whatever number you graded it.** Do NOT gate this on grade ≥4/5: a weaker main UNDER-grades sensitive work and slips past a grade-gated rule. **The keyword is the gate, not your grade** (P1). Sensitivity keys on the PATH + the task's MEANING, never on the edit type — a "comments-only" / "just a typo" edit on an auth/crypto/payment path is still sensitive; DOWN is forbidden, SELF is fine. For sensitive work: escalate UP, keep it on main, or use a vetted built-in (`crypto.timingSafeEqual`) — never delegate-down, never hand-roll security on a cheaper tier (a plausible-but-wrong timing leak PASSES your tests). **"Keep it on main" presumes your OWN current tier already clears the sensitive SAFE FLOOR — `heavy`, fixed regardless of grade** (the fable bullet below: "the cap holds at its safe floor tier `heavy`, never below"). **Placing yourself on that floor is SELF-IDENTIFICATION, not the barred live-model enumeration (P5) — you already know your own name/family; match it against the SAME alias floor Step 0 defines** (`haiku < sonnet < opus < fable`). **If you genuinely cannot place yourself there, don't assume the floor clears — escalate instead**, the same fail-toward-safe posture Step 0 uses for an unrecognized WORKER model. This is **NOT the grade table's per-grade CEILING**, a different and higher number at grade 5 (`strongest`/fable; the two only coincide at grade 4, where both happen to be `heavy`). SELF is the allowed DESTINATION once you clear the FLOOR — it never requires reaching the CEILING. If your own tier sits below `heavy`, staying SELF is not on the table: escalate (spawn UP) at least to `heavy` — same as "forces escalation off the current cheap tier" below (Step 2, tier-ceiling section), the SAME rule seen from underneath, not a second rule. **Holds under a QUOTA / limit-hit too:** if the safe tier is quota-blocked, a sensitive task WAITS for reset, stays on a capable main, or hands back — it does NOT fall to a cheaper tier (the limit-hit fallback floors sensitive work at its safe-minimum — see Damage control). **A `modelTiers` pin cannot lower this floor (P3):** a KNOWN-weaker alias (haiku/sonnet) pinned into a high tier never satisfies the gate — skip it and take the next known-strong model in that tier (`resolveWorker` enforces this on the fall); only an UNKNOWN, unseeable pin (a model released after your training cutoff) is trusted there. (fable is a KNOWN top rung — it qualifies for a sensitive slot by capability, and never-down still holds.)166167**Unknown path/meaning (you genuinely can't yet tell if it's sensitive) → ASSUME sensitive until confirmed otherwise:** inspect the file's imports/callers (both directions) or ask the user; a HARD GATE with an unresolved default fails toward the safe side, never toward speed.168169**The keyword flags are ENGLISH-ONLY, so on a NON-ENGLISH task YOU are the sensitive-gate authority (P2):** a Thai/CJK/Arabic/Cyrillic prompt — OR a Latin-script one (Spanish/French/German/Portuguese/Indonesian) — meaning "constant-time compare" / "auth bypass" matches NO English literal, so no flag fires and the grade reads trivial; a grade-gated reading would let the never-down backstop vanish. Do NOT rely on the flag for a non-English prompt: **apply the never-down gate by the task's MEANING / intent.** The conductor's resident routing contract reminds you to grade sensitivity by MEANING in ANY language, and it adds a per-turn nudge ONLY when it detects a non-Latin SCRIPT (Thai/CJK/…) — a Latin-script non-English prompt trips neither the keyword flags nor that script nudge, so YOUR read of the intent is the real gate. The gate fires on MEANING; the keyword is only its English fast-path.170171**`mode` (config, default `auto`) sets the DIRECTIONS you may route — honor it (the sensitive HARD GATE overrides it):**172- `auto` (default) = route BOTH directions per grade.173- `delegation` = budget mode — delegate-DOWN allowed, escalate-UP SUPPRESSED (miss the bar → do it yourself / hand back, don't climb).174- `escalation` = quality mode — escalate-UP allowed, delegate-DOWN SUPPRESSED (never push work down to save tokens).175- `off` = routing OFF — do it yourself (the conductor also short-circuits its forcer on `mode:"off"`).176- **Override:** a SENSITIVE task obeys the hard gate under ANY mode; escalate-UP for sensitive work is always permitted even in `delegation` mode (safety beats the budget knob).177178**Per-domain `disableRouting` (config) — routing OFF for a matched domain:** if the graded task's domain (`coding` / `text` / `math` / `research`) is in `disableRouting`, OR the list includes `'all'`, routing is OFF for THIS task — do it yourself. Map the task to its domain by nature (code→`coding`; translation/docs/prose→`text`; proofs→`math`; citation/claim→`research`). (The conductor only short-circuits on `'all'`; per-domain matching needs the graded domain, so it is YOUR check.)179180**Double-hook stand-down (CoalBoard may fire the same turn — P13):** a stakes signal (security/crypto/migration/money) present, or CoalBoard already convening, means do NOT escalate independently — defer to CB: it leads, you become its tier-lever (its lenses run at the tier you'd have picked), one consent, never double-prompt. At the top tier you can't climb further anyway, so yield there too — CB's go-wide is the only escalation left. **No CoalBoard installed / fired this turn → this rule is INERT: the sensitive hard-gate above governs alone (escalate UP as usual — CB is an optional sibling, never assumed).**181182Two knobs: **TIER** (coarse, burns Opus quota) × **EFFORT** (fine, cheap, low→max). **Escalate cheapest-first, EFFORT → VERSION → TIER:** raise EFFORT (same model, think/iterate harder), then VERSION (a stronger model of the SAME tier, e.g. Opus 4.6→4.8 — cheaper + closer than a tier jump), then TIER. Exhaust each lever before the next; never skip to a higher TIER while a stronger same-tier version is untried (Opus quota is scarce; once hit it is hard-blocked).183184| Grade | Move | Tier ceiling (EFFORT is set by OUTPUT SIZE — the rubric below — never by the grade) |185|---|---|---|186| 1–2 | **delegate-down** *(only if large — see floor)* else do it yourself | low |187| 3 | do it yourself or delegate | mid — exhaust EFFORT, then VERSION, before raising tier |188| 4 | escalate-up *(if difficulty-driven)* / delegate-down *(if size-driven + mechanical; but if YOU are the floor this is SELF, no "down" exists)* | heavy · sensitive → never down |189| 5 | escalate-up / do it yourself | strongest |190191- **The tier column = the CEILING, not the START — climb the staircase to it:** escalate ONE rung at a time and verify each (Step 3); a lower rung often clears the bar (cheap tiers are more capable than they look). Reach the ceiling only when a lower rung FAILS verify; **jump straight to the top** on far-below / out-of-attempts (`maxTotalAttempts`) OR when the failure mode is subtle / un-verifiable — crypto (timing leaks), numerical precision, security — where a cheaper tier's plausible-but-wrong output would PASS your check (F5). **Rule: climb when verify is your safety net; JUMP to the reliable tier when verify can't catch the failure.** Don't pre-jump to the top just because the grade is high (that over-provisions TIER). (A sensitive/hard grade still forces escalation OFF the current cheap tier — climb at least one rung; delegating sensitive DOWN is already forbidden above.)192- **Fable costs real money — ASK 1 gates it (Step 2). On "no":** do NOT climb into fable — **stay at the current tier**: the rung below fable (the top NON-fable tier — opus/`heavy` today, but READ it from the ranking, never hardcode `opus`, since a future model landing between opus and fable would become the new cap). Declining the money CAPS the climb here even if `qualityBar` is unmet — run the worker at that tier and accept its result, don't climb further (F7). That rung is exactly where a blocked fable re-resolves (`resolveWorker`, the SAME fall an unavailable fable takes), so a grade-5 `reasoning` START with no rung-below drops there too: block the RESOLVED model (the exact name that tripped the ask — a pinned concrete Fable release id, NOT merely the literal `fable`, which would re-resolve + re-serve the pin) and keep declining while `isFableModel` stays true. Sensitive work keeps its never-down floor (the cap holds at its safe floor tier `heavy`, never below). A blocked/unavailable fable spawn falls the same way with no ask.193- **EFFORT tracks OUTPUT-SIZE/iteration, NOT difficulty — the two knobs are independent:** TIER is the correctness lever (difficulty → which model); EFFORT is the size lever (how much to produce/iterate). Reserve max for genuinely LARGE / deep work (a formal proof, a big refactor). Burning max on a tiny hard fn is the over-provisioning trap — the TIER already bought correctness; effort only sizes the work.194- **Effort rubric (by OUTPUT SIZE):** `low` = a lookup / ≤30 lines / 1-2 sentences · `medium` = a function + tests / a short proof / a paragraph · `high` = a multi-case proof / a multi-file change / a long document · `max` = a formal proof all-cases / a big refactor / an exhaustive sweep.195- **Effort is the ALWAYS-ON lever (it pays even when TIER cannot move):** at the top tier, or with only one tier available (cheaper tiers off, or a single-model user), you cannot escalate TIER but STILL optimize by scaling EFFORT per task (low for easy, high for hard). A self-executing top main IS still routing: it pins the tier and tunes effort. **"Pins the tier" on SELF means a CEILING on any worker you might spawn from here, never an instruction to self-downgrade — you are already whatever model you are and cannot change that; the pin only bounds a future spawn.**196- **Delegate-down FLOOR (most important):** spawning a worker has a high fixed overhead (~tens of thousands of tokens — measured). **Delegate down only for a task big enough that doing it yourself would cost far more than the overhead** (≥ `delegateMinLines`, default 120). Small-to-medium → **do them yourself** (offloading loses tokens). `delegateMinLines` is a CODE-line proxy — cheap-per-line work (translation, formatting, plain text) burns far fewer tokens/line, so it needs proportionally MORE than its raw line count before delegating pays. Judge real effort vs overhead, not the line count.197- **Budget gate before you fan out (the proactive limit-hit guard) · spawning is the biggest spend you control:** a worker pays off only when the task clears the floor AND there is clear BUDGET HEADROOM. Near a session/quota limit, or right after a limit-hit, the cheapest correct route is often NO worker: collapse to inline-self (F9). Above all, do NOT fan out SEVERAL workers at once for marginal-value, context-light, mechanical work (a multi-file grep/compare, a small audit, a consistency sweep) the main can do inline (P12) — N spawns × ~tens-of-thousands near a budget edge IS the limit-hit, and a worker that dies on the limit returns NOTHING (partial findings unrecoverable). `resolveWorker` is the REACTIVE fall once a spawn is blocked; THIS is the proactive gate before you spawn at all.198- **If YOU are the floor tier (cheapest — e.g. main is Haiku):** no tier exists below you, so delegate-DOWN is off the table. A big size-driven mechanical task (the one the grade table routes "delegate-down" for a higher main) has NO down-target → it collapses to SELF: do it yourself and CALL it self (a "delegate-down to myself" label is the self-contradiction that then slips the work UP to a costlier tier). Cheap/mechanical work you can do → **do it yourself.** Work beyond your competence (hard / sensitive / too-big-context) → **escalate UP** (don't keep a hard task on the floor just because "it isn't sensitive" or you hold the context). At the floor: self-for-cheap, up-for-hard.199- **Context floor (the worker starts FRESH) — distinguish two kinds of "big context":**200 - **Accumulated CONVERSATION context** (files read, decisions, the user's intent, the back-and-forth) — NON-transferable; a fresh worker can't see it. Work whose correctness depends on it → **keep on main** (compressing it into a contract is lossy + expensive). This is the real context-floor.201 - **On-disk / re-readable INPUT** (a codebase, a large document, logs) — a worker CAN read it itself from a path in the contract. A large on-disk input is NOT a reason to keep work on main — it's a reason to pick a worker with a big-enough context VARIANT (Step 0): choose TIER by difficulty, VARIANT by input size, independently. (Exception: if you ALREADY hold the input in a large-context main, keeping it there is free.)202- **ULTRACODE** (max effort + multi-agent fan-out) = the top rung, for genuinely *broad + independent* work only; workers run a cheap tier, you orchestrate (cap concurrent at `maxConcurrentSubagents`, default 4 — they share one rate limit). **Spawning more than one worker in one move is ASK 2** — state the plan (how many × tier × effort) and get a yes first. **fast-mode is not a routing choice (P11)** — attach it only on an explicit human latency request, and only if `fastModeOnLatencyRequest` (default true); never as a routing rung.203- **Voice:** when `preserveVoiceForUserFacing` (default true), never delegate the final user-facing deliverable to a cheaper model (P9) — INCLUDING a translation, summary, or research write-up (the prose IS the deliverable), even when bulky (reviewing it to protect voice + terminology costs about as much as redoing it). Bulk MECHANICAL work (renames, formatting, codegen) is not "deliverable prose" and stays delegate-able.204- **A SELF result faces the same bar (P7 binds you too), UNCONDITIONALLY — `qaOnMerge` does not gate it** (`qaOnMerge` only tunes a DELEGATED worker's merge rigor, per its own config help text; `off` skips a worker's merge-verify but never skips your own): verify your own output with the same domain-appropriate check as a worker's (Step 3.4), sized to the change — a code edit runs the build/tests, a text/comment edit re-reads the diff for completeness + correctness; eyeballing your own work is still not verifying. Below `qualityBar` → the staircase applies to YOU: raise effort, then escalate (F3). SELF work edits the user's files in place — no `.claude/.coaltipple/proposed/` sandbox (Files ledger).205206## Step 3 — If you delegate2072081. **Task contract** (required when `requireTaskContract`, default true): give the worker goal + constraints + interface + done-criteria. A thin brief makes a cheap worker build the wrong thing with confidence. Keep it compact.2092. **Spawn** at the chosen tier/effort — **lead the spawn's description/label with the chosen model + effort so the human sees what is running** (e.g. `[haiku·low] scaffold 48 handlers`, `[opus·max] formal proof`, `[sonnet·high] refactor auth`; CC renders the description as the task chip). Require a **compact diff/summary**, not whole files. **The worker MUST be BOUNDED — never spawn an open-ended worker that can loop (P6):** the done-criteria (item 1) makes it terminate (a "keep improving" brief runs forever). `subagentTimeoutSeconds` (default 150) catches a STALL (F6); the done-criteria prevents the LOOP.2103. **Spawn-fail-fall — the never-fail availability loop (drives `resolveWorker`; F1–F2):** a spawn can ERROR instantly (0-token) because the model211212…(truncated)