[Bug] Subagent pinned to claude-fable-5 deterministically swaps to claude-opus-5 after exactly 3 turns
Bug Description
# Bug report v3 — deterministic mid-run model substitution: pinned claude-fable-5 subagents swap to claude-opus-5 after exactly 3 calls
Observed: 2026-08-05 and 2026-08-06 (7 consecutive runs) · Claude Code: 2.1.221 at last verification (2026-08-04; current session's version not re-checked) · Type: model routing / serving-side substitution · Supersedes/extends: the v2 report + addenda of 2026-08-04 (same environment)
## Summary
A subagent whose definition pins model: claude-fable-5 is served claude-fable-5 for exactly its first 3 assistant turns, then silently served claude-opus-5 for every remaining turn of the same run. This 3-then-swap signature has now occurred in 7 of 7 consecutive launches of the same agent definition, across two days, two separate sessions, and task lengths from ~28 to ~171 API calls. The swap point's regularity is the new information: earlier observations (v2) included launch-time substitution and mid-run swaps at irregular points (after 13 calls; after 3); the behavior has now stabilized into a deterministic boundary at call 3→4.
## Configuration under test (unchanged across all 7 runs)
- Subagent frontmatter: model: claude-fable-5; tools: Read, Edit, Write, Grep, Glob, Bash (deliberately no Skill, no Agent — the restricted shape that previously passed a 5/5 all-fable acceptance on 2026-08-04 ~16:56).
- Launched via the Agent/Task tool with no model parameter (frontmatter pin governs); CLAUDE_CODE_SUBAGENT_MODEL unset/inherit.
- Main conversation loop: unaffected in all observations.
## The measurement (serving stamps — the API response's own model field per assistant turn, tallied per child transcript)
grep -oh '"model":"claude-[^"]*"' <child_transcript>.jsonl | sort | uniq -c
| run | date | served claude-fable-5 | served claude-opus-5 |
|---|---|---|---|
| 1 | 2026-08-05 ~10:45 | 3 | 64 |
| 2 | 2026-08-05 ~11:50 | 3 | 122 |
| 3 | 2026-08-05 ~12:55 | 3 | 161 |
| 4 | 2026-08-05 ~14:40 | 3 | 168 |
| 5 | 2026-08-05 ~15:20 | 3 | 90 |
| 6 | 2026-08-05 ~16:25 | 3 | 148 |
| 7 | 2026-08-06 ~11:16 (checked mid-run) | 3 | 25+ (still running at check) |
Every run: the first 3 assistant turns are fable, all subsequent turns are opus-5, with no error, no warning, and no change in any request-side parameter across the boundary (v2's differential audit had already eliminated the visible request dimensions on an earlier instance of the swap).
## Contrast data from the same sessions
- Children of other agent types requesting sonnet in the same sessions: served claude-sonnet-5 faithfully every time (7+ runs, 34–168 calls each, zero substitutions).
- One harness-built "Plan" subagent (a different, restricted tool set) on 2026-08-05 ~10:10 was served claude-fable-5 for 47/47 turns — the only fully-faithful fable run in the window, within the hour before run 1 above began its 3-then-swap.
- Explicitly-requested claude-opus-5 (a pinned supervised executor) and the main loop's own model: always served as requested.
## What v2 established that still holds
- The substitution is serving-side: launch-time resolution is correct (resolvedModel: claude-fable-5), the pin is honored at request level, and the swap is invisible except in per-turn serving stamps. Child self-report is unreliable for detection.
- Request-side dimensions (arguments, effort, session, thinking, size to ~87k, timing) were eliminated by a differential audit; granting the Skill tool was isolated as a sufficient-but-not-necessary trigger; two independent projects reproduced 0/15 faithful on the identical definition.
- Rates were session/time-conditioned (100% faithful in one window, 0% in others on unchanged files).
## What is NEW in v3
1. The swap point is deterministic: exactly 3 fable turns, then opus-5, in 7/7 consecutive runs of the unchanged definition — stable across days, sessions, and task sizes. Previously the swap point varied (13-then-74 on one agent; 3-then-24 once).
2. The regularity suggests a systematic mechanism at the serving layer rather than random failover. Hypotheses (unverified, offered for triage): a router re-evaluation after N turns; a context-size threshold crossed once the first few (typically small) tool results give way to larger ones; a serving-affinity or cache TTL expiring after the initial calls — or something else on the serving side we cannot observe.
3. Practical consequence: a "first-turns spot check" of a run now reads as faithful while ~95% of the run executes on the wrong model. Any client-side verification must tally every turn.
## Reproduction
1. Define a subagent pinned model: claude-fable-5, tools Read, Edit, Write, Grep, Glob, Bash, no Skill/Agent.
2. Launch paramless with a multi-step task (≥10 tool calls).
3. Tally serving stamps per the command above.…
Note: Content was truncated.