26. Adopt native model:/effort: agent frontmatter, retire the band resolver¶
Date: 2026-07-22
Status¶
Accepted
Supercedes 4. Pre-dispatch model tier resolution enforced by a PreToolUse hook
Supercedes 8. Use effort bands instead of model names in agent frontmatter
Supercedes 21. Claude-only model routing — no multimodal or cross-provider tier
Supercedes 23. Calibrate agent effort bands from the #1184 eval baseline
Supercedes 24. Keep the single model-routing map keyed by canonical model IDs
Supercedes 25. Allow versioned (dated-snapshot) model IDs in the routing map
Context¶
ADR 0004 built a PreToolUse hook to
resolve a tier alias to a concrete model before every Agent dispatch, because
at the time (2026-06-01) it was unverified whether the harness itself did
anything useful with model: beyond a bare alias lookup, and two real
populations (restricted corporate proxies, Anthropic snapshot deprecation)
needed that resolved reliably. ADR 0008
renamed the frontmatter field from a vendor-named tier (haiku|sonnet|opus) to
a vendor-neutral effort: low|medium|high band, mapped to a model by the same
hook via knowledge/model-routing.json or a per-environment
.claude/model-ladder.json. ADR 0021
scoped that routing to Claude-only. ADR 0023
validated several agents' bands against eval fixtures and downgraded eight of
them. ADR 0024
and ADR 0025 settled the ID
shape the routing map's values should take. Six accepted ADRs, a PreToolUse
hook and its resolver library, a calibration subsystem
(scripts/agent_calibrate.py, knowledge/calibration-floors.json, /agent-eval
--calibrate), ~15 test files, and two dedicated docs
(docs/model-routing.md, docs/band-calibration.md) accumulated around this
one problem: the harness might not resolve model:/reasoning-effort
correctly on its own, so the plugin resolved it first.
ADR 0008's own amendment already recorded a live data point cutting against that premise: the shipped hook layer did not load at all on Claude Code until v10.12.1 (#1178), so every agent ran on the session model regardless of its declared band for an unknown prior period — and nothing in production broke in a way that surfaced the gap. The hook's enforcement guarantee was inert, and the system still functioned.
Anthropic's own documentation (verified 2026-07-22, the source captured
verbatim in plugins/marketplace-dev/knowledge/agent-contract.json) confirms
Claude Code subagents natively support a model: field — an alias
(sonnet|opus|haiku|fable), a full model ID, or inherit — and an effort:
field (low|medium|high|xhigh|max), both resolved by the harness itself
before dispatch. This is exactly the mechanism ADR 0004 built a hook to
provide, now confirmed to already exist natively, with a wider effort range
(five levels, not three) and no dependency on a shipped routing map, ladder
file, or calibration subsystem to keep working. The custom machinery is not
wrong, it is redundant: the harness now does natively what the plugin spent
six ADRs building by hand.
A repo sweep found 70+ files referencing the system being retired: the six
ADRs above, ~15 test files, 2 dedicated docs, the hook and its lib, 51 agent
files across plugins/dev-team/ and plugins/security-assessment/, and the
marketplace-dev:agent-create/plugin-audit tooling that scaffolds and
validates the old scheme today.
Decision¶
Adopt the native model:/effort: frontmatter contract on every agent, and
retire the band resolver, ladder, and calibration infrastructure it
replaces. Concretely, sequenced across sub-issues of epic #1284:
- Contract validator (#1285, shipped). A schema-driven validator
(
plugins/marketplace-dev/scripts/validate_agent_contract.py, wrapped atscripts/validate_agent_contract.pyfor this repo's own self-audit) checks any agent's frontmatter againstagent-contract.json— required fields, name pattern, enum membership formodel/effort/permissionMode/memory/isolation/background/color, unknown-key warnings, and a plugin-agent warning thathooks/mcpServers/permissionModeare inert for plugin-supplied agents. - This ADR (#1286). Records the decision and supersedes ADRs 0004, 0008, 0021, 0023, 0024, and 0025.
- Mechanical migration (#1287). Every agent in
plugins/dev-team/agents/andplugins/security-assessment/agents/(51 files) is rewritten fromeffort: <band>tomodel: <alias>+effort: high. Themodel:alias is derived once, mechanically, from each agent's current band through today'sknowledge/model-routing.json(low→haiku,medium→sonnet,high→opus) — preserving which model each agent already dispatches to.effort:is set uniformly tohighfor every agent, not band-preserving: the old three-way band was doing double duty as both "which model" and "how much reasoning," and now that the native fields separate those concerns cleanly,highis the safe default until a follow-up calibration pass evaluates the native effort dial on its own terms (see Consequences). - Retire the infrastructure (#1288). Delete
hooks/agent_model_resolve.py,hooks/lib/model_resolve.py, theirsettings.json/hooks.jsonregistration,knowledge/model-routing.json,knowledge/calibration-floors.json,skills/model-routing-check/,scripts/agent_calibrate.py,scripts/check_calibration_floors_sync.py,scripts/check_model_staleness.py,scripts/recalibrate_*.py, the--calibratemode's supporting scripts, and the ~15 associated test files. Updateplugins/dev-team/CLAUDE.md's Model Routing section,docs/model-routing.md,docs/model-routing-overrides.md,docs/band-calibration.md, and the band references inknowledge/skills-registry.md,knowledge/agent-registry.md,agents/orchestrator.md, andagents/session-analysis.md. - Marketplace tooling (#1289).
marketplace-dev:agent-createandplugin-auditare updated to scaffold and validatemodel:+effort: highinstead of the old band scheme, and gain optional--memory,--isolation,--color,--max-turns,--background, and askills:preload list as creation-time inputs — matching the full native contract, not just the two fields this migration touches. - Wire the validator in, drop the calibration mode (#1290).
dev-team:agent-auditinvokes the contract validator from #1285 as part of its structural-compliance checks.dev-team:agent-evaldrops its--calibratemode (flag, docs, dispatch logic) — the calibration subsystem it drove no longer exists after #1288.
Consequences¶
Easier:
- Zero plugin-owned code stands between an agent's frontmatter and the model/effort the harness actually runs it with. There is no PreToolUse hook to keep loading correctly (the exact failure mode ADR 0008's amendment already hit once, silently, for an unknown period), no routing map or ladder file to keep in sync with Anthropic's snapshot lineup, and no calibration subsystem to maintain.
- The native
effort:field has five levels (low|medium|high|xhigh|max) instead of the retired scheme's three, and needs no shipped default map to function —inherit/session-effort is the harness's own fallback. - ~70 files' worth of hand-built machinery (hook, lib, ladder convention, calibration scripts, ~15 tests, 2 docs, 6 ADRs) collapses to one validator script and one contract file.
Harder / risks:
- The #1184/#1211/#1023 calibration evidence does not carry forward as an
effort assignment. Those baselines validated the retired band against
eval fixtures; they said nothing about the native
effort:dial, which didn't exist as an independent, harness-resolved field until now. Setting every agent toeffort: highuniformly is a deliberate reset, not a regression of that evidence — but it does mean agents previously downgraded to a cheap model (e.g.a11y-reviewon Haiku, per ADR 0023) now run that cheap model ateffort: high, a combination never calibrated. Re-running calibration against the nativemodel:/effort:combination is out of scope for this epic and tracked as follow-up work, not a gap this ADR closes. - Breaking change for any downstream agent author who copied the retired
scheme. Anyone who forked an agent with
effort: medium(the retired vocabulary) must migrate tomodel:/effort: high(the native one). The marketplace tooling update (#1289) ensures new agents are scaffolded correctly going forward; existing forks outside this repo are the fork owner's responsibility. agent-contract.json's enum values are a point-in-time capture of Anthropic's docs (retrieved 2026-07-22), not a live contract. If the harness adds or renamesmodel/effortvalues,agent-contract.jsonand the validator built on it need a manual refresh — the same staleness risk the retiredknowledge/model-routing.jsonhad, just with a much smaller surface (one JSON file describing an external contract, not a routing table this repo computed).- Sequencing risk across five sub-issues landing separately. #1288
depends on #1287 landing first (agents must already declare
model:before the resolver that readseffort: <band>is deleted), and #1290 depends on both #1285 and #1288. A partial landing (e.g. #1287 merged, #1288 not yet) leaves the retired hook resolving frontmatter that no longer uses its expected vocabulary — mitigated by shipping each sub-issue as its own reviewed PR in the dependency order recorded above, not by attempting one atomic change across 70+ files.