[BUG]Advisor tool injects same-tier opus calls into every opus subagent, doubling spend invisibly
Summary
When the session model and advisorModel are both opus, the advisor-injection gate accepts the pairing because its two whitelists are byte-identical and there is no tier comparison. The advisor is also injected into every subagent (Task / Agent) context — so a 3-subagent opus team silently spawns ~4 extra same-tier opus calls that never appear in /context, never roll up to the orchestrator's token accounting, and emit no log line. The behavior directly contradicts the advisor docstring, which describes a "stronger reviewer model."
---
Expected behavior
The bundled advisor docstring (Vt9 in 2.1.138) states:
You have access to an advisor tool backed by a stronger reviewer model. It takes NO parameters -- when you call advisor(), your entire conversation history is automatically forwarded.
The implied contract:
- Advisor calls go to a strictly stronger tier than the consulting context.
- Same-tier (opus → opus) or weaker-tier advisor pairings should be rejected.
- Costs incurred by
advisor()should be observable to the user, either in/contextor in the subagent summary returned to the orchestrator.
---
Actual behavior
baseModel = claude-opus-4-7 paired with advisorModel = claude-opus-4-7 is accepted, and the same gate runs in every subagent context. With opus subagents the cost compounds:
Orchestrator (opus)
├── advisor() → opus ← 1 invisible call
├── Subagent: planner (opus)
│ └── advisor() → opus ← 1 more invisible call
├── Subagent: architect (opus)
│ └── advisor() → opus ← 1 more invisible call
└── Subagent: executor (opus)
└── advisor() → opus ← 1 more invisible call
A 3-subagent opus team produces ~8 opus turns (4 worker turns + 4 advisor turns) where the user expects 4. Each advisor call duplicates the consulting agent's full transcript into a same-tier model, so token spend roughly doubles for no marginal capability gain.
---
Why it's hard to detect
- Advisor calls inside subagents run in nested contexts. Their tokens do not appear in the main session's
/contextview. - The orchestrator sees
subagent: <name> (opus) — completedwith no breakdown of advisor calls made during the run. - No log line, no warning, no per-tool budget attribution. The first signal is the billing statement.
- Subagent advisor calls also don't roll up into the orchestrator's context-window accounting, so users actively watching token usage still see nothing.
- The advisor docstring suggests "stronger model," so users reasonably assume same-tier pairings are blocked by the gate.
---
Reproduction steps
- Run Claude Code with
model = claude-opus-4-7andadvisorModeleither unset (defaults to the session model when opus) or explicitly set to an opus tier insettings.json. - Start any task that spawns an opus subagent via
Task/Agent. - Inside the subagent, take an action that triggers an
advisor()call (any substantive decision point in a multi-step task — the advisor tool is exposed in subagent contexts). - Observe: the advisor call succeeds and runs on opus. The call does not appear in the orchestrator's
/context, and the subagent's return summary contains no advisor accounting.
Observed on Claude Code 2.1.138 (macOS arm64).
---
Root cause (code level)
In the 2.1.138 bundled JS, Zt9 is the advisor-injection gate. It contains no baseModel vs advisorModel tier comparison:
function Zt9(advisorModel, baseModel) {
if (!lC() || !advisorModel) return;
let q = normalize(advisorModel);
if (!D$H(baseModel)) return; // baseModel whitelist
if (!iA_(q)) return; // advisorModel whitelist
return q;
}
function D$H(H) {
return H.toLowerCase().includes("opus-4-7")
|| H.toLowerCase().includes("opus-4-6")
|| H.toLowerCase().includes("sonnet-4-6");
}
function iA_(H) {
return H.toLowerCase().includes("opus-4-7")
|| H.toLowerCase().includes("opus-4-6")
|| H.toLowerCase().includes("sonnet-4-6");
}
The two whitelists are byte-identical. There is no ordered tier mapping (e.g. sonnet-4-6 < opus-4-6 < opus-4-7), so the gate cannot reject same-tier pairings. The same Zt9 codepath is reused when the advisor tool is injected into subagent contexts; there is no subagent-specific guard.
Related symbols in the same binary:
Vt9— advisor tool docstring ("stronger reviewer model")lC()— env-toggle gate (CLAUDE_CODE_DISABLE_ADVISOR_TOOL,CLAUDE_CODE_ENABLE_EXPERIMENTAL_ADVISOR_TOOL)- Server-side tool name:
advisor_20260301
Source: ~/.local/share/claude/versions/2.1.138 (Mach-O bundled JS, decomp via strings).
---
Proposed fixes
1. Minimum fix — tier guard in Zt9. Add an ordered tier mapping and require the advisor to be strictly stronger than the base model. modelTier() below does not exist in 2.1.138; it needs a small new helper that maps the existing whitelist entries to ordered tiers (e.g. sonnet-4-6 < opus-4-6 < opus-4-7):
// Proposed — modelTier() is new; maps whitelist entries to ordered tiers.
function Zt9(advisorModel, baseModel) {
if (!lC() || !advisorModel) return;
let q = normalize(advisorModel);
if (!D$H(baseModel)) return;
if (!iA_(q)) return;
if (modelTier(q) <= modelTier(baseModel)) return; // require strictly stronger advisor
return q;
}
2. Stronger fix — don't inject advisor into subagent contexts by default. Subagents are already purpose-scoped by the orchestrator, so the advisor relationship doesn't need to propagate transitively. Require explicit per-subagent opt-in (a flag on the Task call) if subagent-level advisor remains a supported workflow.
3. Observability fallback (independent of either fix above). Surface advisor invocations (count + tokens) in the subagent summary returned to the orchestrator, and include nested advisor token counts in /context. Even before the tier or propagation behavior changes, this makes the spend observable rather than invisible. This is the cheapest change of the three and useful regardless of which structural fix is taken.
The three are not mutually exclusive: (1) closes the same-tier hole, (2) prevents transitive amplification, (3) prevents future regressions of the same shape from being undetectable.
---
Environment
- Claude Code: 2.1.138
- Platform: macOS arm64 (also reportedly affects Linux on earlier 2.1.x)
- Repro independent of any third-party tooling; multi-agent sessions amplify the signal
This issue has 7 comments on GitHub. Read the full discussion on GitHub ↗