---
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.
