--- key: process.per-agent-model-routing title: 2026-07-25 — Name the model tier for every dispatched agent; a PreToolUse gate makes the silent default visible (#583) status: active since: '2026-07-25' supersedes: none superseded-by: none rule: State the model tier (and effort, where the client exposes it) in the dispatch itself for every delegated agent — bounded recon → cheapest fast tier at `low`; mechanical slice against a documented contract → mid tier; judgment-heavy work → orchestrator tier; independent review → a different model family than the implementer. signals: 'subagent model routing · `model` omitted · silent tier inheritance · orchestrator tier for a mechanical slice · capability routing · prose rule vs HARD CONSTRAINT · dispatch-time checkpoint · paths: `.claude/hooks/pretooluse-agent-model.sh`, `docs/handoffs/chicorytv-issue-queue.md` · issues: #583, #436, #440' mechanics: '`Agent` tool `model` parameter; PreToolUse hook on the existing `Agent|Task` matcher in `.claude/settings.json`.' --- On 2026-07-25 a session dispatched two implementers (#436, #440) with `model` omitted on both calls; both silently inherited the Opus orchestrator tier. #440 was a mechanical SPA slice against an already-shipped backend contract — a plausible mid-tier candidate. The interesting part is *why*, because it wasn't forgetfulness. **Every rule in `HARD CONSTRAINTS` was followed in that same session** — worktree off `origin/main`, one committing agent per worktree, parallelize on disjoint slices, local gate before push. Routing was the one instruction living only in a prose paragraph, and it was the one that got defaulted. Treat that as the general lesson: in a kickoff doc that is pasted into every session, **the bulleted imperative list is what actually functions as the checklist**, and prose above it is read as background. A rule you want followed belongs in the list, keyed, or it is advisory in practice. Three aggravating factors, all worth checking when writing any future rule here: - **Scope gap.** The low-cost-routing paragraph is written entirely about queue selection and recon ("bounded searches, inventories, log triage, report drafting"). It never named *implementers*, and gave no default for the bounded-but-not-trivial case — so the largest-cost dispatch fell in a gap. - **The wrong default is the silent one.** Omitting `model` produces no artifact. Nothing in the session report revealed the tier; the operator had to ask. Contrast the BOM trap (`process.bom-format-detection-recipe`), where a hook fires because a memory describing the trap demonstrably failed to prevent it twice in one day. - **Distance from the decision point.** The rule sits ~line 84 of the kickoff; dispatch happens after orientation, claiming, the bundle scan and doc reading. Hence the two-part fix: a keyed HARD CONSTRAINT that requires the tier to be **stated out loud in the dispatch** (a self-correcting mechanism — it turns an invisible omission into visible output), plus `.claude/hooks/pretooluse-agent-model.sh`, which `ask`s whenever an agent is dispatched with no explicit `model`. It exempts only `fork`, whose `model` override the tool ignores by design, so a prompt there could not be acted on. It is `ask`, never `deny`: routing is a judgment call with no derivable right answer, unlike the H6/H10 merge gate (`release.merge-consent-autogrant`), which derives a verifiable state and can therefore grant or refuse outright. **The first cut was narrower, and review killed it** — worth recording, because the reasoning was seductive. It fired only when the prompt *text* matched implementer signals (`git commit`, `worktree`, `fixes #`), on the theory that a gate firing on every fan-out trains one-shot dismissal. Independent review confirmed the heuristic both over- and under-fired: a read-only recon brief merely *mentioning* "worktree" nagged, while "author the change and open a PR", "land this on the branch" and "make the changes and commit them" all passed silently — it missed precisely the case it existed to catch. **Prompt prose is not a reliable signal for authority**, and a gate with an unreliable catch rate is worse than none, because it gets trusted. Two further reasons the broad form is right, both of which the narrow version had backwards: - **It now matches the rule it enforces.** The HARD CONSTRAINT says "every dispatched agent"; a hook gating only implementer-looking dispatches contradicted its own rule. - **Routing matters MOST for the cheap cases.** The old exemption list justified itself as "read-only, so routing barely matters" — but bounded recon is exactly what should be explicitly routed *down* to a fast tier. The premise was also false: `Explore`, `Plan` and `claude-code-guide` all carry Bash, so none of them provably "cannot commit". The noise objection is answered by the escape hatch rather than by scoping: **naming a tier costs one parameter and the hook never fires again**. The prompt is self-eliminating for anyone following the rule, which is the habit being built.