Problem
Two detectors treat the Bash tool inconsistently, and the result is that any workflow which does file lookup through the shell gets scored badly for doing nothing wrong.
READ_TOOL_NAMES ignores Bash entirely, so rg, grep, and cat count as zero reads. countRetries treats any Bash call as a verification step, so edit → grep → edit is recorded as rework.
The same tool is invisible where it should count and decisive where it should not. Both detectors then feed the Optimize tab and the efficiency grade.
1. read-edit-ratio does not count shell reads
src/optimize.ts:2190
export const READ_TOOL_NAMES = new Set(['Read', 'Grep', 'Glob', 'FileReadTool', 'GrepTool', 'GlobTool'])
Bash is absent, so a session that searches with rg reads nothing as far as this detector is concerned.
Measured over 7 days of local Claude transcripts (1,787 session files), counting tool_use blocks:
| Source |
Calls |
Read / Grep / Glob |
539 |
Bash, read-shaped (rg, grep, cat, head, find, git log, git diff, …) |
5,277 |
Edit / Write / NotebookEdit |
1,565 |
Ratio excluding Bash: 0.34:1. Including it: 3.72:1, against a HEALTHY_READ_EDIT_RATIO of 4.
The detector reported "Claude edits more than it reads" at high impact with trend: active. Roughly 90% of the reads were never counted.
This bites hardest on the workflows the advice is aimed at. An agent instructed to prefer rg over the Grep tool — a common convention, and faster — scores worse the more disciplined it is about looking before editing.
2. countRetries treats every Bash call as verification
src/classifier.ts:168
if (BASH_TOOLS.has(call.tool)) {
lastVerifyStep = i
}
if (EDIT_TOOLS.has(call.tool)) {
const fileKey = call.file ?? '__no_file__'
const prevStep = lastEditStep.get(fileKey)
if (prevStep !== undefined && lastVerifyStep > prevStep && lastVerifyStep < i) {
retries++
}
Any Bash call sets lastVerifyStep. So edit X → ls → edit X and edit X → pytest → edit X are scored identically, though only the second is rework. The first is a normal refinement.
Replaying this algorithm over the same 7 days and classifying the intervening command:
| Intervening Bash command |
Retries |
Share |
Real verification (pytest, vitest, tsc, eslint, npm run build, cargo test, …) |
53 |
15.7% |
Lookup only (rg, ls, cat, git log, git status, …) |
284 |
84.3% |
This number propagates. retryTax in src/usage-aggregator.ts:634 multiplies retries by average cost per edit turn, and it drove the headline "retry tax" past 190% of spend on this machine. findLowWorthCandidates (src/optimize.ts:2578) also gates on retries >= WORTH_IT_MIN_RETRIES, so inflated retries pull ordinary sessions into "possibly low-worth expensive sessions".
Suggested approach
src/bash-utils.ts already exports extractBashCommands(rawCommand): string[], which resolves wrappers — including rtk, added in #657. Both fixes can build on it rather than introduce new parsing.
- Classify each Bash call once into
read / verify / mutate / other from its extracted command names.
- Count
read toward READ_TOOL_NAMES's numerator in detectLowReadEditRatio.
- Set
lastVerifyStep only for verify in countRetries.
Step 3 changes the retry number for every existing user, so it is a judgment call rather than a clear bug fix: the question is where the line sits between verification and inspection. git diff after an edit is a reasonable check; ls is not. I would put test, build, typecheck, lint, and run commands in verify, and leave search and inspection in read, but that is the part worth settling before any code is written.
Scope of the evidence
Measured on one machine, Claude transcripts only, 7-day window, using an independent reimplementation of countRetries rather than the shipped code path. The split shows the pattern; it is not a restatement of what codeburn itself would report across all providers.
Environment
- codeburn at
8578199
- macOS 24.6.0, Node 26
- Providers present: Claude, Codex, Antigravity
Happy to send a PR for any subset of this once there is a call on the verify boundary in step 3. Per CONTRIBUTING I am checking on the approach before writing code.
Problem
Two detectors treat the
Bashtool inconsistently, and the result is that any workflow which does file lookup through the shell gets scored badly for doing nothing wrong.READ_TOOL_NAMESignores Bash entirely, sorg,grep, andcatcount as zero reads.countRetriestreats any Bash call as a verification step, soedit → grep → editis recorded as rework.The same tool is invisible where it should count and decisive where it should not. Both detectors then feed the Optimize tab and the efficiency grade.
1.
read-edit-ratiodoes not count shell readssrc/optimize.ts:2190Bashis absent, so a session that searches withrgreads nothing as far as this detector is concerned.Measured over 7 days of local Claude transcripts (1,787 session files), counting
tool_useblocks:Read/Grep/Globrg,grep,cat,head,find,git log,git diff, …)Edit/Write/NotebookEditRatio excluding Bash: 0.34:1. Including it: 3.72:1, against a
HEALTHY_READ_EDIT_RATIOof 4.The detector reported "Claude edits more than it reads" at high impact with
trend: active. Roughly 90% of the reads were never counted.This bites hardest on the workflows the advice is aimed at. An agent instructed to prefer
rgover the Grep tool — a common convention, and faster — scores worse the more disciplined it is about looking before editing.2.
countRetriestreats every Bash call as verificationsrc/classifier.ts:168Any Bash call sets
lastVerifyStep. Soedit X → ls → edit Xandedit X → pytest → edit Xare scored identically, though only the second is rework. The first is a normal refinement.Replaying this algorithm over the same 7 days and classifying the intervening command:
pytest,vitest,tsc,eslint,npm run build,cargo test, …)rg,ls,cat,git log,git status, …)This number propagates.
retryTaxinsrc/usage-aggregator.ts:634multiplies retries by average cost per edit turn, and it drove the headline "retry tax" past 190% of spend on this machine.findLowWorthCandidates(src/optimize.ts:2578) also gates onretries >= WORTH_IT_MIN_RETRIES, so inflated retries pull ordinary sessions into "possibly low-worth expensive sessions".Suggested approach
src/bash-utils.tsalready exportsextractBashCommands(rawCommand): string[], which resolves wrappers — includingrtk, added in #657. Both fixes can build on it rather than introduce new parsing.read/verify/mutate/otherfrom its extracted command names.readtowardREAD_TOOL_NAMES's numerator indetectLowReadEditRatio.lastVerifySteponly forverifyincountRetries.Step 3 changes the retry number for every existing user, so it is a judgment call rather than a clear bug fix: the question is where the line sits between verification and inspection.
git diffafter an edit is a reasonable check;lsis not. I would put test, build, typecheck, lint, and run commands inverify, and leave search and inspection inread, but that is the part worth settling before any code is written.Scope of the evidence
Measured on one machine, Claude transcripts only, 7-day window, using an independent reimplementation of
countRetriesrather than the shipped code path. The split shows the pattern; it is not a restatement of what codeburn itself would report across all providers.Environment
8578199Happy to send a PR for any subset of this once there is a call on the
verifyboundary in step 3. Per CONTRIBUTING I am checking on the approach before writing code.