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. I...
Scanned 9/2/2026
Install to Claude Code
npx -y skills add TheColliery/CoalTipple --skill coaltipple --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Coaltipple?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/thecolliery-coaltipple)More formats (shields.io, HTML) on the badges page.
---
name: coaltipple
description: >-
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.
---
# 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 a pinned concrete id like `claude-fable-5`) 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 `claude-fable-5`, 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 `.proposed/` sandbox (Files ledger).
## Step 3 — If you delegate
1. **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.
2. **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.
3. **Spawn-fail-fall — the never-fail availability loop (drives `resolveWorker`; F1–F2):** a spawn can ERROR instantly (0-token) because the model is **unavailable / disabled / out of quota / gone** ("X is currently unavailable" — proven live with `fable`). Availability is knowable ONLY here, at spawn-time. On error: **(a)** add that model to a `blocked` set; **(b)** call `resolveWorker(ranking, desiredTier, {blocked, floorTier})` (classify.mjs — walks `desiredTier`→`floorTier`, skipping `blocked`); **(c)** spawn the returned worker; **(d)** repeat (a)–(c) on each fresh unavailable error; **(e)** if `resolveWorker` returns `null` (everything blocked to the floor), STOP — for a SENSITIVE task the `floorTier` is its safe-minimum, so `null` means hand back / wait for reset and **NEVER breach never-down** by falling cheaper (F2). **Guarantee: routing reaches a working model OR hands back cleanly — never stuck on an unavailable model, never falls sensitive work below its floor.** (This is the DOWN-for-availability move; the quality climb below is the UP-for-quality move — opposite directions, don't conflate.) Mechanics + the limit-hit/quota fallback detail: **`references/damage-control.md`.**
4. **Verify against qualityBar:** rigor follows `qaOnMerge` (strict / standard / off; default standard) — `off` skips merge-verify (trust mode), `strict` = exhaustive. Output may "look right but be wrong" — **never trust it blindly, never let the worker grade itself (P7).** Measure against external criteria = the contract done-criteria + a **domain-appropriate objective check** (code: build/test/lint/type-check · text/translation: every segment present + consistent + format preserved · research: every claim has a real, authoritative source, verified with the web/doc tools *you have yourself* · math: check the steps / substitute back · **a comment-only edit INSIDE a code file is BOTH: re-read the diff for correctness/completeness AND confirm the file still parses/lints**). **RUN the check — actually execute the tests/build/parse; do NOT eyeball the code. A cheaper main especially defaults to eyeballing: it catches obvious misses but mis-judges subtle bugs. Eyeballing is not verifying.** Clears the bar → done.
- **Below the bar → climb the staircase (F3):** near the bar + attempts remain → **escalate one rung** (the next lever in EFFORT/VERSION/TIER order — a stronger same-tier VERSION first, then the next TIER; don't retry the same model, don't skip a tier while a stronger same-tier version is untried). **Far below (the next rung won't clear it), or out of `maxTotalAttempts` (default 2) → jump straight to the top tier (F4)** (don't die by a thousand cuts). At the top and still failing → hand back to the user.
- **competence-introspection:** if you *know* from the start the task exceeds your ceiling → escalate immediately, don't waste an attempt (F11).
5. **Never let a cheap main QA an expert's work (P8)** — if you escalated *up*, let the strong worker own both the hard task and the integration.
## Memory anchor · Damage control · Self error-report
These run only on a specific, infrequent path — keep the rules out of the per-route hot path:
- **Memory anchor** (a fresh worker gets project context beyond the bare contract): when you are about to delegate **context-dependent** work, find the anchor — `contextFiles` if set, else the platform's auto-loaded `CLAUDE.md`/`AGENTS.md`. If none exists AND `memoryOffer` is `auto` AND you have not offered this session (ASK 3) → read **`references/damage-control.md` (Memory anchor)** for the lazy-offer + write-policy (append, never clobber — P17). Changed anytime via `/coaltipple memory`.
- **Damage control** (bound the blast radius of a mid-run death — a silent stall, a limit-hit/unavailable fallback, the `.proposed/` sandbox + `state.json` journal, worktree-isolation, the never-delegate/never-retry-a-side-effect rule P15): the load-bearing safety invariant stays in the gates above (sensitive never falls below its floor on a limit-hit — HARD GATE + Step 3.3). The mechanics fire only when a delegation is actually failing (F6 · F10) → **`references/damage-control.md`.**
- **Self error-report** (CoalTipple misbehaves — a contradictory instruction, a loop/dead-end, a clearly-wrong route, the Lock refusing a valid ranking, a config key off-spec): STOP, summarize what went wrong, OFFER to file at `github.com/TheColliery/CoalTipple/issues` (never auto-submit — P16). Detail: **`references/damage-control.md`.**
## Always
Honor the MERGED config (every value is tunable): global `~/.claude/.coaltipple.json` (your defaults) 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`; per-key override; a project file is OPTIONAL — absent = global + schema defaults) — **EXCEPT `mode`/`updateMode`/`fableConsent`, a cloned project file may only QUIETEN these, never escalate:** `mode`/`updateMode` can't climb past your GLOBAL choice, or past the schema default (`auto`/`ask`) when you set no global at all — a cloned repo cannot silently turn ON auto-routing or auto-updates. `fableConsent` is narrower: a bare project `true` with NO matching global setting is your own "always-this-project" consent (`configure.mjs --project --fableConsent true`) — honor it; only an EXPLICIT global `false` blocks a project `true` (never let a project override a global you deliberately set to false). **Consent-gate token spend per the Asks ledger** — never spawn fable unconsented (ASK 1), never fan out several workers silently (ASK 2). **Respond in the user's language** (auto-detect, or the `language` config): translate the prose, but **never translate technical terms** — commands, paths, identifiers, tier/model names, severity labels, config keys stay verbatim. `/coaltipple off` turns routing off; `/coaltipple stats` shows approximate savings (an estimate — there is no cost API); `/coaltipple memory [on|off|set <file>]` sets up or disables the memory anchor; `/coaltipple update` checks for a newer version.
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!