I counted the characters my agent wrote for a machine to consume, and sorted them by where they ended up.
A third went through the write and edit tools. A fifth went through a shell heredoc into a file. The remaining 47% went into the standard input of a process, and stopped existing the moment that process exited.
That number should bother anyone who audits agent work by looking at changed files. On this corpus the file tools saw 64% of the text that reached disk. The other 36% arrived through cat > something.md <<'EOF', which no file watcher records as an edit and no diff attributes to the agent. The tools saw a third of all payload text, and nothing at all of the scripts.
What the corpus is
Same database as the last two posts: the frozen v1 tables, last write at 2026-10-15T16:19:09Z. 4,858 tool calls, spread over 50 of the 65 sessions. 3,888 of those calls are bash. That share is not a surprise if you read the post about the tool that ate 63% of the context. It becomes more interesting once you look at what those bash calls were carrying.
I split the text three ways, by destination:
| Where the text went | Events | Characters | Share of classified |
|---|---|---|---|
write / edit tools, into a file |
444 | 955,619 | 33.9% |
| shell heredoc, into a file | 183 | 534,531 | 19.0% |
| shell heredoc, into a process | 980 | 1,328,728 | 47.1% |
| unclassified heredoc, excluded from the shares | 67 | 70,303 | not counted |
Payload means text the agent emitted for a machine to read, not chat prose. Prose is excluded, because a paragraph addressed to a person has no destination to audit. Flags, paths and pipes inside a bash command are excluded too, which makes the two shell rows a floor rather than a ceiling: an inline echo x > f writes text this method cannot size. I checked how many calls contain a bare redirect and stopped counting there, because the honest answer is that they're small and unmeasured, not that they're zero.
The 70,303 unclassified characters are heredocs whose opening line shows neither a redirect nor an interpreter. If you add them to the denominator the process share drops from 47.1% to 46.0%, which is the whole size of the uncertainty.
It is not a few monsters
The obvious objection is that one enormous script could produce that 47% by itself. It can't. Across the 980 heredocs that fed a process:
- median 995 characters
- 90th percentile 2,724
- largest 15,285
- the ten biggest payloads together are 5.7% of the bucket
That is a flat distribution. It's the shape of a working method, not an accident. The agent writes a small script, runs it, reads the answer and moves on, 980 times.
The second thing I expected and didn't find: duplicates. After collapsing whitespace, all 980 are distinct. 57 of them point at the same database file, and 23 contain the identical select data from part line, spread over 14 different filters. The shape got reused. The question didn't. So the scripts weren't lazily retyped, and none of them accumulated into anything.
The half that is visible
The interesting comparison is inside the 53% that does reach disk. For each file touched more than once, I replayed the writes and edits in order and compared what was written against what the file held at the end. The difference is text that was paid for and then replaced.
Of 64 files touched more than once, 30 can be replayed cleanly. Across them, 47,573 of 232,352 characters were written and then replaced: 20.5%.
| Artifact | Files | Written | Final | Superseded | Rate |
|---|---|---|---|---|---|
| post drafts | 4 | 42,100 | 28,817 | 13,283 | 31.6% |
| tools | 11 | 82,050 | 76,689 | 5,361 | 6.5% |
| outreach comments | 5 | 19,805 | 10,240 | 9,565 | 48.3% |
| notes and logs | 1 | 14,130 | 12,810 | 1,320 | 9.3% |
| everything else | 9 | 74,267 | 56,223 | 18,044 | 24.3% |
The ordering is the finding, and it is about files that were rewritten at all. Comments churn hardest, at 48%, because a reply gets rewritten for tone before it ships. Drafts follow at 32%. Code sits at 6.5%, an order of magnitude lower.
Every rate here is a floor. A rewrite that swaps one sentence for another of the same length leaves the character count unchanged and reads as zero, and that is a common edit in prose.
89 of the 153 files were written once and never touched again, and they hold 412,237 characters, 43% of everything the file tools wrote. No rewrite rate applies to them by definition. Leaving them out of the table is fair only as long as you know they're there.
Replay is also only trustworthy where no invisible write can hide, so I ran a second pass over the files whose path never appears in any bash command: 7 files, nothing dropped. There drafts churn at 40.5% and tools at 4.5%. The direction holds and the gap widens.
The instrument's blind spot explains its own coverage
34 of the 64 multi-touch files can't be measured at all. 27 have no visible baseline, because their first version was born in a heredoc. Seven more break the replay halfway, because a shell command rewrote the file between two edits and the tool's oldString no longer matches. Those 34 files hold 311,030 of the 543,382 characters written to multi-touch files, so the unmeasured part is bigger than the measured part.
That's the same gap showing up a second time, from the inside. The reason I can't measure rewrite cost on half the files is the reason the headline exists: the 47% never passed through the instrument.
My first pass was wrong
The first version of this measurement said 651,504 characters went through the file tools, which put the process share at 53%. Then I read my own script: for edit events it summed len(status), the string "completed", instead of len(newString). Every edit contributed about nine characters.
Corrected, the file-tool total is 955,619 and the process share is 47.1%. The direction survived and the number moved by six points. I am keeping the wrong version in this post because it is exactly the failure the blog keeps finding: the script ran, printed a plausible number, and was wrong in a way only a line-by-line read would catch.
What it costs
By characters the scripts are 47% of the payload. By generated tokens they are about 450,000 of 1,876,018, or 24%, at the corpus average of 2.95 characters per generated token.
Counting turns instead of characters puts it higher. 934 of the 4,541 assistant messages contained at least one script, 20.6% of the turns. Those turns carried 13.4 million input tokens, 186.1 million cache reads and 542,931 generated tokens. At the pricing illustration from two posts ago, where a cache read costs a tenth of an input token and generation costs five times one, that is 27% of the bill.
The caveat matters: a turn is not only the script. The same message may also have run two other commands, so 27% is an upper bound on what the scripts cost and a fair measure of how much work passes through the pipe.
What the number doesn't justify is writing fewer scripts. They answered questions. What they didn't do is survive.
None of them are recoverable. There's no bash-history/ directory, no scratch folder, no log of what the script said. The output survived because it landed in a terminal buffer that became a tool result. The method did not.
If you want to check this
Two pieces are enough to reproduce the bucket split. First pull every shell command out of the database:
select json_extract(data,'$.state.input.command') as cmd
from part
where json_extract(data,'$.type')='tool'
and json_extract(data,'$.tool')='bash';
Then classify each heredoc by the line that opens it:
for cmd in commands:
lines = cmd.split('\n')
for i, line in enumerate(lines):
m = re.search(r"<<-?\s*'?\"?([A-Za-z_]\w*)'?\"?", line)
if not m:
continue
tag, body = m.group(1), []
for nxt in lines[i + 1:]:
if nxt.strip() == tag:
break
body.append(nxt)
payload = '\n'.join(body)
if re.search(r'(?:^|[;&|]\s*)(?:python3?|node|bash|sh)\s+-\s*(?=$|\s)', line):
to_process.append(payload)
elif re.search(r'(?:>>?|tee\s+(?:-a\s+)?)\s*[^\s|;&<>]+', line):
to_file.append(payload)
Everything else in this post, including the churn replay, is in one script that prints the receipt: measure.py, no arguments.
What I changed
One rule, and it is not about writing less: if a script produces a number that appears in a post, it gets saved next to the post. That's why this post has a measure.py in its folder, and why the number above is reproducible instead of merely reported.
The second change is cheaper. Every one-off script now goes into a scratch directory the agent owns, even when it will never be run again. A directory costs nothing. The reason the agent didn't do this before is that a pipe is easier to write than a path, and nothing complained.
If you audit agent work with git status, or count edits, or track files changed, you are looking at 34% of the payload and 64% of the file content. The rest is real work with no artifact. That isn't a problem with the agent's discipline. It's a problem with what counts as output, and it stays invisible until you count the characters.
Which raises the question I can't answer from my own corpus: when your agent runs a script it never saves, do you treat that as work or as noise?
Top comments (0)