diff --git a/docs/superpowers/specs/2026-07-15-model-routing-design.md b/docs/superpowers/specs/2026-07-15-model-routing-design.md new file mode 100644 index 0000000..e1b1419 --- /dev/null +++ b/docs/superpowers/specs/2026-07-15-model-routing-design.md @@ -0,0 +1,143 @@ +# Model routing — reflection inline (big model) / execution pinned (Sonnet) — design + +**Date**: 2026-07-15 · **Status**: approved (user, 2026-07-15) · **Branch**: `feature/model-routing` +**Lifecycle**: transient planning artifact (BDR-065) — committed during the run, deleted post-merge. + +## Principle + +The session model is assumed to be a big reasoning model (Fable 5, or Opus when +Fable is unavailable). Everything that **thinks** — brainstorming, planning, +technical decisions, audits, loop decisions — runs INLINE in the main +conversation, or in subagents that inherit the session model. Everything that +**executes** a ready-made plan — writing code, applying fix bundles, commits, +deliverable rendering — runs on Sonnet-pinned subagents. A blocking gate +enforces the "session = big model" assumption at the entry of every reflection +orchestrator. + +User verdicts baked in (2026-07-14/15): +- Scope = hybrid: ship-feature/init-project execution → sonnet; `/feat` + re-architected (plan inline → dispatch executor); bugfix/hotfix stay fully + inline (BDR-050 conserved for them). +- Gate = BLOCKING, not advisory. +- Audit agents inherit the session model (no opus pin); the gate extends to + audit orchestrators. +- verifier + security-auditor KEEP `model: sonnet` (job9 decision confirmed). +- client-handover-writer → sonnet (requires converting its inline-load to a + true dispatch; human gates relocate to the main loop). + +## 1. Blocking model gate + +New `lib/model-check.sh`: resolves the current session model from +`settings.json` (physical path resolution — LRN-023 class), normalizes +(`claude-fable-5[1m]` → fable, `claude-opus-*` → opus, sonnet, haiku), prints +`big|small|unknown`. Exit 0 = big, 2 = small, 3 = unknown. + +New `lib/model-gate.md` snippet (same include pattern as `lib/design-gate.md`): +run the check; `small` → STOP the skill: "session model is — reflection +requires Fable/Opus. Switch with /model, then relaunch." `unknown` → +fail-visible: show the raw value, ask the user to confirm or abort (BDR-025 +doctrine — unknown never silently passes). + +Wired as a STEP 0 line in the reflection orchestrators: +`ship-feature, init-project, feat, bugfix, onboard, seo, geo, web-validate, +harden, audit-delta, tour, code-clean`. +NOT wired in: `hotfix` (trivial by definition), `commit-change`, `doc`, +`status`, `release-candidate`. + +Caveats to prove at implementation time: +- `/model` mid-session rewrites settings.json (LRN-098 observed it once — + re-prove with a live flip-test before trusting the source). +- The helper itself must be flip-tested (LRN-096: an unproven guard is a + vacuous guard). + +## 2. Frontmatter pins (`agents/*.md`) + +| Agent | Before | After | Rationale | +|---|---|---|---| +| feater | (inherit) | **sonnet** | executor as subagent: seo/geo L1 applier + new /feat dispatch | +| hotfixer | (inherit) | **sonnet** | L1 applier (seo/geo/web-validate); /hotfix inline unaffected (pin inert on inline load) | +| client-handover-writer | opus | **sonnet** | deliverable executor; pin becomes EFFECTIVE only with §5 dispatch conversion (today's opus pin is inert — the agent is inline-loaded) | +| analyzer | haiku | **(none — inherit)** | analysis feeds the plan = reflection; runs big via the session model | +| verifier | sonnet | keep | F1 confirmed (job9) | +| security-auditor | sonnet | keep | F1 confirmed (job9) | +| seo-analyzer, geo-analyzer, validator-analyzer | (inherit) | keep (inherit) | audit = reflection = session model; covered by the gate | +| code-cleaner | (inherit) | keep (inherit) | audit phase = reflection; fixes hand off to refactorer (sonnet) via CODE-CLEAN-SCOPE.md (job9 H1) | +| doc-syncer, onboarder, scaffolder, refactorer, interviewer, plugin-advisor | sonnet | keep | workers/executors | +| status-reporter | haiku | keep | mechanical collector | +| bugfixer, commit-changer | (inherit) | keep | inline-only playbooks — a pin would be inert | + +## 3. `/feat` re-architecture (partial supersede of BDR-050 — feat only) + +`skills/feat/SKILL.md` absorbs the reflection: analyze-before-plan, design +gate, MINI-PLAN, contract (`lib/contract-interview.md`) — all inline. Then +dispatches `Agent(subagent_type="feater")` (sonnet via pin) with: the +contract, the plan, the branch name, repo conventions. + +`agents/feater.md` is rewritten as a pure executor: implement the plan to the +letter, run project checks, commit (no attribution trailers), return a +structured summary. No user interaction inside feater (subagents cannot ask) — +every decision must be closed pre-dispatch. + +The verify-secure loop moves out of feater.md into the /feat main loop +(LRN-083 invariant: loop decisions live in the main loop): fresh verifier → +ECARTS → re-dispatch feater with the verdict deltas, bounded 3×; then the +security gate. Escalation paths unchanged. + +## 4. SDD execution pinned (ship-feature STEP 4, init-project STEP 8) + +One instruction line in each SKILL.md: every implementation subagent +dispatched under `superpowers:subagent-driven-development` MUST carry +`model: "sonnet"` in the Agent call. No fork of the superpowers skill — the +main loop emits the Agent calls and controls the params. + +## 5. client-handover conversion (inline-load → true dispatch) + +`skills/client-handover/SKILL.md`: collect params inline (URL, logo, options), +then `Agent(subagent_type="client-handover-writer")` — the sonnet pin becomes +effective. Human gates (per-axis threshold escalation, overrides) RELOCATE to +the main loop: the writer returns a structured `GATE NEEDED` status instead of +asking; the dispatcher asks the user and re-dispatches (or continues via +SendMessage) with the decision. `AskUserQuestion` is removed from the writer's +tools. + +OPEN VERIFY POINT: the writer's own nested dispatches (seo/harden re-runs as +general-purpose subagents) — verify at implementation what nested children +inherit (session model vs parent model). If they inherit the sonnet parent, +the re-run audits violate the principle → force the model explicitly in those +nested dispatches or lift them to the main loop. + +## 6. web-validate fixes → L1 applier + +STEP 3 stops applying fixes via inline Edit; dispatches `hotfixer` (sonnet) +with the fix bundle — same pattern as seo/geo (BDR-061 alignment). + +## 7. Memory / doc / tests + +- New BDR: model-routing principle (reflection inline big / executors sonnet / + blocking gate); partial supersede of BDR-050 (feat only); records F1 + (verifier/security stay sonnet) and the analyzer haiku→inherit change. +- README: agent-model table refresh. CHANGELOG Unreleased entry. +- Tests: flip-tests for `model-check.sh` (fable[1m] / opus / sonnet / garbage + fixtures); gate STOP proven on a small-model fixture (LRN-096); /feat smoke + on a throwaway repo (LRN-079): plan inline → dispatch carries sonnet → + verify loop decided in main loop; grep census: no executor dispatch without + an effective pin. + +## Out of scope / accepted deviations + +- `/doc` and `/commit-change` stay inline on the session model (judgment and + execution interleaved; converting them buys little). Revisit under quota + pressure. +- bugfix/hotfix fully inline (BDR-050 conserved). +- No per-agent "fable-else-opus" fallback exists in the harness — the session + model IS the fallback mechanism; the gate is its backstop. + +## Risks + +- Model strings in settings.json may change shape with CC updates → + model-check must return `unknown` (fail-visible), never guess. +- feater as a subagent loses main-conversation context → the plan becomes the + contract; weak plans cost verify-loop iterations. Mitigation: + contract-interview stays mandatory in /feat. +- Nested model inheritance under client-handover-writer unknown → §5 verify + point.