Skip to content

mr-transparent-routing

When transparent routing is enabled, swap the model that runs each subagent — keep the agent’s identity, prompt, and tools intact. This is what preserves engine-kit specialization (Unity DOTS, Cocos, etc.): the resolved Unity agent still runs, just on a cheap LLM instead of Opus.

Read .claude/t1k-config-mr.json. The hook + rule both only fire when:

  • File exists AND modelRouter.enabled is true AND modelRouter.mode is "transparent"

If any condition is false, this rule is inert.

Two mechanisms cooperate:

1. mr-task-interceptor.cjs (PreToolUse on Task)

Section titled “1. mr-task-interceptor.cjs (PreToolUse on Task)”

When the main session spawns a subagent via the Task tool, the hook resolves the agent’s .md, reads its model: frontmatter, and looks that tier up in modelRouter.modelMapping. On a match — and unless the agent is in excludeAgents or floored by one of the policies below — it runs mr-delegate.sh synchronously, DENIES the original Task, and returns the cheap delegation’s stdout as a systemMessage, so the parent sees it as the Task’s result. Otherwise: silent passthrough to Anthropic.

This is the primary mechanism, and it is mechanical and deterministic — it runs whether or not you read this rule. Full algorithm, every config key, and the worked examples: docs/mr-transparent-routing.md.

Selection precedence — modelMapping is LAST, and loadBalance shadows it

Section titled “Selection precedence — modelMapping is LAST, and loadBalance shadows it”

modelMapping is the knob people reach for first and the one the interceptor consults last. The resolved order, top to bottom — the first that yields a target wins, and everything below it is never consulted:

#MechanismApplies to
1perSpawnModelOverride (tool_input.model / an mr-model: prompt directive)that one spawn
2contextGrowth long-context preferenceagents in contextGrowthAgents only
3loadBalanceany task whose required caps the pool target satisfies
4Rule-based capability selectiontasks with any required capability, cheapest tier first
5modelMapping (tier → provider/model)last resort, when 1–4 all yield nothing

The consequence, stated plainly: while loadBalance.enabled is true, editing modelMapping changes nothing for ordinary text tasks and you get no warning that it didn’t. Rows 3 and 4 answer first, row 5 is never reached, and the edit is dead config that looks live. If a modelMapping change produced no behaviour change, check loadBalance.enabled before re-reading the mapping. (Row 3 only claims a delegation when the pool target satisfies every required capability and is healthy, so specialised tasks — long-context, vision — still fall through to row 4; that is why the shadowing looks intermittent rather than total.)

Which row fired, and why long-context disabling doesn’t reduce routing volume: docs/mr-transparent-routing.md.

2. Delegation Bias (this rule, behavioral)

Section titled “2. Delegation Bias (this rule, behavioral)”

For main-session work that doesn’t go through Task (inline Edit / Write / Bash), you decide whether to delegate. The interceptor only catches Task spawns; mainline edits stay on Opus unless you redirect them yourself.

Step 0 — MANDATORY pre-tool consultation (mainline only)

Section titled “Step 0 — MANDATORY pre-tool consultation (mainline only)”

Before ANY inline Edit / Write / Bash that mutates user files, evaluate Delegation Bias. The Task interceptor handles delegated work for you. This step is for the rest.

The check is one sentence: “Is this task mechanical, boilerplate, or single-purpose?”

  • Yes → spawn it via Task with the appropriate subagent_type (e.g. t1k-fullstack-developer for implementation, t1k-code-reviewer for review). The interceptor will route to a cheap model automatically. If no subagent_type fits, call mr-delegate.sh directly with a sensible agent name.
  • No → proceed with inline Edit/Write/Bash. State briefly why (e.g. “multi-file refactor needing cross-file context, inlining”).

Skipping this step and going straight to inline Read+Edit burns Opus tokens for work a cheap model could do. The session-start hook mr-transparent-routing-reminder.cjs reinforces this — if you see [t1k:transparent-routing] ACTIVE, Step 0 is required.

Step 0.5 — Parallel sub-agent dispatch (Agent tool, TeamCreate)

Section titled “Step 0.5 — Parallel sub-agent dispatch (Agent tool, TeamCreate)”

When fanning out parallel sub-agents for mechanical code work, pick the narrowest specialized T1K agent that fits the task. The transparent-routing interceptor will route the agent to a cheap provider automatically via modelMapping — no need to name a v1 cheap-coder shim.

