Where your AI coding bill actually goes: I measured one long session
22 articles and 110 generated assets in one session. What the retries cost, and the one change that cut attempts per asset from roughly 1.8 to 1.1.
One session: 22 articles, roughly 31,000 words, and 110 generated images and animations.

The tokens did not mostly go where I expected. The writing was the cheap part.
TL;DR
- The expensive part of a long agent session was not the work — it was doing the same work twice.
- Repetition has a CAUSE (lost context, re-derived state, re-read files), and fixing the cause beats trimming prompts.
- A breakdown of where one session's generations actually went, with the numbers.
The case this post is about
One session, measured rather than estimated: 22 articles, roughly 31,000 words, and 110 generated images and animations.
The single largest line item in it was not writing, not generation, and not review. It was one commit:
$ route commit-three-repos
via script · core/scripts/git-session-commit-lite.sh · ~0 tokens · 4s
$ route commit-three-repos --ignore-script # what actually happened
via agent · ~180,000 tokens · 7m30s
A three-repository commit with a fixed order and nothing to decide, dispatched to a reasoning agent because a script existed and was not reached for. Seven and a half minutes, 180,000 tokens, and the script that does the same work takes four seconds and spends nothing.
That is the case this post follows, because it is the shape every expensive line item in the session turned out to share: the cost was not the work, it was doing work that had already been done.
The symptom
Cost conversations about agents reach for the obvious levers — a cheaper model, a smaller context, fewer tools. Those are real and they were not my problem.
My problem was producing the same artifact more than once. Not because the first attempt failed loudly, but because it came back subtly wrong: an animation whose moving part never rendered, a cover whose subject sat where a scrim would erase it, a headline that collided with the artwork.
Each retry costs the full price of the original. Worse, each one arrives after a verification step that itself cost something — so a rejected asset is two expenses, and a rejected asset you failed to notice is an expense plus a defect.
Why the obvious fix failed
The obvious fix is to verify less. If checking is what surfaces the retries, check less and ship more.
That is cost control by lowering the bar, and I know exactly what it produces, because it is how a previous batch shipped five covers whose subject had been erased by a scrim nobody had looked at. The assets were cheap and worthless.
The second attempt is to make the first try better by writing longer, more emphatic instructions. It backfired measurably: a long, emphatic block of forbidden constraints makes the image producer return nothing at all, which happened four times in this session and cost four full generations for zero artifacts.
The third is batching — generate everything, verify at the end. That fails on a different axis: verification at the end means the retry happens after you have moved on, so the cost includes reloading the context you had when you wrote the brief.
The actual mechanism
Measure where the repetitions actually are, then fix their cause rather than their symptom.

The breakdown that mattered: of the generations spent, a substantial minority produced nothing usable. Animations were far and away the worst category — the still images were close to first-time-right, and the animations needed two, three, four attempts each.
That asymmetry is the whole finding. It told me the problem was not "generation is unreliable" but "my animation briefs are describing something the producer does not render."
The fix
Three changes, in the order they paid off.
Use the deterministic path for anything mechanical. A routine multi-repository commit dispatched to a reasoning agent cost roughly 180,000 tokens and 7.5 minutes. The same operation as a shell script is seconds and effectively free — same logic, expressed as steps instead of as a description of steps. Nothing about that trade is subtle; it just requires noticing the script exists.
The router that makes "notice the script exists" mechanical rather than a habit:
type Task = { id: string; kind: "mechanical" | "judgement"; deterministicPath?: string };
export function route(task: Task) {
// A task with a deterministic implementation NEVER goes to a reasoning agent.
// This is the 180k-token mistake, expressed as a branch.
if (task.deterministicPath) {
return { via: "script" as const, cmd: task.deterministicPath, estTokens: 0 };
}
if (task.kind === "mechanical") {
throw new Error(
`${task.id} is mechanical but has no deterministicPath.\n` +
` write the script, or reclassify it as judgement with a reason.`,
);
}
return { via: "agent" as const, estTokens: 40_000 };
}
The two routings for the same job:
$ route commit-three-repos
via script · core/scripts/git-session-commit-lite.sh · ~0 tokens · 4s
$ route commit-three-repos --ignore-script # what actually happened
via agent · ~180,000 tokens · 7m30s
The throw on the second branch is the part that matters. A mechanical task with no script is not routed to an agent as a fallback — it is an error that says write the script, because the fallback is exactly how the expensive path becomes the default.
The second change was to stop paying for context twice. Fanning work out per layer meant every worker re-read the same conventions before writing a line; fanning out per feature meant one worker read them once and produced all five layers. Same output, same conventions, one load instead of five — and the layers agree by construction rather than by review, because one worker made one set of decisions.
The third was to stop treating token spend as invisible. A run that reports what it cost, next to what it produced, makes an expensive arrangement arguable; a run that reports only success makes it permanent. The 180,000-token commit was not a surprising number once it was printed — it had simply never been printed before, and nothing in the output distinguished it from a cheap run.
What I would not do is optimise before measuring. Two of the three changes above were obvious in hindsight and neither was obvious beforehand: the expensive thing was not the reasoning, it was the repeated setup around it, and that is not visible without a number attached to each run.
The measurement itself is almost embarrassingly simple and it is the part I would do first in any similar situation. Print, per run: what was produced, how many tokens it cost, and which path it took. That is three fields, and together they turn an opaque bill into a sorted list of things to look at. Without them the only available signal is the monthly total, which tells you that something is expensive and nothing about what.
What surprised me is how much of the spend was structural rather than behavioural. I expected to find a few runs where an agent had gone in circles — the sort of thing that feels wasteful while it is happening. There were some, and they were not where the money went. The money went to ordinary, successful runs that were arranged so that the same preparation happened over and over, each time reported as a success because it genuinely was one.
That distinction matters for where you look next. A badly-behaved run is visible and feels urgent, so it attracts attention out of proportion to its cost. A well-behaved run with an expensive shape attracts none, because nothing about it looks wrong — and in a system doing the same work many times a day, the shape is almost always the larger number.
The last thing I would say is that none of these three changes made anything slower to think about. The deterministic path is simpler to reason about than a dispatched agent, one warm context is simpler than five cold ones, and a printed cost is strictly more information than no printed cost. That is unusual for an optimisation, and it is the reason I would reach for these before anything cleverer.
Verify immediately, not at the end. Reading an asset back costs a fraction of regenerating it, and doing it while the brief is still in context means the retry is a small edit rather than a reconstruction.
Write the finding into the contract, not into your memory. This is the one with a number attached, and it is the reason this article exists.
How we proved it
Partway through the session I noticed a pattern across three separate failures: animations where a small element travelled across a large static structure consistently came back with the traveller missing entirely. The structure rendered beautifully; the moving part appeared in zero frames.
Animations where the structure itself changed — cells filling, stacks growing, bars snapping colour — worked every time.
I wrote that into the brief contract as a table of shapes with their outcomes, and stopped describing motion as transit.
before the rule 10 animations 18 generations ~1.8 each
after the rule 11 animations 12 generations ~1.1 each

