DEV Community

Cover image for Your Claude Code Subagents Inherit Your Effort Level. 2.1.316 Lets You Set Them Lower
Gabriel Anhaia
Gabriel Anhaia

Posted on Edited on AI-assisted

Your Claude Code Subagents Inherit Your Effort Level. 2.1.316 Lets You Set Them Lower


Three Explore subagents sent out at high to find every caller of the one function you're refactoring. Until Claude Code 2.1.316, that is what a high session got you: a subagent with no effort of its own inherited the session's, unless Claude named a level when it spawned it.

The changelog entry for 2.1.316, dated October 31, 2026, reads: "Added CLAUDE_CODE_SUBAGENT_EFFORT to set the effort level subagents run at when nothing else names one, instead of inheriting the session's."

The official docs stop there. The environment variable reference doesn't list the variable yet, so everything below about its behaviour comes from that entry plus the documented effort rules around it. What it saves depends on your repo, and the transcripts overcount if you sum them naively.

What the variable does, and what still beats it

Effort controls adaptive reasoning. The model configuration docs put it plainly: "Lower effort is faster and cheaper for straightforward tasks, while higher effort provides deeper reasoning for complex problems." The levels run low, medium, high, xhigh, max, depending on the model. Opus 5.5, Sonnet 5.5 and Haiku 5.5 default to medium when nothing sets a level. Thinking tokens are billed as output tokens, so effort shows up directly in the bill.

"When nothing else names one" is doing the work here. Three things already name an effort for a subagent, per the subagents docs:

  • An effort field in the subagent's frontmatter. It overrides the session level.
  • An effort parameter Claude passes when it spawns the subagent (v2.1.292 or later). It overrides the frontmatter field.
  • The CLAUDE_CODE_EFFORT_LEVEL environment variable, which takes precedence over both.

So if you export CLAUDE_CODE_EFFORT_LEVEL, that is something naming a level, and the new variable has nothing left to decide. Unset it for this experiment.

Explore is where this lands. The docs describe the built-in Explore subagent as read-only (Write and Edit are denied), used for "file discovery, code search, codebase exploration", running on the main conversation's model. They list no effort level for it, so unless Claude passes one at spawn time, Explore is exactly the "nothing else names one" case. Effort and model are separate knobs: CLAUDE_CODE_SUBAGENT_MODEL on its own doesn't change the model Explore and Plan run on, per the same page.

A large black dial turned high, with three small magnifier figures tethered to it, each holding its own smaller dial turned low
The session keeps its effort. Subagents with nothing else set get their own.

Two worktrees, one read-heavy task

Check your version first. You need 2.1.316 or later:

claude --version
claude update
Enter fullscreen mode Exit fullscreen mode

Then give each run its own folder, so neither one sees the other's session history or files:

cd ~/code/app
git worktree add -b effort/inherit ../app-inherit
git worktree add -b effort/low ../app-low
Enter fullscreen mode Exit fullscreen mode

Pick a task that makes Claude explore and doesn't need edits. Asking it to find something you can check yourself works well, because it gives you a ground truth for the quality check later:

TASK='Use Explore subagents to find every place
this repo reads an environment variable. List
each one as file:line and the variable name.
Do not edit anything.'
Enter fullscreen mode Exit fullscreen mode

Write down the answer key now, with plain git grep. Adapt the pattern to your stack:

git grep -n -E 'process\.env|os\.Getenv|getenv\(' \
  > ~/env-reads-baseline.txt
wc -l ~/env-reads-baseline.txt
Enter fullscreen mode Exit fullscreen mode

Run it twice and read the totals

Pin the model and the session effort in both runs, so the only difference is the subagents' effort. With --output-format json, the headless docs say the payload includes total_cost_usd and a per-model cost breakdown. The cost tracking guide adds that total_cost_usd counts subagent requests alongside the main loop.

cd ~/code/app-inherit
claude -p "$TASK" --model opus --effort high \
  --max-budget-usd 3 --output-format json \
  > ~/effort-inherit.json

cd ~/code/app-low
CLAUDE_CODE_SUBAGENT_EFFORT=low \
  claude -p "$TASK" --model opus --effort high \
  --max-budget-usd 3 --output-format json \
  > ~/effort-low.json
Enter fullscreen mode Exit fullscreen mode

--max-budget-usd is a guard: subagent spend counts toward the cap, and spend can pass it, so leave headroom. Since v2.1.302, a claude -p run starts in Manual mode when nothing sets a permission mode, and a call that needs approval is denied with nobody there to give it. For a read-only comparison, that is what you want.

Now compare:

for f in ~/effort-inherit.json ~/effort-low.json; do
  echo "$f"
  jq '{cost: .total_cost_usd,
       models: (.modelUsage
         | map_values({costUSD, outputTokens}))}' "$f"
done
Enter fullscreen mode Exit fullscreen mode

Those dollar figures are client-side estimates computed from token counts at list price, not your bill. On a Pro, Max or Ultra plan the dollar amount isn't what you pay at all, but the token counts still tell you which run drew more of your limits.

One pair of runs is an anecdote. Claude decides how many Explore agents to start and how thorough each should be (quick, medium or very thorough, per the docs), so the same prompt can fan out differently twice. Run each side three times, alternate which goes first, and compare the medians.

Split the subagents' share out of the transcripts

The totals tell you whether the low run was cheaper. The transcripts tell you how much of it was the subagents. Subagent transcripts live under ~/.claude/projects/{project}/{sessionId}/subagents/, one agent-{agentId}.jsonl per subagent.

