Workflow code-review burns excessive tokens (1.1M+ for 5 files) and returns empty results
Problem
The code-review workflow (Workflow({ name: "code-review" })) consistently consumes disproportionate tokens and wall-clock time relative to the work being done, often returning null/empty results.
Reproduction
In a session reviewing 5 changed files (3 Python scripts, 1 YAML config, 1 Markdown skill definition — all small), the workflow:
- Consumed ~1.1M tokens (went from ~33% of a 4-hour budget to ~75% in minutes)
- Ran for 18+ minutes before being manually stopped
- All 4 partial results in the journal were
null— zero findings returned
This is the third incident in the same project where the workflow code review went off the rails in the same way.
For comparison, fork-based reviews (/internal-code-reviewer, /internal-test-reviewer) of the same files completed in under 2 minutes, consumed a fraction of the tokens, and returned actionable findings (dead code, variable shadowing, stdout leaks in tests).
Impact
- Real budget cost: ~40% of a 4-hour session budget burned on a single review that produced nothing
- The user had remaining implementation work (2+ tasks) that is now budget-constrained
- The workflow's token consumption is not visible or controllable until after the damage is done
Expected behavior
A code review of 5 small files should not consume more tokens than writing those files did. The workflow should either:
- Scale its agent count to the size of the changeset (5 files doesn't need the same fan-out as 50)
- Have a token budget cap that prevents runaway consumption
- Fail fast if agents are returning empty results rather than continuing to spawn more
Environment
- Claude Code CLI
- Model: Opus
- The
code-reviewworkflow is a built-in named workflow, not a custom script
4 Comments
For clarity - I'm not advocating for a refund here. I mostly want to make sure this isn't happening at scale.
The 1.1M token / 18-minute / empty result combination on 5 small files is a diagnostic pattern worth naming: the code-review workflow is likely spawning subagents that each receive the full session context, not just the diff they need to review. If the session was already at moderate depth before triggering the review, every spawned subagent inherits that context and each "reviewer" is paying for context that has nothing to do with the 5 files.
A few things worth checking in the transcript (
~/.claude/projects/**/*.jsonl) to confirm:taskoragentspawns withasync: true, each one is pulling from the same token pool simultaneously.context_usedorinput_tokensfields on the subagent tool_result entries. If they're large (relative to the 5-file diff), the subagents are receiving more context than they need.The practical workaround for now: run code-review workflows from a fresh session scoped to just the diff, rather than from inside a long-running session. It trades the convenience of one session for a much lower token floor per review pass.
Longer term this is a subagent context scoping problem - the workflow should be able to declare "reviewers only need the diff and the repo summary, not the full parent context." That would make the workflow usable without the transcript-size caveat.
Same issue here
Independent reproduction with the mechanism identified, plus token accounting that refines @kcarriedo's context-inheritance hypothesis.
Repro: a single-file diff — 28 lines changed (+28/−7) in one C# file, Unity project. Invoked
/code-review <PR#>with no explicit level. Manually stopped at ~20 minutes.Result: 14 agents, ~20 minutes, 1 confirmed finding (a simplification nit). Of the 9 verifier verdicts that completed before I killed it, 6 came back REFUTED.
Root cause — the verify fan-out is uncapped and scales with candidate line numbers, not changeset size. From the persisted workflow script:
high= 3 correctness finders + 1 cleanup finder, each asked for up to 6 candidates → up to 30 candidates.(file, line)location, with no cap on that count.maxFindings: 10caps the report, not the fan-out.So the smaller the diff, the worse the ratio. Four finders fishing for six candidates each inside 28 lines cannot collide.
Token accounting — parsed from the workflow's own
agent-*.jsonltranscripts, summed across all 14 agents:| | tokens |
|---|---|
| output | 118,600 |
| cache_creation | 1,978,219 |
| cache_read | 25,467,587 |
| uncached input | 17,955 |
Two things this shows:
cache_creation, so each agent does start with a large context floor — directionally what @kcarriedo suggested. But the dominant term iscache_readat 25.5M, and that is driven by turn count, not fan-out alone: three agents ran 91, 73 and 45 assistant turns, contributing 8.1M, 5.5M and 3.0M cache reads respectively. Every turn re-reads the full ~120k+ prefix, and there is no per-agent turn or tool-call budget.The cost model is
fan-out × per-agent context floor × turns. On a 28-line diff, none of the three terms was bounded by the size of the work.Second finding — the routing trigger is effort level, never changeset size. The workflow's own
whenToUsereads: "Launched by the /code-review skill at high, xhigh, or max effort when workflows are enabled." I passed no level, so it inherited"effortLevel": "high"from settings and silently left the session. A user who has always typedlow/mediumgets inline review for months, then one untyped invocation fans out 14 agents — with no visible difference in how they invoked it. Compounding it,"skipWorkflowUsageWarning": truewas present in my settings without my having set it by hand (cf. #78019), so the pre-launch confirmation never appeared.Suggested fixes, roughly in order of value:
min(groups, 6)or similar) — the report is capped at 10 findings anyway.perAnglecandidate budget by diff size, not by effort level alone.🤖 Generated with Claude Code