Task patternPreferred subagent_type
Mass rename, mechanical refactor, edit-per-plant1k-fullstack-developer
Read-only exploration (“find/list/grep”)Explore (built-in)
Run tests + report resultst1k-tester
Code review (read-only with Bash for lint/grep)t1k-code-reviewer
Doc audit (read-only) or doc writes per spect1k-docs-manager
Multi-server MCP tool invocationt1k-mcp-manager

general-purpose is the FALLBACK when no specialized T1K agent matches. Default bias: pick the narrowest specialist that fits, not the broadest generalist.

Delegation Bias — Prefer delegation for mechanical work

Section titled “Delegation Bias — Prefer delegation for mechanical work”

The primary motivation is Opus token preservation. Cheap subagents cost roughly 1-5% of Opus per token.

Task patternDefault
Single-file rename, format, lint-fix, add boilerplateDelegate (Task → implementer-type agent)
Run a test suite + report resultsDelegate (Task → tester)
Update README / docstring / commentDelegate (Task → docs-writer)
Code review of changed lines (single PR / small scope)Delegate (Task → reviewer)
Find files matching a pattern, list usages, search refsDelegate (Task → explorer)
Audit existing docs for gapsDelegate (Task → docs-scout / reader)
Multi-file refactor with cross-file reasoningInline (Opus owns this)
Design decision, architecture, planningInline (judgment calls)
Task that needs 3+ different tool types or chained contextInline (orchestration overhead > delegation cost)
Reading one file to gather context (no edit follows)Inline (single Read is free)

Heuristic — apply BEFORE picking a tool: ask “is this task mechanical, boilerplate, or single-purpose?” If yes → spawn via Task. If it needs design judgement, cross-file reasoning, or 3+ distinct tools → inline. When in doubt for write/mutate tasks → delegate.

Anti-pattern: “the task is too trivial to spawn a subagent for.” That phrase is wrong when transparent routing is on. The Task interceptor does the heavy lifting — your job is just to USE Task for mechanical work instead of inlining.

  1. Parallel/multi-agent mode: skill invoked with --parallel flag or multi-agent pipeline.
  2. Orchestration tasks: planner, git-manager, brainstormer, project-manager — usually need Opus reasoning; mark them in excludeAgents if you want the interceptor to skip them.
  3. MR_SPAWNED=1: already inside a delegated session (interceptor self-skips, but inline edits should also skip).
  4. User explicitly requested Claude: user said “use Claude” or “don’t delegate”.