Don't sum every usage object in those files. One API response that contains several content blocks is written as several assistant entries that share one message.id and carry the same usage. A maintainer confirmed in issue #87303 that this is intended: "Consumers are expected to count each message.id once". The reporter's sample overcounted cache writes by 1.92x when summed naively. The cost tracking guide also warns that per-step output_tokens is a placeholder, so take output totals from the run's JSON above, not from the transcripts. I wrote up the full set of traps in a separate post on summing transcript usage.

This shell function counts each response once, across every subagent file of one session, and reports the input and cache tokens:

sub_usage() {
  find ~/.claude/projects -path "*/$1/subagents/*" \
    -name 'agent-*.jsonl' -exec cat {} + |
  jq -cR 'fromjson? // empty
    | select(.type == "assistant")
    | select(.message.id and .message.usage)
    | {id: .message.id, u: .message.usage}' |
  jq -s 'unique_by(.id) | map(.u) | {
    responses: length,
    input: (map(.input_tokens // 0) | add),
    cache_write:
      (map(.cache_creation_input_tokens // 0) | add),
    cache_read:
      (map(.cache_read_input_tokens // 0) | add)
  }'
}

sub_usage "$(jq -r .session_id ~/effort-inherit.json)"
sub_usage "$(jq -r .session_id ~/effort-low.json)"
Enter fullscreen mode Exit fullscreen mode

fromjson? // empty skips a line that doesn't parse instead of stopping, and entries with no message.id are dropped rather than lumped together. Count how many subagents each run started, too. If the low run started fewer, part of any difference is fan-out, not effort:

SID=$(jq -r .session_id ~/effort-low.json)
find ~/.claude/projects -path "*/$SID/subagents/*" \
  -name 'agent-*.jsonl' | wc -l
Enter fullscreen mode Exit fullscreen mode

Then check quality against the answer key. Pull the file:line pairs out of each answer and count what each run missed from the baseline:

cut -d: -f1,2 ~/env-reads-baseline.txt | sort -u \
  > ~/key.txt
for a in inherit low; do
  jq -r .result ~/effort-$a.json |
    grep -oE '[A-Za-z0-9_./-]+:[0-9]+' | sort -u \
    > ~/ans-$a.txt
  echo "$a missed: $(comm -23 ~/key.txt ~/ans-$a.txt | wc -l)"
done
Enter fullscreen mode Exit fullscreen mode

The match only works when the answer uses the same repo-relative paths git grep prints, so skim comm -23 ~/key.txt ~/ans-low.txt before you trust the count. A cheaper run that missed four of thirty env reads did not save you anything. If low drops results on your repo, try medium before giving up on the setting.

Watch both live in Hermes, then keep the cheaper setup

I build Hermes IDE, so weigh this section with that in mind. The headless runs above are the measurement. Hermes is for watching the interactive version, where you see the agents work as they go.

Press ⌘N (Ctrl+Shift+N on Windows and Linux) twice and start two Claude Code tasks on the same repo. Each gets its own hermes/ branch and worktree. Set the model chip to Opus and the effort chip to high in both. Give both the same cheap first task, such as "Summarize README.md in two sentences", then split the panes so they sit side by side. The sidebar shows each session's spend: the cost the agent reports, or an estimate from its transcript marked as estimated, with token totals.

Two agent sessions side by side in split panes, each in its own hermes/ worktree, with the sessions sidebar showing status, context use, cost and branch
Hermes 2.3.4: two real agent sessions in split panes, each in its own hermes/ worktree, with status, context use, cost and branch per session in the sidebar.

Find the second task's folder with git worktree list and create .claude/settings.local.json in it:

{
  "env": {
    "CLAUDE_CODE_SUBAGENT_EFFORT": "low"
  }
}
Enter fullscreen mode Exit fullscreen mode

The env var docs say a saved env block applies to a running session, but I haven't checked whether this particular variable is re-read mid-session, so don't rely on it. Quit Claude Code in the second pane and run claude -c --model opus --effort high in its worktree, which continues the same conversation with the same main-session settings. Now paste $TASK into both panes and watch them fan out. When both finish, run /usage in each pane for Claude Code's own session totals.

If low holds up on your repo, keep it. To make it the default for every new Hermes task in that repo, put the same settings.local.json in your main checkout and have the worktree recipe copy it into each new worktree before the agent starts:

# .hermes/worktree.toml
copy = [".claude/settings.local.json"]
Enter fullscreen mode Exit fullscreen mode

The recipe's copy only takes files git ignores, and refuses the whole recipe otherwise. git check-ignore -v .claude/settings.local.json prints the matching ignore rule if it is covered. The main-session part of the setup (agent, model, effort, account) goes in a Hermes preset, so the rest of the combination is one pick in the launcher.

The new task sheet with a task typed in, chips for Claude Code, the project, a new hermes/ worktree, Accept edits, model and effort, and a preview of the claude command Hermes will run
The ⌘N New task sheet in Hermes 2.3.4: agent, project, worktree, approval mode, model and effort chips, and the "Hermes will run" preview of the claude command.

A subagent that needs to think, a code reviewer for example, can keep its own effort: high in its frontmatter. That names a level, so the variable leaves it alone, and only the agents with nothing set drop to low.


If this was useful

Running the same task twice and checking both the bill and the answer is a small eval, and that habit is what keeps LLM features honest once they're in production. Observability for LLM Applications covers the full version of it: tracing each request, tying cost to the step that spent it, and catching quality regressions before your users do.

Hermes IDE is free to download for macOS, Windows and Linux, source-available under BSL 1.1, and on GitHub.

Observability for LLM Applications — the book

Top comments (0)