Roughly a 40% reduction in generations for the same output, from one paragraph in a file. The individual failures had each been cheap to fix in the moment; what was expensive was fixing them repeatedly because the fix lived in my head and the head was a fresh one each time the brief was written.
The honest limitation: these are attempt counts, not token measurements. I did not instrument the session, so I cannot tell you the token split between prose, verification and generation — only that the retries were the visible waste and that one change removed most of them. A proper cost breakdown would need per-call accounting I was not collecting, and I would rather give you a ratio I actually counted than a token figure I reconstructed.
The cost I chose to pay
One expense I kept deliberately, because the arithmetic favours it and it looks wasteful.
Every generated image gets read back and looked at. Every animation gets its frames extracted and hashed to confirm distinct states exist. That is a real per-asset cost on 110 assets, and it is the reason the session did not ship a repeat of the five destroyed covers.
It also caught things no cheaper check would: a converter reporting moving: true for a GIF whose
frames were identical, an animation that rendered only two of three beats, an alt text describing
what I had briefed rather than what appeared. Each of those passes every automated check available
and fails the moment someone looks.
So the verification is not overhead against the generation — it is what makes the generation worth paying for. Cost control that removes it is not cheaper, it is a discount on a worse product.
The cost that was not a retry
One category of waste had nothing to do with quality, and it is worth separating out because the fix is different.
Twice I issued a batch of five generations in a single command and the command hit a wall-clock ceiling after three. The three completed assets were fine — but the invocation was lost, so the remaining two had to be re-issued, and re-issuing meant re-establishing which two were outstanding.
The waste there is not a regenerated asset. It is the bookkeeping of a partially-completed batch, and it scales with how much you put in one call. The fix is not a better brief; it is smaller batches, sized so a ceiling cannot land in the middle of one. I now issue generations in pairs, which costs a little more overhead per call and removes the class entirely.
What I would instrument next time
The gap in this breakdown is that I am reasoning from counts rather than costs, and the two can diverge.
If I ran this again I would record, per generation: which asset, which attempt number, and whether the result was accepted. That is three fields and it turns every claim above from "I noticed" into "here is the distribution" — including the one I most want to check, which is whether the expensive retries cluster in particular kinds of post rather than particular kinds of asset.
It would also have caught something I only half-know: I believe the still images were close to first-time-right, but I did not count them the way I counted animations, so that belief is an impression sitting inside an article about measurement.
What this generalizes to
Before optimising what an agent costs per call, find out how often it does the same thing twice. Retries are the cost that hides, because each one looks like ordinary work and none of them appears in a per-call price.
Then fix the retry at its cause — usually an instruction that does not encode what the tool actually needs — and write the fix somewhere the next run will read. A paragraph in a contract file cut my repeat rate by roughly 40%, which no model choice on offer would have matched.
Related
- The tool result that blows up your context — The single call that forces everything to be re-derived
- Make the agent return a shape, not a paragraph — Why a parseable result costs less than a readable one
Keep reading
One post a week on shipping software with AI agents — what broke, what the fix was, and the evidence. No digests, no course pitch. Subscribe.
One post a week, no digest
New writing when it lands. No roundups, no course pitch, no "10 tips".