Three floors sit ABOVE modelMapping. Only the premium-tier floor is unconditional; the other two are config-gated and ship OPEN by default (a #302/#245 decision — full rationale, blast radius, and the clause-by-clause breakdown: docs/mr-transparent-routing.md).

Premium tiers are never routed. Any agent whose model: frontmatter is anything other than a proven cheap-routable tier (sonnet/haiku, shorthand or full-ID form) passes through to Anthropic before modelMapping is even consulted — opus, fable, inherit, a mistyped tier, and any model Anthropic ships after this file was last read all resolve identically. A modelMapping row for a non-cheap name is a no-op; the passthrough guard wins. This is a code-level guard (isKitPassthroughModel(), an allowlist of cheap tiers rather than an enumeration of premium ones — so a future premium model is protected without a kit release), not a config knob.

Write-capable and spawn-capable agents are floorable but do NOT floor by default:

modelRouter.writeAgentFloorBehavior
"premium-only" (aliases "off", false) — shipped defaultNothing floors beyond the premium-tier guard above
"named-agents"Also floors t1k-git-manager and *-developer-suffix agents
"all"Also floors any agent whose tools: declares a write tool (Write/Edit/MultiEdit/NotebookEdit) or is unrestricted — the pre-#302 behavior
modelRouter.spawnCapableFloorBehavior
"off" (or false) — shipped defaultDisabled entirely
"file-agents"Floors any agent whose tools: declares Agent/Task/TeamCreate
"all"Additionally floors general-purpose and claude

An unrecognized value on either key fails closed — to "all" for writeAgentFloor, "file-agents" for spawnCapableFloor. A config that omits the key entirely resolves the same way in-code; only the shipped t1k-config-mr.json sets the open values explicitly.

perSpawnModelOverride does not pierce these floors. A caller may name a model per spawn (tool_input.model, or an mr-model: directive in the prompt), but an opus/fable-declared agent — or a write-capable agent while its floor clause still covers it — resolves the override to null and stays on Anthropic. The resolved provider is still subject to the security allowlist below.

Fallback tier — a no-route passthrough must not silently land on Opus (#330)

Section titled “Fallback tier — a no-route passthrough must not silently land on Opus (#330)”

defaultBuiltInModel only steers routing; it does nothing once routing itself gives up. Any spawn with no agent .md resolving a real declared tier (Explore/general-purpose built-ins, an engine-kit agent whose .md lives in a kit not installed here, or a broken/typo’d subagent_type) previously rode the bare passthrough() exit with no pin at all — inheriting the SESSION model (Opus, in an Opus session) regardless of the work. Measured 2026-08-23: 58% of all passthroughs had no resolvable tier. modelRouter.fallbackTier (default, per-built-in builtinOverrides, enabled: false to opt out) pins a sane native tier via the SAME PreToolUse mechanism the interceptor already uses — hookSpecificOutput.updatedInput, which replaces the entire tool_input object, so the interceptor always spreads the original input and only adds model. Does not touch the already-correct pinned-tier path. The resolved tier is ALWAYS sonnet or haiku, never opus — user decision 2026-08-23 (“the final fallback will be the original anthropic cheap model”); CHEAP_FALLBACK_TIERS clamps any out-of-set config value rather than honoring it. Config shape, defaults, and the data-driven builtinOverrides rationale: docs/mr-transparent-routing.md.

Security — data-class gate + provider allowlist (#158)

Section titled “Security — data-class gate + provider allowlist (#158)”

Transparent routing re-originates a separate upstream connection to a third-party provider, which therefore gets full plaintext access to the prompt and code context (OWASP ASI04/ASI07). Two gates are mandatory and both fail toward Anthropic:

  • Data-classification gate. Before routing, the prompt is classified (mr-data-classifier.cjs). If a class listed in security.dataClassification.blockClasses is detected — absent/empty means any sensitive class — the interceptor does not route; the task runs on Anthropic native. Never work around a block by re-wording the prompt.
  • Provider allowlist. Only providers named in security.allowedProviders may be routed to, and the allowlist is enforced at both ends: the interceptor allowlists the primary pick, and mr-delegate.sh enforces it on every failover hop. A chain is only as safe as its weakest hop.

Known classes, patterns, config shape, and the audit breadcrumbs (pass-data-class-blocked / pass-provider-not-allowlisted in ~/.model-router/debug.jsonl): docs/mr-transparent-routing.md.

Timeouts — SSOT markers (do not mirror these values elsewhere)

Section titled “Timeouts — SSOT markers (do not mirror these values elsewhere)”

scripts/mr-validate-timeout-ssot.cjs gate-checks these two values against scripts/mr-defaults.cjs — this is their single normative statement:

  • perHopTimeoutSec — default 600. Per-attempt budget for one hop.
  • contextGrowthPerHopTimeoutSec — default 1800. Ceiling for agents in contextGrowthAgents, whose real payload arrives after the spawn; unset ⇒ max(1800, perHopTimeoutSec), so raising the global key must never shorten it. Raised 1200 → 1800 on 2026-09-03: over 24h, 23 hops died on the 1200s wall and all 23 were clock-bound (0 hit the turn or budget cap, so the routedTurnsMultiplier could not have saved them); 22 recovered real transcript text yet every one was emitted DEGRADED, not an answer and re-run on Anthropic from scratch. t1k-fullstack-developer was 14 of the 23.
  • contextGrowthMinTurns — default 20 (#311). An agent NOT in contextGrowthAgents by name still gets the contextGrowthPerHopTimeoutSec ceiling when its own declared maxTurns frontmatter reaches this floor — the same censoring pattern #261/#275 fixed for Explore/general-purpose also hits write-capable implementers doing genuine multi-turn work, and a hardcoded 2-name allowlist has to be remembered for every new one. Keyed on the agent’s OWN declared value, never mr-delegate.sh’s post-fallback default (25) — an agent that never declares maxTurns must not silently opt in. 0 disables the turn-budget widening entirely (name-only membership, pre-#311 behavior). Governs the timeout ceiling only — the separate contextGrowth MODEL-SELECTION preference (Selection precedence row 2, above) stays name-based on purpose.

Cost of those defaults: the worst case is computed, never frozen. mr-validate-hook-timeout.cjs derives it from the SSOT computeOuterBudgetSec (mr-defaults.cjs): needed = ((perHopSec × (1 + maxRetries) + probeSec + resumeSec) × hopCount) + bufferSec, where perHop = 1800s (the contextGrowthPerHopTimeoutSec ceiling — one registered timeout covers every agent), maxRetries=1, probe=60s, resumeSec = 600s (perHop / RESUME_CEILING_DIVISOR, the ONE resume attempt #399 spends on a slow-had-output ceiling kill), buffer=30s → 4260s per hop, and hopCount = pipe.length > 0 ? 1 + pipe.length : 2. Today’s single-entry pipe (2 hops) needs 8550s against the registered 8610s (mr-task-interceptor, .claude/settings.json:21) — 60s headroom. No further pipe entry fits: 3 hops would need 12810s and fail the gate, so widening the pipe now requires raising the registered timeout in the same change. Recompute from the formula on any pipe/constant change — a frozen constant is how “zero headroom” went stale (corrected 2026-08-29, re-derived 2026-09-03, resume term folded in 2026-09-04 — the pre-resume formula asserted a bound it did not actually hold, since #399 already spent the resume term in mr-delegate.sh regardless of what this formula believed).

The ceiling is clamped at runtime to what the REGISTERED outer timeout can afford. The two numbers are set by different actors in different files: the ceiling above is a kit constant, while the PreToolUse Task|Agent timeout Claude Code actually kills the interceptor at is whatever is on that machine’s disk. mr-validate-hook-timeout.cjs gates that they agree, but runs in CI against the KIT CHECKOUT — a consumer whose settings.json has drifted below the budget gets no warning and every long delegation is SIGTERM’d mid-hop instead of failing over (measured 2026-09-03; theonekit-cli#504). So the interceptor reads its own registered timeout and inverts computeOuterBudgetSec for the largest per-hop it can pay: at 5520 that is 1150s, at 8610 it is 1812s (both resume-inclusive as of 2026-09-04 — the pre-resume inverse returned 1342s at 5520, which was never actually affordable once #399’s resume spend is priced in). The clamp can only ever LOWER the ceiling, and an unreadable/absent/unparseable registration clamps NOTHING — failing toward the constant preserves today’s behaviour, whereas failing toward a larger number would silently reintroduce fleet-wide SIGTERM. Constants are untouched; this is a runtime floor-guard, not an SSOT change. A per-hop-ceiling-clamped row in ~/.model-router/debug.jsonl is how you see it fire.

Every delegation publishes ~/.model-router/runs/<traceId>.json ({traceId, agent, provider, model, state, startedAt, updatedAt, sid, hop, turns}, state ∈ running | retrying | failing-over | done | failed, plus exit when terminal), written by temp-file + rename at the four points mr-delegate.sh already reaches. Retention MR_RUN_STATE_RETAIN_MIN minutes (default 720), swept on any terminal write; MR_RUN_STATE_ENABLED=0 disables it. The per-hop scratch envelope (~/.model-router/<traceId>[-h<N>].stdout / .stderr + orphaned .ceiling-kill markers, written by the hop spawn and NOT cleaned by the immediate rm -f paths, which only delete the last hop) is kept under the same rule: MR_SCRATCH_RETAIN_MIN minutes, default 720, same find -mmin +N sweep on the same terminal write, disabled with MR_SCRATCH_RETAIN_ENABLED=0. The sweep is mtime-based and cannot touch a live hop’s files (their mtime is seconds old while the hop writes) or a stalled hop’s (ceiling ≤1800s, far inside 12h).

sid is the point of the file. _hop_session_id mints the hop’s session UUID before the spawn and passes --session-id, so ~/.claude/projects/<slug>/<sid>.jsonl is locatable while the hop is still writing — but that id never left the script, and debug.jsonl route rows carry no session id in any form. Publishing traceId → sid → agent is the missing link that makes routed-hop behaviour measurable at all: given a trace you can now find its transcript.

Read it with mr-status.cjs (--json, --all, --stall N) or the statusline segment (mr-status-segment.cjs, renderRoutingSegment()). Both DERIVE staleness from the transcript’s mtime at read time rather than storing it — staleness is a function of “now”, so a stored flag is wrong the moment it is written. A live hop can look idle while ONE slow tool runs; read the transcript before calling it wedged. Full detail, and why the statusline segment is a library core requires rather than a hook this kit registers: docs/mr-transparent-routing.md.

Failover pipe order, the circuit-breaker state machine, in-hop retry, config-block schemas, and the measurement narrative behind each tuning: docs/mr-transparent-routing.md.

Guard coverage — routed spawns fire no SubagentStop (#784)

Section titled “Guard coverage — routed spawns fire no SubagentStop (#784)”

A routed delegation produces no SubagentStop event: the interceptor delivers its result by DENYING the original Task, so no native sub-agent is ever spawned and there is no lifecycle to stop. Every SubagentStop-only guard is therefore blind to routed agents — workflow-failure-detector.cjs (P1/P6) and subagent-uncommitted-guard.cjs (#508/#613) do not cover them; lesson-collector.cjs does, only because it is also registered on Stop.

Because modelMapping routes the sonnet/haiku tiers and defaultBuiltInModel routes the built-ins, those guards effectively cover opus agents only — i.e. not the agents most likely to be doing mechanical edit work. Do not rely on them to catch an uncommitted or empty-handed routed agent; check its work yourself. mr-delegate.sh appends what facts it has to ~/.model-router/routed-stops.jsonl (zero-work signals, dirty-tree backstop); why we do not synthesise a SubagentStop, and the wiring-up path: docs/mr-transparent-routing.md.

Routed hop environment — no team roster, no unconditional MCP, no fan-out

Section titled “Routed hop environment — no team roster, no unconditional MCP, no fan-out”

A routed hop is a standalone claude -p subprocess. It sits outside any team roster, so SendMessage has no peer to address even where nominally present. Agent is disallowed outright; Task/TeamCreate are absent from the allowlist and stall on a headless approval prompt — no fan-out. Write tools follow the agent’s own surface: read-only built-ins (Explore, Plan) default to plan (no Edit/Write); every other agent defaults to acceptEdits (Write enabled) unless frontmatter overrides it (#132) — routed hops are NOT uniformly plan-mode.

MCP is forwarded only when both sides agree (#314). The hop’s default --mcp-config is still '{"mcpServers":{}}' (no server) — that default is what fires when either condition below fails. It is overridden with a real config file only when the agent’s tools: declares mcp__<Server>__* tools AND that <Server> is listed in modelRouter.security.allowedMcpServers. The allowlist is fail-closed: empty or absent, nothing is forwarded for any agent. A built-in with no .md (general-purpose, Explore) has no tools: to read, so it can never forward — a structural ceiling, not a config gap.

Brief accordingly: never make SendMessage or a file write the ONLY delivery channel — put the full deliverable in the final message; the interceptor already returns that text as the Task’s result (below).

Delivery Contract resolution (#265). Core’s per-agent ## Delivery Contract (“your deliverable IS your returned summary, sent via SendMessage”) and this section read as directly contradictory — and an agent that is actually in a routed hop cannot satisfy core’s instruction, because the tool it names is not there. They are not actually in conflict; core’s wording is just written for the native-roster case and doesn’t carve out this one. Resolution: the routed hop’s own final assistant message is the routed-hop-native form of SendMessage, not a lesser fallback that might not arrive. The interceptor forwards that exact text back to whoever spawned the Task (see Delegation Output, below) — that forwarding is the return channel core’s contract asks for, running over a different transport. Put the full deliverable there and core’s Delivery Contract is satisfied by construction, even though the named tool never ran.

This is not merely documented — it is enforced in the prompt every routed hop actually receives. mr-delegate.sh prepends an [mr:routed-hop] preamble to every hop’s task (MR_HOP_PREAMBLE, added by #296) stating explicitly: “You are not in a team, so SendMessage reaches nobody, and no user is present” and “Your final message is the ONLY channel back to the caller. Put the complete deliverable in it.” An agent inside a routed hop therefore does not need to infer which contract applies from its own tool list at all — the preamble tells it outright, ahead of its own agent body’s (possibly contradictory) instructions. This section’s role is to resolve the same question for anyone reading the RULES (a kit maintainer, or a native agent deciding what to promise a hop it is about to brief) rather than the live hop itself.

The observable signal an agent can branch on, for the case where no [mr:routed-hop] preamble is present (e.g. reasoning about this rule outside a live hop): whether SendMessage appears in your own available tools. No new environment variable or capability probe is needed — the tool’s own absence (not an error, not a permission denial; it is simply not present) already tells an agent which environment it is in. A built-in with no .md (Explore, general-purpose) never sees core’s Delivery Contract text at all in the first place — this is the guidance that applies to it once routed, since core#904 has no agent body to install into.

What this does NOT resolve — measured, and it is not a routing defect. The issue’s t1k-researcher residue — declares SendMessage and Agent, so per #245 it should have stayed in-roster with a working SendMessage, and it still returned nothing twice on a kit already carrying the core#904 contract fix — asked for a runtime investigation rather than a doc clarification. That investigation ran (2026-08-27) against ~/.model-router/debug.jsonl: every t1k-researcher row is decision: pass-kit-policy (33 of 33, 2026-06-08 → 2026-08-27), as is every row for any modelKey: opus agent (551 of 551). The premium-tier floor held on every spawn — the agent was never routed, so it ran natively with a working SendMessage and ended its turn without calling it.

That makes the residue a core Delivery Contract compliance question, not a model-router one, and this rule’s routed-hop guidance does not apply to it: nothing here can make a native agent call a tool it already holds.

Attributing the next one costs a jq, not an archaeology pass. Every agent-identified intercept row now carries sendMessageDeclared, read from the agent’s own tools: (null for a built-in, which declares nothing either way). Read it beside decision:

decisionsendMessageDeclaredWhat an empty return means
routetrueThe hop could not reach the channel its body names — MR_HOP_PREAMBLE is the surface, and this section applies
pass-*trueNative, held a working SendMessage, did not call it — a core question
eitherfalse / absentThe agent never had the channel; its final message was always the only route back

The interceptor hands the cheap model’s text back in two fields at once, and only one of them reaches the model: permissionDecisionReason is what is injected into the calling LLM’s context, systemMessage is the user-visible copy in the transcript. Both carry the same body (banner line + --- Delegated agent output --- + stdout). An earlier design put the body in systemMessage ALONE, so the parent session received the banner and nothing else and the delegated answer was invisible to Claude (mr-task-interceptor.cjs ~line 2335). The duplication is deliberate — do not collapse it.

A SUCCESSFUL routed delegation renders as Error: in the transcript

Section titled “A SUCCESSFUL routed delegation renders as Error: in the transcript”
Error: [t1k:mr] ✓ ROUTED OK — t1k-doctor-manager[deepseek-v4-flash] — opencode-go (loadBalance:...)
--- Delegated agent output ---
...

The Error: prefix is the harness rendering the mechanism, not a router fault. Substituting a result for a tool call is only possible under the PreToolUse contract by DENYING the original call (permissionDecision: 'deny', mr-task-interceptor.cjs ~line 2354), and Claude Code renders every deny as Error:. The deny is load-bearing — do not “fix” it. Without it the Task also spawns on Anthropic, on top of the cheap call already made, and both are billed.

Read the BODY, not the prefix. The banner glyph now reports the delegation’s OUTCOME, not just that routing fired. ✓ ROUTED OK — agent[model] — provider (selector) means mr-delegate.sh exited 0; ✗ ROUTED FAILED (exit <n>) means it did not, and the second line carries output may be partial — see --- stderr --- below. A recovered result ([mr:recovered-result] / [mr:partial-result] in the output) is still a success but says ROUTED OK (result reconstructed) on the second line. Line 1 keeps a constant shape (no variable-cardinality counts) so the parent prompt cache is not busted by a failed delegation.

If mr-delegate.sh exits non-zero (timeout, provider down), the interceptor surfaces what it has + the exit code. Don’t let the Task fall through to Anthropic on error — that would burn Opus tokens AND the cheap call’s tokens. If the delegation failed, decide whether to retry or inline. Exit 42 is the one exception: all cheap providers exhausted with Anthropic fallback opted in passes through instead of denying, so it shows no Error: banner at all.

docs/mr-transparent-routing.md — interceptor algorithm, modelMapping / defaultBuiltInModel / fallbackTier / contextGrowthAgents / perSpawnModelOverride schemas and worked examples, the routing-floor clause-by-clause breakdown and #302/#245 rationale, the failover.pipe / inHopRetry / circuitBreaker config blocks and state machine, per-hop budget arithmetic, the security pattern catalogue, and the incident archaeology (#211 inherited MCP schemas, #229, #236, #244, #249, #261, #330, #784).