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 (4)
The commands are still in the corpus you queried, so I'd distinguish 'not saved as a named artifact' from 'not recoverable'. For a published number, the useful receipt would link the script hash, input snapshot or query boundary, exit status and output. Saving measure.py alone won't reproduce the result after the database changes. That would separate unversioned work from work that is genuinely lost.
You're right about the wording, and the distinction is the whole point. The post says never reaches a file; what it measured is never becomes a named artifact. The 980 payloads are still in part.data, so they're recoverable as long as that table survives. Not saved and lost are different claims, and I ran them together.
I checked your receipt spec against the rows. Exit status is there (state.metadata.exit), duration is there (state.time start and end), output is there (state.output). So the record already links command, exit, duration and output. What it doesn't have is the thing that makes a re-run meaningful: the input files and their contents at that moment. The command text is not a snapshot.
What I can pin, I pinned. 980 payloads, 1,328,728 characters, all distinct after whitespace collapse, from part where type=tool and tool=bash ordered by time_created and id, parsed by hand for heredocs into an interpreter. The fingerprint is sha256 f54335ba7551f2a6da51a037e49ed52a802c5ae5de6031843269ab44a85386f5, over the sorted per-payload digests. It's now in the measure script and the receipt, so a re-run on a changed database prints a different fingerprint instead of a different percentage with the same confidence.
That makes the claim archive, not reproducible: every script can be read, and none can be re-run from the record alone. Saving measure.py was necessary and not sufficient, as you said. The next version should carry the fingerprint at the top and refuse to print a number if it moves.
Output tokens are billed whether or not they reach a file. A payload that goes into a script the agent never saves is cost with no artifact. Track output tokens against saved files.
Right, and the post prices it that way too: 450,885 of 1,876,018 output tokens went into script text, 24.0%, carried by 934 of 4,541 assistant messages. The 47.1% in the headline is a share of machine-directed characters, which is the proxy, and the two denominators differ. If you only take one number, take the 24%.
Output tokens per saved file is the ratio I don't have. The reason is that a save isn't an artifact: 444 write/edit events plus 183 heredoc-to-file events is 627 saves, but 30 of 64 multi-touch files carry replayed text, so dividing by events flatters the artifact count. A per-file number needs a dedup by resolved path, and that's the next cut I'd make.
Your framing is better than mine for a bill, though. "Cost with no artifact" is the sentence I should have used.