Files
claude/docs/superpowers/plans/2026-07-15-model-routing.md
T

43 KiB
Raw Blame History

Model Routing Implementation Plan

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: Reflection (planning, audits, loop decisions) stays on the session big model behind a blocking gate; execution (code from a closed plan, fix-bundle application) runs on sonnet-pinned subagents.

Architecture: A deterministic witness (lib/model-check.sh) + a blocking include (lib/model-gate.md) wired into 12 reflection orchestrators; frontmatter model: pins on executor agents; /feat re-architected from inline playbook to "plan inline → dispatch sonnet executor"; SDD implementation subagents and web-validate fix application routed to sonnet.

Tech Stack: bash (shellcheck-clean), Claude Code SKILL.md/agent.md markdown, agent frontmatter model: field, Makefile test loop.

Spec: docs/superpowers/specs/2026-07-15-model-routing-design.md (approved 2026-07-15).

Global Constraints

  • Work on branch feature/model-routing (already checked out). Commit per task. NEVER merge/finish — human gate.
  • NO commit attribution trailers of any kind (Co-Authored-By, Claude-Session) — user ban, guards will red.
  • make test must be green at every commit (run from repo root /home/bchanot/Documents/claude).
  • New/edited .sh files: shellcheck <file> clean and bash -n <file> clean.
  • The config-protection PreToolUse hook BLOCKS Edit/Write on lib/tests/*, hooks/*, settings.json, lib/gitflow.sh. Before EACH Edit/Write to lib/tests/* in this plan, write the one-shot bypass sentinel (consumed per use): printf 'model-routing plan: <what you are writing>' > .claude/.config-edit-ok
  • Agent frontmatter must stay yaml.safe_load-parseable (job9 gate): if a value contains : , quote it.
  • Memory registries: append-only, caveman format, English.
  • SPEC §5 (client-handover conversion) is DEFERRED to a separate plan — do NOT touch agents/client-handover-writer.md or skills/client-handover/SKILL.md in this plan.

Task 1: lib/model-check.sh witness + flip-tests

Files:

  • Create: lib/model-check.sh
  • Test: lib/tests/model-check.test.sh (guarded path — sentinel required)

Interfaces:

  • Consumes: $HOME/.claude/settings.json "model" key; env override MODEL_CHECK_SETTINGS=<path> for fixtures.

  • Produces: stdout <class>:<raw> where class ∈ big|small|unknown; exit 0=big, 2=small, 3=unknown. Task 2's lib/model-gate.md calls bash "$HOME/.claude/lib/model-check.sh" and branches on these exact codes.

  • Step 1: Write the failing test

printf 'model-routing plan: create model-check flip-tests' > .claude/.config-edit-ok

Then create lib/tests/model-check.test.sh with exactly:

#!/usr/bin/env bash
# lib/tests/model-check.test.sh — flip-tests for lib/model-check.sh (LRN-096)
set -u
S="$(cd "$(dirname "$0")/../.." && pwd)/lib/model-check.sh"
pass=0; fail=0
check() { if [ "$2" = "$3" ]; then pass=$((pass+1)); else fail=$((fail+1));
  printf 'FAIL %s: got[%s] want[%s]\n' "$1" "$2" "$3"; fi; }
T="$(mktemp -d)"; trap 'rm -rf "$T"' EXIT

fx()  { printf '{"model": "%s"}' "$1" > "$T/s.json"; }
run() { MODEL_CHECK_SETTINGS="$T/s.json" bash "$S" >"$T/out" 2>&1; echo "$?"; }

fx 'claude-fable-5[1m]';        check T1-fable-exit "$(run)" 0
check T1-fable-class "$(cut -d: -f1 <"$T/out")" big
fx 'claude-opus-4-8';           check T2-opus       "$(run)" 0
fx 'claude-sonnet-5';           check T3-sonnet     "$(run)" 2
fx 'claude-haiku-4-5-20251001'; check T4-haiku      "$(run)" 2
fx 'opusplan';                  check T5-opusplan   "$(run)" 3
fx 'gpt-9-mega';                check T6-foreign    "$(run)" 3
printf '{"no_model": true}' > "$T/s.json"; check T7-no-key    "$(run)" 3
printf '{broken'            > "$T/s.json"; check T8-malformed "$(run)" 3
check T9-missing-file "$(MODEL_CHECK_SETTINGS="$T/absent.json" bash "$S" >/dev/null 2>&1; echo $?)" 3

printf 'model-check: %d pass, %d fail\n' "$pass" "$fail"
[ "$fail" -eq 0 ]
  • Step 2: Run test to verify it fails

Run: bash lib/tests/model-check.test.sh Expected: FAIL on every check (script missing → bash exits non-zero, got[127]-style mismatches), final line model-check: 1 pass, 9 fail or similar non-zero fail count, exit 1. (T1-fable-class may pass vacuously on empty output only if cut returns empty — any red is enough: the suite CAN fail.)

  • Step 3: Write the implementation

Create lib/model-check.sh with exactly:

#!/usr/bin/env bash
# lib/model-check.sh — classify the persisted session model: big | small | unknown
#
# Witness for lib/model-gate.md (reflection requires a big model). Reads the
# "model" key of the user-scope settings (the file /model rewrites — LRN-098).
# Override the source with MODEL_CHECK_SETTINGS (tests use fixtures).
#
# stdout : <class>:<raw>   (raw = value found, empty if none)
# exit   : 0 = big (fable/opus) · 2 = small (sonnet/haiku) · 3 = unknown
set -u

SETTINGS="${MODEL_CHECK_SETTINGS:-$HOME/.claude/settings.json}"

raw=""
if [ -f "$SETTINGS" ]; then
  raw="$(python3 - "$SETTINGS" 2>/dev/null <<'PY'
import json, sys
try:
    v = json.load(open(sys.argv[1])).get("model", "")
    print(v if isinstance(v, str) else "")
except Exception:
    print("")
PY
)"
fi

norm="$(printf '%s' "$raw" | tr '[:upper:]' '[:lower:]')"
case "$norm" in
  *opusplan*)       printf 'unknown:%s\n' "$raw"; exit 3 ;; # opus-for-plan, sonnet otherwise — ambiguous
  *fable*|*opus*)   printf 'big:%s\n'     "$raw"; exit 0 ;;
  *sonnet*|*haiku*) printf 'small:%s\n'   "$raw"; exit 2 ;;
  *)                printf 'unknown:%s\n' "$raw"; exit 3 ;;
esac

(The heredoc passes the settings path as sys.argv[1] — never pipe INTO a heredoc'd interpreter, LRN-012.)

  • Step 4: Run test to verify it passes

Run: bash lib/tests/model-check.test.sh Expected: model-check: 10 pass, 0 fail, exit 0.

  • Step 5: Lint

Run: shellcheck lib/model-check.sh lib/tests/model-check.test.sh && bash -n lib/model-check.sh Expected: no output (clean), exit 0.

  • Step 6: Full suite + commit

Run: make test Expected: every suite line green, exit 0.

git add lib/model-check.sh lib/tests/model-check.test.sh
git commit -m "feat(model-routing): model-check witness (big/small/unknown) + flip-tests"

Task 2: lib/model-gate.md blocking include

Files:

  • Create: lib/model-gate.md

Interfaces:

  • Consumes: lib/model-check.sh exit codes (Task 1).

  • Produces: the include that Tasks 3 and 5 reference verbatim as `$HOME/.claude/lib/model-gate.md`.

  • Step 1: Create the include

Create lib/model-gate.md with exactly:

# Model gate — reflection requires a big model (BLOCKING)

Shared include. Runs FIRST in any orchestrator whose reflection —
brainstorming, planning, contract, audit judgment, loop decisions —
executes inline or in inherit-model subagents. Sonnet-pinned executors are
not what this gate protects; it protects the thinking around them (BDR-066).

## 1. Self-check

Your system prompt names the model powering this session. Fable or Opus →
big. Sonnet, Haiku, anything else → small.

## 2. Witness — deterministic check

    bash "$HOME/.claude/lib/model-check.sh"

Output `<class>:<raw>`; exit 0 = big, 2 = small, 3 = unknown. The witness
reads the PERSISTED model (settings.json — the file `/model` rewrites,
LRN-098). It can lag reality (session launched with `--model`, settings not
yet rewritten) — that is why the self-check exists alongside it.

## 3. Verdict

| self-check | witness | action |
|---|---|---|
| big | big (0) | proceed, SILENT — the nominal path prints nothing |
| small | any | **STOP** |
| big | small (2) | disagreement — **STOP**, surface BOTH values; the user confirms or relaunches |
| big | unknown (3) | fail-visible: print `model gate: witness unknown (<raw>) — self-check says <model>` and ask the user to confirm before continuing (BDR-025: unknown never silently passes) |

**STOP means**: print exactly

    ⛔ MODEL GATE — session on <model>. Reflection steps of this skill
    require Fable or Opus. Switch with /model, then relaunch the skill.

then end the turn. No later step runs, no agent is dispatched, nothing is
edited.
  • Step 2: Commit
git add lib/model-gate.md
git commit -m "feat(model-routing): blocking model-gate include (self-check + witness)"

Task 3: Wire the gate into the 12 reflection orchestrators

Files:

  • Modify: skills/ship-feature/SKILL.md, skills/init-project/SKILL.md, skills/onboard/SKILL.md, skills/seo/SKILL.md, skills/geo/SKILL.md, skills/web-validate/SKILL.md, skills/harden/SKILL.md, skills/audit-delta/SKILL.md, skills/tour/SKILL.md (orchestrator idiom), skills/feat/SKILL.md, skills/bugfix/SKILL.md, skills/code-clean/SKILL.md (thin-wrapper idiom)

Interfaces:

  • Consumes: lib/model-gate.md (Task 2).

  • Produces: the string lib/model-gate.md present in each of the 12 files — Task 8's census greps exactly this.

  • Step 1: Insert the orchestrator gate block (9 files)

For each of the 9 orchestrator skills, Edit with old_string = the file's unique H1 line (below), new_string = the same H1 line followed by a blank line and this exact block:

## MODEL GATE (blocking — run before any other step)

Run `$HOME/.claude/lib/model-gate.md`. Reflection here (planning, audit
judgment, loop decisions) requires Fable/Opus. Verdict `small` → STOP: the
gate prints the remedy; end the turn — no later step, no dispatch. Nominal
(big) path is silent.

H1 anchors (verbatim, one per file):

  • skills/ship-feature/SKILL.md → # ORCHESTRATOR: SHIP FEATURE

  • skills/init-project/SKILL.md → # ORCHESTRATOR: INIT PROJECT

  • skills/onboard/SKILL.md → # ORCHESTRATOR: ONBOARD

  • skills/seo/SKILL.md → # /seo — parallel SEO + GEO dispatcher

  • skills/geo/SKILL.md → # /geo — GEO (AI-search) audit + fix dispatcher

  • skills/web-validate/SKILL.md → # /web-validate — web standards audit (W3C + WCAG)

  • skills/harden/SKILL.md → # /harden — web hardening audit

  • skills/audit-delta/SKILL.md → # /audit-delta — Incremental multi-axis code audit

  • skills/tour/SKILL.md → # /tour — grouped multi-axis sweep (clean + security + reconcile + doc)

  • Step 2: Insert the thin-wrapper gate paragraph (3 files)

For skills/feat/SKILL.md, skills/bugfix/SKILL.md, skills/code-clean/SKILL.md: Edit with old_string = Load and follow strictly: and new_string =

MODEL GATE (blocking): run `$HOME/.claude/lib/model-gate.md` BEFORE loading
the agent below. Verdict `small` → STOP — print the gate's remedy, end the
turn, do not load the agent.

Load and follow strictly:

(Load and follow strictly: occurs once per file — safe anchor.)

  • Step 3: Verify the wiring by census

Run: for s in ship-feature init-project feat bugfix onboard seo geo web-validate harden audit-delta tour code-clean; do grep -L 'lib/model-gate.md' "skills/$s/SKILL.md"; done Expected: no output (grep -L lists files MISSING the pattern — empty = all wired).

Run: for s in hotfix commit-change doc status release-candidate; do grep -l 'lib/model-gate.md' "skills/$s/SKILL.md"; done Expected: no output (excluded skills stay unwired).

  • Step 4: Full suite + commit

Run: make test Expected: green, exit 0.

git add skills/ship-feature/SKILL.md skills/init-project/SKILL.md skills/onboard/SKILL.md skills/seo/SKILL.md skills/geo/SKILL.md skills/web-validate/SKILL.md skills/harden/SKILL.md skills/audit-delta/SKILL.md skills/tour/SKILL.md skills/feat/SKILL.md skills/bugfix/SKILL.md skills/code-clean/SKILL.md
git commit -m "feat(model-routing): wire blocking model gate into 12 reflection orchestrators"

Task 4: Frontmatter pins — hotfixer sonnet, analyzer un-pinned

Files:

  • Modify: agents/hotfixer.md:1-5 (frontmatter)
  • Modify: agents/analyzer.md:1-7 (frontmatter)

Interfaces:

  • Produces: model: sonnet line in hotfixer frontmatter (Task 7's applier dispatches and seo/geo L1 appliers ride on it); NO model: line in analyzer frontmatter (inherits session). Task 8's census greps both.

  • Step 1: Pin hotfixer

Edit agents/hotfixer.md, old_string:

tools: Read, Edit, Write, Bash, Grep, Glob, Agent
---

new_string:

tools: Read, Edit, Write, Bash, Grep, Glob, Agent
model: sonnet
---
  • Step 2: Un-pin analyzer

Edit agents/analyzer.md, old_string:

tools: Read, Grep, Glob, Bash
model: haiku
memory: project

new_string:

tools: Read, Grep, Glob, Bash
memory: project
  • Step 3: Verify YAML stays parseable

Run: python3 -c "import yaml,sys; [yaml.safe_load(open(f).read().split('---')[1]) for f in ['agents/hotfixer.md','agents/analyzer.md']]; print('YAML OK')" Expected: YAML OK.

  • Step 4: Full suite + commit

Run: make test Expected: green (includes the job9 review guards).

git add agents/hotfixer.md agents/analyzer.md
git commit -m "feat(model-routing): pin hotfixer sonnet (executor), un-pin analyzer (inherits session)"

Task 5: /feat re-architecture — reflection inline, execution dispatched

Files:

  • Modify (full rewrite): skills/feat/SKILL.md
  • Modify (full rewrite): agents/feater.md
  • Modify (3 surgical edits): lib/verify-secure-loop.md

Interfaces:

  • Consumes: lib/model-gate.md (Task 2), lib/verify-secure-loop.md, lib/contract-interview.md, lib/gitflow-aiguillage.md, lib/analyze-before-plan.md, lib/design-gate.md (all existing).

  • Produces: Agent(subagent_type="feater") dispatch in feat/SKILL.md; feater FEAT-EXEC REPORT grammar STATUS : DONE | NEED-DECISION | BLOCKED; model: sonnet in feater frontmatter. Task 8's census greps subagent_type="feater", verify-secure-loop.md, feater model: sonnet, feater has NO AskUserQuestion.

  • Step 1: Rewrite skills/feat/SKILL.md

Replace the ENTIRE file content with:

---
name: feat
description: |
  Small feature implementation (1-5 files). Reflection inline (scope,
  plan, contract — session model), execution dispatched to the
  sonnet-pinned feater executor. For features that don't need the full
  /ship-feature pipeline (no design brainstorm, no plugin check gate).
  Trigger: "feat", "small feature", "add this", "petite feature",
  "quick feature", "ajoute ca", "implement this small thing".
  For multi-file features needing design → use /ship-feature.
  For bug fixes → use /hotfix or /bugfix.
argument-hint: <feature description>
allowed-tools:
  - Read
  - Edit
  - Write
  - Bash
  - Grep
  - Glob
  - Agent
---

# /feat — small-feature orchestrator (reflection inline, execution dispatched)

MODEL GATE (blocking): run `$HOME/.claude/lib/model-gate.md` BEFORE any
step below. Verdict `small` → STOP — print the gate's remedy, end the
turn, dispatch nothing.

## REQUEST
$ARGUMENTS

---

## STEP 0 — SCOPE CHECK

Before starting, verify this is actually a small feature:

```bash
git status
git log --oneline -3
```

Read the relevant existing code to understand the context.

### Decision rules (apply in order — first match wins)

| Rule | Trigger | Action |
|---|---|---|
| 1 | Estimated diff < 2 files AND no logic (config value, copy fix, missing field) | DOWNGRADE → load `$HOME/.claude/agents/hotfixer.md` |
| 2 | New external dependency (`npm install <x>`, `pip install`, `cargo add`) required | ESCALATE → `/ship-feature` (dep choices need design gate) |
| 3 | New route family / new top-level module / new DB migration | ESCALATE → `/ship-feature` |
| 4 | Estimated diff > 5 files | ESCALATE → `/ship-feature` |
| 5 | User wording is uncertain ("not sure how", "what do you think") | ESCALATE → `/ship-feature` (needs brainstorming) |
| 6 | UI feature on a stack with a design system AND the design toolchain incomplete | Proceed in `/feat`, but flag it in STEP 0.5 design gate |
| 7 | Otherwise | PROCEED in `/feat` |

### Worked examples

- "Add `/health` endpoint returning `{status:"ok",version}`" → 1-2 files, no new dep, route added to existing router → **PROCEED**.
- "Add a dark-mode toggle bound to `prefers-color-scheme`" → 2-3 files, design system exists → **PROCEED** (design gate triggers in STEP 0.5).
- "Add OAuth login (Google + GitHub providers)" → new deps, new routes, secrets handling → **ESCALATE** to `/ship-feature`.
- "Show a 'New' badge on items created this week" → 1-2 files, pure UI predicate → **PROCEED**.
- "Fix copy: 'Sign In' → 'Sign in'" in 1 file → **DOWNGRADE** to `/hotfix`.

Print a one-line scope confirmation (use the rule that fired):
```
FEAT: <feature name> — rule <N>, ~<N> files, <brief approach>
```

## STEP 0.5 — DESIGN GATE

Follow `$HOME/.claude/lib/design-gate.md`:
- Scan $ARGUMENTS and target files for design/UI/style signals.
- If signals found → run `design-tool-gate.sh`; if it reports INCOMPLETE,
  tell the user to run `/profile design` before proceeding.
- If no signals → skip (zero overhead).

## STEP 0.6 — MEMORY READ-BEFORE (decisions-first)

Run the scan per `$HOME/.claude/lib/analyze-before-plan.md`, decisions-weighted: a BDR may
already constrain or forbid the approach; an LRN may name a gotcha to apply. Emit RELATED
MEMORY; feed STEP 1 PLAN. Inline consumption — reader = planner, no injection.
`.claude/memory/` absent → guarded no-op (zero overhead on a memory-less repo).

## STEP 0.7 — CONTRACT

Run `$HOME/.claude/lib/contract-interview.md` (main loop — you are it). It
captures the request verbatim, asks 0-3 questions PROPORTIONAL to ambiguity
(a complete request → zero questions, silent), derives testable acceptance
criteria + file scope, and writes the contract to
`.claude/tasks/contracts/<date>-<slug>-<HHMM>.md`. Keep the path — the
executor reads it first and GATE 1 (STEP 4) hands it to a fresh verifier.

## STEP 1 — PLAN (dispatch-ready)

The executor follows this plan to the letter and CANNOT ask questions —
close every decision here:

1. Files to create or modify (with line references).
2. Approach in 2-5 bullets — name every choice (naming, data shape, API
   surface); an open choice left here comes back as a NEED-DECISION
   round-trip.
3. Edge cases to handle.
4. Tests to add/update (exact files).
5. Disposition (from STEP 0.6): name each in-force BDR/LRN this plan honors
   (`honors BDR-xxx by …`), or state `no in-force decision constrains this feature`.
   A plan with neither = read-then-ignore; the disposition must surface as a trace.

Print the plan as a compact checklist:
```
PLAN:
  [ ] <file> — <what to do>
  [ ] <file> — <what to do>
  [ ] <test file> — <test to add>
```

If the approach is ambiguous: ask the user ONE focused question BEFORE
dispatching — never after (the executor cannot relay questions).

## STEP 2 — BRANCH

**Gitflow aiguillage (before dispatch):** follow `$HOME/.claude/lib/gitflow-aiguillage.md`
— your type = `feature`. On `main`/`develop` it branches first; on a working
branch it's a no-op (commit in place). Never `finish`.

## STEP 3 — DISPATCH EXECUTOR

Dispatch the executor — sonnet by frontmatter pin, do not override:

```
Agent(subagent_type="feater")
prompt: "CONTRACT: <path from STEP 0.7>
PLAN: <the STEP 1 checklist + approach bullets + edge cases, verbatim>
BRANCH: <current branch — verify with git branch --show-current, never switch>
Implement the plan to the letter. Tests alongside code. No commit, no
branch ops, no new dependencies, no files outside the contract FILE SCOPE.
Finish with the FEAT-EXEC REPORT."
```

Parse the `FEAT-EXEC REPORT`:
- `STATUS : DONE` → STEP 4.
- `STATUS : NEED-DECISION` → make the decision HERE (that is reflection),
  append it to the plan, re-dispatch a FRESH feater with plan + decision.
  Max 2 decision round-trips → escalate to the user.
- `STATUS : BLOCKED` → surface the blocker to the user, stop.

## STEP 4 — VERIFY + SECURE (fresh gates, bounded loops)

Run the two fresh gates per `$HOME/.claude/lib/verify-secure-loop.md` with
`CONTRACT` = the STEP 0.7 path, `DIFF` = the working-tree diff the executor
produced, `TEST` = the suite named in its report:

- GATE 1 — a FRESH verifier judges the diff against the contract (blind).
  CONFORME on the first pass → straight to GATE 2, no loop. ECARTS → the
  "dev" of the loop is the dispatched executor: re-dispatch a FRESH feater
  with the CONTRACT path + the exact gap lines, nothing else. Max 3 →
  escalate.
- GATE 2 — a FRESH security-auditor (`MODE: gate`) scans the diff. PASS →
  STEP 5. BLOCK → re-dispatch a FRESH feater with the BLOCKING list + the
  CONTRACT path; re-verify the request THEN re-scan, max 3 → escalate.

Loop decisions stay HERE, in the main loop (LRN-083). Nominal (clear
request, conform first pass, clean diff) = one executor + one verifier +
one security dispatch.

## STEP 5 — COMMIT

Commit using conventional format:
```
feat(<scope>): <what was added>

<brief description of the feature>
```

If the feature touched multiple concerns (e.g., feature + config +
test), consider splitting into 2-3 atomic commits — load
`$HOME/.claude/agents/commit-changer.md` and follow its grouping logic.

Print summary:
```
FEAT COMPLETE
FEATURE  : <name>
FILE(S)  : <created/modified files>
TEST(S)  : <added tests>
VERIFIED : <what was checked>
```

## STEP 6 — DOC SYNC (automatic)

Load `$HOME/.claude/agents/doc-syncer.md`.
Execute in automatic mode:
`auto-mode scope: <list of files modified during this session>`

**Then commit the docs** — follow `$HOME/.claude/lib/doc-commit.md`: it surgically commits
ONLY the files doc-syncer patched (its `PATCHED_FILES` output), never `git add -A`, never
`.claude/`/`CLAUDE.md` (rc 4 = a loud BDR-022 anomaly, not a silent skip), and no-ops when
nothing was patched — the common case for a trivial change. No FINISH in an inline flow, so
it just commits the docs on the current branch (no ordering concern).

## STEP 7 — CAPITALIZE (memory registries)

A small feature may or may not involve a design choice. Scan the work for:

- **Non-trivial design choice** (even small: a library pick, a naming convention, a data-model tradeoff) → propose `BDR-XXX` in `.claude/memory/decisions.md` with alternatives considered.
- **Reusable pattern or gotcha encountered** → propose `LRN-XXX` in `.claude/memory/learnings.md`.

Present the candidates grouped:
```
CAPITALIZE — proposé
  [decisions.md]   BDR-XXX — <titre> (optionnel)
  [learnings.md]   LRN-XXX — <pattern> (optionnel)
Valider ? (all / <IDs> / edit / skip)
```

Always append a 1-line entry to today's heading in `.claude/memory/journal.md`.

**Language rule**: written entries are ALWAYS in English (see CLAUDE.md "Memory registries" § Language). The interactive gate may mirror the user's language; the appended entries must not.

If no substantive capture candidate → skip with `CAPITALIZE: nothing to log`.

**Then commit the memory** — follow `$HOME/.claude/lib/capitalize-commit.md`: it
surgically commits what capitalize just wrote (`.claude/memory` + `.claude/tasks`
only, never `git add -A`) as one `chore(memory)` commit, reports the memory-commit
hash, and no-ops if nothing was written.

---

## RULES
- Max 5 files. If more needed → `/ship-feature`.
- Reflection (scope, plan, contract, loop decisions) NEVER leaves this main
  loop; execution NEVER stays in it — the executor is the sonnet-pinned
  feater subagent (BDR-066).
- The executor is dispatched FRESH on every round-trip — feedback travels
  as contract path + named gaps/decisions, never as transcript.
- Design gate only (not full plugin check). See STEP 0.5.
- No brainstorm/design phase (if needed → `/ship-feature`).
- Keep scope tight. If scope creep happens mid-work, stop
  and suggest splitting into `/feat` + follow-up task.
- Follow existing code patterns. Don't introduce new patterns
  for a small feature.

(Note: this rewrite REPLACES the Task 3 thin-wrapper gate paragraph for feat — the gate line is now native under the H1. The census greps lib/model-gate.md, satisfied either way.)

  • Step 2: Rewrite agents/feater.md

Replace the ENTIRE file content with:

---
name: feater
description: Small-feature EXECUTOR — dispatched by /feat with a closed plan + contract. Implements to the letter, tests, reports. No planning, no questions, no commit.
tools: Read, Edit, Write, Bash, Grep, Glob
model: sonnet
---

# FEATER — plan executor

You receive a CLOSED plan from the /feat orchestrator. Your job is faithful
execution, not design. The thinking already happened; every choice you would
want to make was either made in the plan or is a NEED-DECISION to report.

## INPUT (in the dispatch prompt)

- `CONTRACT`: path to the contract file — read it FIRST; its acceptance
  criteria + FILE SCOPE bound everything you do.
- `PLAN`: files + approach + edge cases + tests.
- `BRANCH`: verify with `git branch --show-current`; mismatch → STATUS
  BLOCKED — never create or switch branches.
- `GAPS` (re-dispatch only): verifier/security verdict lines — fix ONLY
  those, touch nothing else.

## EXECUTION RULES

- Follow the plan to the letter. A plan hole or an open choice (naming,
  data shape, API surface, dependency) → STOP, report `NEED-DECISION` with
  the precise question. Never improvise a design decision.
- Stay inside the contract FILE SCOPE. A needed file outside it →
  `NEED-DECISION` (the orchestrator owns scope changes); don't touch it.
- Write tests alongside the code, as the plan names them. Run the relevant
  suite incrementally; run it fully before reporting.
- Follow existing code patterns and CLAUDE.md limits (function size,
  params, no global state). Match comment density and naming.
- FORBIDDEN: `git commit`, branch ops, push, merge, new dependencies,
  editing `.claude/**` or memory registries, user questions (you cannot
  ask — report instead), attribution trailers of any kind.

## OUTPUT — end with exactly this report (your final message)

```
FEAT-EXEC REPORT
STATUS   : DONE | NEED-DECISION | BLOCKED
FILES    : <created/modified paths>
TESTS    : <added/updated + final suite run result, verbatim line>
NOTES    : <DONE: deviations (must be none) | NEED-DECISION: the exact
           question + the options you see | BLOCKED: the blocker verbatim>
```
  • Step 3: Update lib/verify-secure-loop.md (3 surgical edits)

Edit 1 — header, old_string:

finished diff into a verified, security-cleared change through two fresh
gates and bounded loops. The dev stays inline (LRN-083: subagents =
execution + report; loop decisions live here, in the main loop).

new_string:

finished diff into a verified, security-cleared change through two fresh
gates and bounded loops. Loop decisions live here, in the main loop
(LRN-083: subagents = execution + report). The dev step is either inline
(bugfix) or a dispatched sonnet executor (/feat's feater): "hand the dev"
below means fix inline, or re-dispatch a FRESH executor with exactly those
inputs.

Edit 2 — GATE 1 ECARTS bullet, old_string:

  lines (NOT-MET / out-of-scope), nothing else. Dev fixes inline, then
  re-dispatch a FRESH verifier.

new_string:

  lines (NOT-MET / out-of-scope), nothing else. Inline dev fixes in place;
  a dispatched dev is re-dispatched FRESH with those inputs only. Then
  re-dispatch a FRESH verifier.

Edit 3 — GATE 2 BLOCK bullet, old_string:

- `BLOCK(n)` → hand the dev the `BLOCKING` list + the CONTRACT path. Dev
  fixes inline. Then **re-verify the REQUEST first** (GATE 1, fresh

new_string:

- `BLOCK(n)` → hand the dev the `BLOCKING` list + the CONTRACT path (inline
  fix, or FRESH executor re-dispatch). Then **re-verify the REQUEST first** (GATE 1, fresh
  • Step 4: Structural verification

Run: grep -c 'subagent_type="feater"' skills/feat/SKILL.md; grep -c 'verify-secure-loop.md' skills/feat/SKILL.md; grep -c 'model: sonnet' agents/feater.md; grep -c 'tools: Read, Edit, Write, Bash, Grep, Glob$' agents/feater.md; grep -c 'AskUserQuestion' agents/feater.md; true Expected: 1 / 1 (or more) / 1 / 1 (exact tools line — no Agent tool) / 0 (no AskUserQuestion anywhere).

Run: python3 -c "import yaml; yaml.safe_load(open('agents/feater.md').read().split('---')[1]); print('YAML OK')" Expected: YAML OK.

  • Step 5: Full suite + commit

Run: make test Expected: green.

git add skills/feat/SKILL.md agents/feater.md lib/verify-secure-loop.md
git commit -m "feat(model-routing): /feat re-architecture — reflection inline, feater = sonnet executor (partial supersede BDR-050)"

Task 6: Pin SDD implementation subagents to sonnet (ship-feature, init-project)

Files:

  • Modify: skills/ship-feature/SKILL.md:144-148
  • Modify: skills/init-project/SKILL.md:166-170

Interfaces:

  • Produces: the literal model: "sonnet" in both files — Task 8's census greps it.

  • Step 1: ship-feature STEP 4

Edit skills/ship-feature/SKILL.md, old_string:

`finishing-a-development-branch` step — this orchestrator owns integration via
`gitflow finish` (STEP 9). When SDD's flow reaches "Use
finishing-a-development-branch", stop and return.

new_string:

`finishing-a-development-branch` step — this orchestrator owns integration via
`gitflow finish` (STEP 9). When SDD's flow reaches "Use
finishing-a-development-branch", stop and return.

**Model routing (BDR-066):** every subagent dispatched under SDD — per-task
implementers AND its reviewers — MUST carry `model: "sonnet"` in the Agent
call. The plan is closed; execution and plan-conformity review are sonnet
work. Reflection (task decomposition, review verdict arbitration) stays in
this loop.
  • Step 2: init-project STEP 8

Edit skills/init-project/SKILL.md, old_string:

`finishing-a-development-branch` step — this orchestrator owns integration via
`gitflow finish` (STEP 11). When SDD's flow reaches "Use
finishing-a-development-branch", stop and return.

new_string:

`finishing-a-development-branch` step — this orchestrator owns integration via
`gitflow finish` (STEP 11). When SDD's flow reaches "Use
finishing-a-development-branch", stop and return.

**Model routing (BDR-066):** every subagent dispatched under SDD — per-task
implementers AND its reviewers — MUST carry `model: "sonnet"` in the Agent
call. The plan is closed; execution and plan-conformity review are sonnet
work. Reflection (task decomposition, review verdict arbitration) stays in
this loop.
  • Step 3: Verify + commit

Run: grep -c 'model: "sonnet"' skills/ship-feature/SKILL.md skills/init-project/SKILL.md Expected: 1 for each file.

Run: make test — Expected: green.

git add skills/ship-feature/SKILL.md skills/init-project/SKILL.md
git commit -m "feat(model-routing): SDD implementation + review subagents dispatched model sonnet"

Task 7: web-validate fixes via hotfixer L1 applier

Files:

  • Modify: skills/web-validate/SKILL.md:274-277 (STEP 3, options A and B)

Interfaces:

  • Consumes: hotfixer sonnet pin (Task 4); mirrors the geo L1 applier idiom (skills/geo/SKILL.md:72-77).

  • Produces: subagent_type="hotfixer" in web-validate — Task 8's census greps it.

  • Step 1: Replace inline-Edit application with L1 dispatch

Edit skills/web-validate/SKILL.md, old_string:

4. On `A` : apply each bundle via `Edit` (targeted `old_string` /
   `new_string`). Never use `Write` on shared templates (risk of
   overwriting /seo or /geo content — meta tags, JSON-LD).
5. On `B` : for each diff, show and ask yes/no/skip.

new_string:

4. On `A` : dispatch each file-group's applier at L1 (execution = sonnet;
   this loop only orchestrates), serially — one applier at a time, appliers
   share files:

   ```
   Agent(subagent_type="hotfixer")
   prompt: "<paste the file-group's bundle items: file, issue, current,
     expected fix>.
     Context: web-validate fix bundle, user-approved scope — no
     confirmation needed. Apply via targeted Edit (old_string/new_string);
     NEVER Write whole files (shared templates carry /seo and /geo
     content — meta tags, JSON-LD). Do NOT commit — apply and self-verify
     only."
   ```

5. On `B` : for each diff, show and ask yes/no/skip; apply approved diffs
   as in `A` (hotfixer dispatch).
  • Step 2: Verify + commit

Run: grep -c 'subagent_type="hotfixer"' skills/web-validate/SKILL.md Expected: 1.

Run: make test — Expected: green.

git add skills/web-validate/SKILL.md
git commit -m "feat(model-routing): web-validate fix bundle applied via hotfixer at L1 (BDR-061 alignment)"

Task 8: Census guard lib/tests/model-routing.test.sh + flip-test

Files:

  • Test: lib/tests/model-routing.test.sh (guarded path — sentinel required)

Interfaces:

  • Consumes: every string produced by Tasks 3-7 (see greps below). Auto-discovered by the Makefile test glob lib/tests/*.test.sh — no runner edit needed.

  • Step 1: Write the census test

printf 'model-routing plan: add census guard test' > .claude/.config-edit-ok

Then create lib/tests/model-routing.test.sh with exactly:

#!/usr/bin/env bash
# lib/tests/model-routing.test.sh — census: gate wiring + pins + executor shape (BDR-066)
set -u
R="$(cd "$(dirname "$0")/../.." && pwd)"
pass=0; fail=0
ok() { pass=$((pass+1)); }
ko() { fail=$((fail+1)); printf 'FAIL %s\n' "$1"; }
has()   { if grep -qF "$2" "$R/$1"; then ok; else ko "$1 missing: $2"; fi; }
lacks() { if grep -qF "$2" "$R/$1"; then ko "$1 must NOT contain: $2"; else ok; fi; }
fm_lacks() { if awk 'NR<=10' "$R/$1" | grep -qF "$2"; then ko "$1 frontmatter must NOT contain: $2"; else ok; fi; }

# 1) gate wired in the 12 reflection orchestrators
for s in ship-feature init-project feat bugfix onboard seo geo web-validate harden audit-delta tour code-clean; do
  has "skills/$s/SKILL.md" 'lib/model-gate.md'
done
# 2) gate NOT wired in the excluded skills (encodes the spec exclusion list)
for s in hotfix commit-change doc status release-candidate; do
  lacks "skills/$s/SKILL.md" 'lib/model-gate.md'
done
# 3) executor + gate pins
has "agents/feater.md"          'model: sonnet'
has "agents/hotfixer.md"        'model: sonnet'
has "agents/verifier.md"        'model: sonnet'
has "agents/security-auditor.md" 'model: sonnet'
fm_lacks "agents/analyzer.md"   'model:'
# 4) /feat executor shape
has "skills/feat/SKILL.md" 'subagent_type="feater"'
has "skills/feat/SKILL.md" 'verify-secure-loop.md'
lacks "agents/feater.md" 'AskUserQuestion'
# 5) SDD execution pinned
has "skills/ship-feature/SKILL.md" 'model: "sonnet"'
has "skills/init-project/SKILL.md" 'model: "sonnet"'
# 6) web-validate applies via L1 applier
has "skills/web-validate/SKILL.md" 'subagent_type="hotfixer"'

printf 'model-routing census: %d pass, %d fail\n' "$pass" "$fail"
[ "$fail" -eq 0 ]
  • Step 2: Run — expect green (everything already wired by Tasks 3-7)

Run: bash lib/tests/model-routing.test.sh Expected: model-routing census: 28 pass, 0 fail, exit 0. (Count: 12 wired + 5 excluded + 5 pins + 3 feat-shape + 2 SDD + 1 web-validate.)

  • Step 3: Flip-test the guard (LRN-096 — prove it CAN fail)
sed -i 's|lib/model-gate.md|lib/model-gate-REMOVED.md|' skills/tour/SKILL.md
bash lib/tests/model-routing.test.sh; echo "exit=$?"
git checkout -- skills/tour/SKILL.md
bash lib/tests/model-routing.test.sh; echo "exit=$?"

Expected: first run prints FAIL skills/tour/SKILL.md missing: lib/model-gate.md and exit=1; second run prints 28 pass, 0 fail and exit=0.

  • Step 4: Lint + full suite + commit

Run: shellcheck lib/tests/model-routing.test.sh && make test Expected: clean + green.

git add lib/tests/model-routing.test.sh
git commit -m "test(model-routing): census guard — gate wiring, pins, executor shape (flip-tested)"

Task 9: README + CHANGELOG

Files:

  • Modify: README.md (agent/model documentation)

  • Modify: CHANGELOG.md (Unreleased section)

  • Step 1: Locate the README insertion point

Run: grep -niE 'agents?/|sonnet|haiku|model' README.md | head -20

If README has a table listing agents (a row per agent), refresh/add its model info from the table below. If not, insert a new subsection ### Agent model routing (BDR-066) immediately after the section that documents agents/ (fallback: before the "Skills" section), with exactly:

### Agent model routing (BDR-066)

Reflection (brainstorm, plan, contract, audit judgment, loop decisions) runs
INLINE on the session model — assumed Fable/Opus, enforced by a blocking
gate (`lib/model-gate.md` + `lib/model-check.sh`) at the entry of the 12
reflection orchestrators. Execution runs on pinned subagents:

| Agent | Model | Tier |
|---|---|---|
| feater, hotfixer | sonnet (pinned) | executors — code from a closed plan, fix-bundle appliers |
| verifier, security-auditor | sonnet (pinned) | fresh gates (≤3×/loop) |
| doc-syncer, onboarder, scaffolder, refactorer, interviewer, plugin-advisor | sonnet (pinned) | workers |
| status-reporter | haiku (pinned) | mechanical collector |
| client-handover-writer | opus (pinned, currently inert — inline-loaded; sonnet conversion planned) | deliverable writer |
| analyzer, seo-analyzer, geo-analyzer, validator-analyzer, code-cleaner, bugfixer, commit-changer | inherit session (Fable/Opus) | reflection / audit / inline playbooks |
  • Step 2: CHANGELOG

Run: grep -n 'Unreleased' CHANGELOG.md

Under the ## [Unreleased] heading (create ### Added / ### Changed subsections if absent), add:

### Added
- Model routing (BDR-066): blocking model gate (`lib/model-gate.md` +
  `lib/model-check.sh`, flip-tested) wired into 12 reflection orchestrators;
  census guard `lib/tests/model-routing.test.sh`.
- `/feat` re-architected: reflection inline (scope/plan/contract), execution
  dispatched to the sonnet-pinned `feater` executor; verify+secure loop
  decided in the main loop with fresh executor re-dispatches.

### Changed
- `hotfixer` pinned `model: sonnet` (seo/geo/web-validate L1 applier);
  `analyzer` haiku pin removed (inherits the session model).
- ship-feature / init-project: SDD implementation + review subagents
  dispatched with `model: "sonnet"`.
- web-validate `--fix`: bundle applied via `hotfixer` at L1 instead of
  inline Edit (BDR-061 alignment).
  • Step 3: Commit

Run: make test — Expected: green.

git add README.md CHANGELOG.md
git commit -m "docs(model-routing): README agent-model table + CHANGELOG entry"

Task 10: Capitalize memory + TODO follow-up

Files:

  • Modify: .claude/memory/decisions.md (append BDR-066 + Index row)

  • Modify: .claude/memory/journal.md (1 line under a ## 2026-07-15 heading)

  • Modify: .claude/tasks/TODO.md (chantier section + plan-2 backlog)

  • Step 1: Append BDR-066 to .claude/memory/decisions.md

Add to the Index table (after the BDR-065 row):

| BDR-066 | 2026-07-15 | Model routing: reflection inline (session big model) + sonnet-pinned executors + blocking gate | accepted |

Append at end of file:

## BDR-066 — Model routing: reflection inline (session big model), executors pinned sonnet, blocking gate

- **Date**: 2026-07-15
- **Status**: accepted (partial supersede of BDR-050: /feat dev no longer inline; bugfix/hotfix dev-inline CONSERVED)
- **Decision**: reflection (brainstorm, plan, contract, audit judgment, loop decisions) runs on session model (Fable; Opus fallback) — inline or inherit subagents, never pinned down. Execution (code from closed plan, fix-bundle application) runs sonnet-pinned subagents: feater + hotfixer pinned sonnet; SDD implementation+review subagents dispatched `model: "sonnet"` (ship-feature/init-project); web-validate fixes via hotfixer L1 (was inline Edit). analyzer haiku pin REMOVED (digest feeds plan = reflection tier). verifier + security-auditor STAY sonnet (job9 confirmed — procedural gates, ≤3×/loop). Blocking gate `lib/model-gate.md` (self-check + witness `lib/model-check.sh`) wired in 12 reflection orchestrators; small → STOP, unknown → fail-visible; census guard `lib/tests/model-routing.test.sh` flip-tested.
- **Why**: big-model quota burned on mechanical execution (Fable exhausted mid-job8); plan closed at dispatch → executor needs obedience not judgment; fresh sonnet gates catch executor drift.
- **Alternatives rejected**: opus pins on audit agents (session-independent) — rejected: session assumed big + blocking gate as backstop, one tier fewer; advisory gate — rejected by user, blocking; split bugfix/hotfix too — rejected: bugfix investigation interleaved w/ fix, hotfix gain marginal vs dispatch overhead.
- **Caveats**: client-handover-writer conversion (inline-load → sonnet dispatch, 11 human-gate sites to relocate) DEFERRED to own plan — its opus pin stays inert meanwhile; feater cannot ask → NEED-DECISION report = escalation valve, plan must close decisions; witness reads settings.json — lags `--model`-launched sessions (self-check compensates).
- **Reference**: spec `docs/superpowers/specs/2026-07-15-model-routing-design.md` + plan `docs/superpowers/plans/2026-07-15-model-routing.md` (transient, BDR-065 lifecycle), branch `feature/model-routing`.
  • Step 2: Journal line

Append under a ## 2026-07-15 heading (create it if absent) in .claude/memory/journal.md:

- model routing shipped on feature/model-routing: BDR-066 (reflection inline big / executors sonnet / blocking gate), /feat re-arch, census guard. client-handover conversion deferred to plan 2.
  • Step 3: TODO follow-up entry

Add at the top of .claude/tasks/TODO.md (above the 2026-07-08 section):

## 2026-07-15 — model routing (feature/model-routing)
Spec + plan in docs/superpowers/ (transient, BDR-065). BDR-066. Branch
unmerged — human gate.
- [x] gate lib/model-check.sh + lib/model-gate.md (flip-tested) wired ×12
- [x] pins: hotfixer/feater sonnet, analyzer un-pinned; SDD model:"sonnet";
      web-validate → hotfixer L1; census guard model-routing.test.sh
- [x] /feat re-arch: reflection inline → feater sonnet executor (partial
      supersede BDR-050)
- [ ] DOGFOOD (manual, next sessions): /feat live run — plan closes
      decisions, dispatch carries sonnet, verify loop in main loop; gate
      STOP on a sonnet session (LRN-079 class, not automatable here)
- [ ] PLAN 2 — client-handover conversion (spec §5): inline-load → sonnet
      dispatch, relocate 11 human-gate sites to dispatcher (inventory in
      plan-1 session), or lighter variant: dispatch only the redaction
      phase. Decide shape at plan time.
  • Step 4: Commit memory scoped
git add .claude/memory/decisions.md .claude/memory/journal.md .claude/tasks/TODO.md
git commit -m "chore(memory): BDR-066 model routing + journal + TODO follow-ups"
  • Step 5: Final gate

Run: make test Expected: green, exit 0. Then report the full commit list (git log --oneline develop..HEAD) for the human merge gate. Do NOT run gitflow finish.