| name | architect |
| description | Have Fable 5 do the expensive meta-work once — deep plan a complex task (codebase review, greenfield project, large migration) AND build the execution kit (task briefs, model-pinned subagents, skills, verification loops) so Opus/Sonnet can execute at near-Fable quality. Use when the user says "plan this with Fable", "architect this", "review this codebase", or escalates a complex task. |
Fable-as-architect
The goal: Fable 5 runs once, at the start; its judgment persists as scaffolding. The output is not just a plan — it is an execution kit a lesser model can run at near-Fable quality, because every decision that needed Fable-level judgment is already made and written down.
Step 0 — Get onto Fable 5
Check the current session model. Two entry modes:
- Dispatch mode (session is on Opus/Sonnet — the normal daily-driver case): spawn the architecture work as a subagent via the Agent tool with
model: fable. Give it this entire skill's Steps 1–3 as instructions plus a complete brief of the task and all relevant conversation context (the subagent sees none of it otherwise). It writes the kit files directly; you relay its summary. The main session never leaves the daily driver.
- Native mode (user already ran
/model fable, or wants to steer interactively): do Steps 1–3 in this session, then remind them to switch back (/model opus) once the kit is built.
If the task is ambiguous, ask the scoping questions BEFORE burning Fable time — Fable works best with the full spec up front in one shot.
Step 1 — The plan
Write .claude/kits/<slug>/PLAN.md in the target project (<slug> = short kebab-case task name):
- Goal and what "done" looks like (checkable, not vibes)
- Constraints and out-of-scope (explicitly fence what executors must NOT do)
- Architecture & key decisions, each with rationale — the "why" is what lets a cheaper model make consistent micro-decisions later
- Risks and their tripwires — what an executor should watch for and what to do when hit
- Autonomy posture (optional) — a single
autonomy: advisory or autonomy: auto line anywhere in PLAN.md (absent = advisory). Execute reads it as the kit's dial for live re-routing and auto-escalation; the user can override it per run at invocation. An optional PLAN.md line, not a task field — the task-field contract is unchanged.
- Budget dial (optional) — a single
budget: max-dispatches=N max-escalations=N max-consults=N line anywhere in PLAN.md (any subset of the three keys; absent = unbounded, today's behavior). Execute's own loop and all three headless drivers (bin/claude_execute.py, bin/copilot_execute.py, bin/codex_execute.py) honor it: on reaching a declared cap they stop cleanly, leave the remaining tasks' status untouched, and append ONE outcome: ... result=budget-stop line (run= included) instead of dispatching — never folded into a fluent summary. budget-stop is a fifth, no-verdict result= value (bin/routing_scorecard.py's RESULTS); it is excluded from first-try/escalation-rate signals because no dispatch happened, and — being no verdict at all — it never supersedes one: a budget-stop line for a task id that already carries a real result is declined by the drivers and, if one reaches the ledger anyway, ignored by the scorecard in favor of the recorded verdict. Like autonomy:, this is a PLAN.md line family, never a task field — the task-field contract is unchanged.
- Roles roster (optional) — a single
roles: <token> <token> ... line anywhere in PLAN.md, naming the optional pipeline roles this kit adds beyond the standing trio (implementer / verifier / reviewer). Tokens come from exactly seven: scout, test-author, second-verifier, red-team, security-auditor, docs-editor, synthesizer; anything else is out of grammar. Absent = the trio, today's behavior — every kit written before this dial existed stays valid, and nothing here changes the default or auto-declares a role. Execute reads the line once at setup, dispatches each declared role at its own hook point, and records every declared-role dispatch as an agent: line in the kit's NOTES.md so the roster can be measured afterwards. Recommended comparison ladder (so rosters are comparable across kits, not a constraint — a kit may declare any subset): R3 = the trio, no roles: line; R5 = R3 + test-author + red-team; R7 = R5 + scout + second-verifier; R10 = R7 + security-auditor + docs-editor + synthesizer. Consult the evidence before you declare: python3 bin/routing_scorecard.py --roles renders the cross-kit per-role value table — dispatches, findings, confirmed, marginal catches (confirmed findings that no earlier layer of the pipeline raised on that task or phase), precision, marginal rate over the dispatches where marginal was actually adjudicated, and dollars only where transcripts priced them — with "insufficient sample" below the evidence floor instead of a number it cannot support. Read it as evidence, not as a roster-setter: declare a role in order to TEST it, and carry it into the next kit only when its measured marginal value earns its measured cost. A role with no recorded dispatches is absent from that table, not zero, and roles whose value is indirect (scout, docs-editor, synthesizer) produce no adjudicable findings by design — judge those qualitatively against their dispatch cost, never by an empty findings column. Like autonomy: and budget:, this is a PLAN.md line family, never a task field — the task-field contract is unchanged.
Before dispatching wide exploratory reads over an unfamiliar target repo, check whether it already carries a graphify-out/graph.json (or offer the user /polytropos:graphify to build one — an optional, external, availability-gated tool, not assumed present). When a graph exists, python3 bin/graph_brief.py brief --graph <path> plus a handful of targeted graphify explain <symbol> calls can ground the plan at near-zero context cost: a repo-shape card and precise symbol lookups instead of a wide read sweep. But the graph's limits bind — dynamic loaders (importlib, plugin registries) are invisible to its AST extraction, and the absence of an edge is never evidence of the absence of a dependency — so treat it as a first map that narrows where to look next, never as the sole evidence backing a contract or dependency claim in PLAN.md.
Step 2 — The execution kit
This is the part that makes lesser models perform above their weight. Produce:
TASKS.md (same kit directory)
Ordered task list. Every task must be executable by a model with zero access to this conversation:
id, title, status (pending/in-progress/done/blocked), model (sonnet default; opus for the genuinely hard ones; haiku for trivial mechanical ones)
- Self-contained brief: files involved, relevant conventions, interfaces/contracts pinned down exactly, known gotchas, and the why behind the approach
- Acceptance criteria: concrete and checkable
- Verify command: a shell command (tests, build, lint, a curl) that proves the task done
- Tasks are grouped under
## Phase N — <name> headings; the execute loop dispatches the reviewer agent at each phase end.
- Each task marks ordering explicitly:
depends: <ids> or independent: yes — execute parallelizes only tasks marked independent.
- Flag warm-cluster candidates as free text in the TASKS.md dispatch preamble (e.g. "T2 → T3 → T4 are strictly serial (same file)"): serial
depends: chains that share a primary file and carry the same model pin. Execute may then serve the whole cluster with one continued (warm) implementer instead of N cold spawns; tasks marked independent: still fan out fresh. This is a hint, not a new task field — the task-field contract is unchanged.
- The task's
model field is authoritative at dispatch time: execute passes it as the Agent tool's model parameter, which overrides the implementer agent's frontmatter default. When a kit runs with the autonomy dial on auto, execute may layer a logged, upgrade-only runtime override on top at dispatch (one tier step, never to frontier) — the field itself is never rewritten and stays the dispatch default.
- Consult the routing history when choosing the initial
model pins: run python3 bin/routing_scorecard.py --history — the cross-kit per-tier track record aggregated from every prior kit's outcome ledger and re-route events (first-try rate, escalation rate, upgrade frequency, plus dollars where kits recorded a session: line). It is EVIDENCE, not an auto-pin-setter: the architect weighs it and decides — a tier that keeps needing upgrades on similar work argues for pinning one tier up front; a tier passing cleanly argues the cheap pin is safe. Two further sections appear once the history holds the evidence for them. Escalation lineage groups each escalation under the parent task that spawned it, so a cheap pin that keeps spawning rescues reads as a chain rather than as scattered independent passes. counts failures by class ( / / ) under the IMPLEMENTER's dispatch tier — the tier the failing work actually RAN on, which is the task's raw pin only when nothing shifted it. A outcome attributes to the tier of the model it ran on; an attributes to the reconstructed dispatch tier (the cheap tier that needed rescuing), never to the tier that fixed it; and a child/consult outcome carrying contributes nothing, because its parent's own line already carries the failure. Read it as "this tier failed on this class", not "this pin was written in TASKS.md" — where an escalation or a re-route moved the model, those two differ, and that is exactly the case this section exists to reason about. Every in-grammar counts, adjudicated or not ( is the role ledger's separate vocabulary and does not gate this table). Read the class before the tier: and failures argue for upgrading the implementer pin, but a tier failing mostly on means work was called done that wasn't — that argues for a stronger VERIFIER pin on the next kit, not a stronger implementer. Upgrading the implementer there buys nothing and costs a tier. The card also carries a section: verifier and reviewer precision (findings raised vs findings the orchestrator adjudicated as real), escalation results, and the architect — the lines execute records when a task brief's defect was confirmed against repo reality mid-run. Read the recurring defect kinds (, , and the like) as evidence about YOUR OWN briefs — they are the architect's failure modes, not the executor's — and write the next kit against them: prefer content assertions over anchors that go stale, counts over line numbers, verify commands that can actually fail, and helpers that some task in the kit actually creates.
Pin down contracts and interfaces precisely; leave implementation judgment open where any competent model would do fine. Over-prescription wastes the kit; under-specification wastes the executor.
Project subagents (.claude/agents/ in the target project)
Only create what the kit needs and doesn't already exist. Typical trio, each with project-specific conventions baked into its prompt:
<slug>-implementer.md — model: sonnet — executes one task brief exactly; stops and reports rather than improvising when the brief is wrong
<slug>-verifier.md — model: haiku (or sonnet if judgment needed), tools: Bash, Read, Grep, Glob — fresh-context adversarial check of acceptance criteria; never trusts the implementer's claims, reruns the verify command itself
<slug>-reviewer.md — model: opus, tools: Bash, Read, Grep, Glob — reviews completed phases against PLAN.md for drift
Pin the read-only roles' tools: frontmatter — mandatory for every kit you generate. Verifier and reviewer get tools: Bash, Read, Grep, Glob (read/search plus Bash, no Write, no Edit); the implementer's tools stay unpinned, because writing is its job. Scope structurally rather than by instruction: a verifier with no editor cannot be talked into fixing the defect while it is in there. But write the honest limit into the agent file too, instead of trusting the pin — Bash alone can delete or rewrite any tracked file, and in this repo a verifier holding exactly this pin destroyed an authored docs section during mutation testing, never restored it, and reported its own damage as the implementer's defect. Removing Write/Edit removes the casual path, not the capability. So pair the pin with the practice that actually closes the gap, stated in both read-only agents' prompts: prefer non-mutating checks; when a check genuinely needs mutation, copy the target to a temp directory and mutate the copy, never a tracked file in place; if the tree is touched anyway, restore it byte-for-byte before reporting and say so; and close with git status --porcelain, reporting any unexpected change as the agent's own defect, never the implementer's.
When PLAN.md declares a roles: line, instantiate each declared role from its template. This skill ships one generic template per optional role at skills/architect/references/roles/<role>.md (resolve via ${CLAUDE_PLUGIN_ROOT}, falling back to "relative to this SKILL.md") — each a complete agent file with frontmatter, the role's mission, its hook point in the pipeline, and its recording contract. Copy the template to .claude/agents/<slug>-<role>.md in the target project and replace every <slug> placeholder with the kit slug. Keep the template's model: default and its tools posture as written — the read-only roles carry the same tools: pin and damage-restore practice as the verifier and reviewer above; the write-capable roles (test-author, docs-editor, synthesizer) carry a scoped write mission instead of a pin — unless this kit has a specific reason to differ, in which case state the reason in PLAN.md. Do not widen a template's mission to cover another role's ground: the boundaries are the design (red-team attacks beyond the acceptance criteria rather than re-running the verifier's checks; security-auditor is fences, leaks, and injection surface only, not the reviewer's drift review; second-verifier must carry a stated different lens from the verifier's). And as with the trio, never reuse the name of an agent listed in an aesop manifest's primitives.agents.
Harness guardrails
Write the kit's fences to .claude/kits/<slug>/GUARDRAILS.md — task-scoped conventions, forbidden shortcuts, "always run X before claiming done". Execute reads the file at setup, so they load only when this kit runs and never tax other sessions. Always create this file, one per kit, no exceptions: if the kit genuinely needs no fences beyond PLAN.md's out-of-scope section, write that sentence and point at PLAN.md rather than omitting the file — an absent file is indistinguishable from a forgotten one, and a layout check that requires it fails the whole suite (hence every task's verify command), not just the kit that skipped it. Give it real substance, not a stub. Add to the target project's global CLAUDE.md only an invariant that is genuinely permanent and project-wide — true for every future session, not just this kit — and sparingly: that file is loaded everywhere, forever. Prefer judgement over rules: state the principle and name the signal to read (e.g. "match the surrounding file's error-handling style") instead of an absolute — EXCEPT rules protecting real money, live CLIs, or user data, which stay absolute and explicit. If a procedure is complex enough, make it a project skill (.claude/skills/<name>/SKILL.md) instead. These are the Fable-judgment rails the executors run on.
Aesop-managed target? If the target project has an aesop.yaml at its root, or an <!-- aesop:begin fence in its CLAUDE.md/AGENTS.md, those files are compiled output — hand-edits get flagged as drift by aesop sync and overwritten by aesop compile. Put the guardrails in aesop.yaml under primitives.instructions.blocks (scope: project) instead, then run aesop compile and confirm aesop sync reports no drift. Kit directories (.claude/kits/…) and kit-prefixed agent files are safe to write directly — aesop tracks only files it emits — but never reuse the name of an agent listed in the manifest's primitives.agents.
Step 3 — Handoff
End with exactly what happens next, e.g.:
Kit ready at .claude/kits/<slug>/. Switch back to your daily driver (/model opus) and run /polytropos:execute <slug>. Blocked tasks escalate back to Fable one at a time — you won't pay Fable prices for execution.
The kit's model-pinned agents mean the model mix is enforced automatically during execution — nobody has to remember to downgrade.