I have the largest Claude subscription you can buy. For months it was more than I needed. Then one week my projects started burning through it fast enough that I noticed, and I could not work out why.
So I did the sensible thing and turned on API credits as a fallback, so work would keep running past the subscription limit instead of stopping dead. Those credits lasted about half an hour.
That got my attention. I turned everything off and went looking.
What I found was not a runaway process or a bug. It was one automated loop, doing exactly what I told it to do, wired in a way that made it roughly twenty times more expensive than the same work done differently. And the thing I had been suspicious of the whole time turned out to be half a percent of the problem.
What the loop does
Claude Code has a /loop feature. You give it a task and an interval and it re-runs that task on a schedule.
I built a job called watch-fleet. Once an interval, it picks one error out of our monitoring system, reproduces it, works out which of eight sibling storefronts it affects, writes a fix and a test for each one that is affected, and opens a pull request per repository. Then it stops until the next tick.
It works. That is the annoying part. There was no failure to point at.
The detail that turns out to matter: every pass is self-contained. Pass 30 does not need to know what pass 1 fixed. They are different bugs in different files. The job's own instructions say as much. It handles one issue across every repo where that bug actually reproduces, and then it is done.
Two wrong turns before the right one
My first theory was the code reviews.
I have a standing rule that every pull request gets reviewed by a fresh agent before it merges, and those reviewers are not cheap by design. They run the test suite. They deliberately break each safety check to confirm a test actually catches it. They go hunting for counterexamples to whatever the pull request claims. When I went looking for something expensive, that is the thing I expected to find, because it is the thing that visibly does a lot of work.
Before that, there was a dumber wrong turn. My first measurement came back listing three projects I did not recognize. The tool had read the default config directory, and this work runs under a separate account with its own directory. I had measured a real day of real work. It was just the wrong half of my own life.
Both mistakes point at the same thing, which is really what this post is about: intuition about where AI spend goes is unreliable, and the actual data is sitting on your disk.
The numbers
One day, one account, ranked by tokens read from cache:
| What | Tokens | Share |
|---|---|---|
The watch-fleet loop session | 955,499,010 | 62% |
| That loop's helper agents (about 15 of them) | ~160,000,000 | ~10% |
| My interactive session, a full day of hands-on work | 56,694,098 | 3.7% |
| The code reviewer I suspected | 7,040,142 | 0.5% |
The reviews were half a percent. The loop was sixty-two.
The loop session itself:
- Ran about 16 hours, across a day boundary
- 30 iterations, all inside one conversation
- 1,884 turns
- One context compaction in the entire run
- Context per turn: median 474,000 tokens, 90th percentile 829,000, peak 999,832
That last number is the whole story in one line. The ceiling is one million. The loop finished its run pressed flat against it.
It was not just costing me money. It had run out of room to think.
Why it happens
/loop re-enters the same session. That is the mechanism, and it is not a bug. For plenty of tasks, continuity is exactly what you want. A loop iterating toward a goal should remember what it already tried, or it will try it again.
But there are two kinds of repeating work, and they want opposite things:
Iterative. Each pass builds on the last. Refining a design, converging on a fix, narrowing down a flaky test. The memory is the point.
Independent. Each pass starts clean. Polling a queue, scanning for new errors, checking a dashboard. The memory is pure overhead.
watch-fleet is emphatically the second kind, and I had it running on a mechanism built for the first. Every iteration inherited the full context of all the ones before it, and paid to re-read that context on every single turn.
The multiplier is roughly the average context divided by the context a fresh run would actually need. For me that was 474,000 against maybe 20,000 to 50,000. Ten to twenty-five times more expensive than the same work done clean.
Worth noting what did behave correctly: the helper agents. Each one got its own context, did its job, and disappeared, at something like 10 to 23 million tokens apiece. That is the shape the parent should have had.
What actually fixes it
The obvious fix would be for the loop to clear its own memory between passes. It cannot. Clearing a conversation is something a person types; the model has no way to call it on itself. There is no fresh-context option on the loop feature either.
And everything that keeps a session open has the same problem. A loop on a fixed interval, a loop that paces itself, an in-session scheduler. All the same trap.
What does start clean, every time, is anything that launches a new process:
- A cron job invoking the agent in headless mode
- A scheduled task on the desktop
- A cloud-hosted scheduled run
For my case that is a one-line crontab entry running the agent headless, once an hour. Each invocation is a brand new process with an empty context. The flags to avoid are the ones that resume a previous conversation, because those put you right back where you started.
There is a middle option if you want to stay in an interactive session: have each iteration hand the actual work to a sub-agent, so the heavy context lives and dies in the child and the parent only keeps a short summary. The parent still grows, but it grows by kilobytes per pass instead of half a megabyte.
The part I have not solved
I still want loops.
The interactive loop is genuinely good. I can watch it work. I can interrupt it, redirect it mid-flight, answer a question it stops to ask me. A headless cron job gives me none of that, and it cannot answer a permission prompt either, so anything needing approval just stalls silently at three in the morning and I find out at breakfast.
So the honest position is: I know why it was expensive, I know the mechanism that avoids it, and avoiding it costs me the interactivity that made the feature worth using. What I want is a loop that resets its own context between iterations while staying in the room with me. That does not exist yet.
One more thing worth checking
While I was in there I found a second problem that had nothing to do with context.
I had set the loop to fire every 10 minutes. A single pass, triage through to eight pull requests, takes closer to an hour. So the interval was not controlling anything at all. The loop was simply restarting the moment it finished.
An interval shorter than the work is not a schedule. It is a while-loop with extra steps. Worth checking yours.
Caveats, because they matter
These are transcript counts, not a bill. They come from the agent's own local log files. Providers weight different token types differently, and cached reads are much cheaper per token than freshly generated ones. Use these for relative comparison between activities, not to work out a dollar figure.
The lopsidedness is what carries the argument. 955 million against 7 million survives any reasonable weighting. A three-to-two ratio would not have.
The limit I hit is a rolling multi-hour session limit, not a daily cap. That matters for the shape of the story: one unattended overnight run can eat a window that a full day of hands-on work barely touches.
And this is one account, one workload, one tool. I would expect the pattern to generalize and the numbers not to.
The takeaway
I wrote a while ago about measuring where my AI budget actually goes, and the lesson then was that I was wrong about my own usage in four separate ways. This is the fifth, and it is the most expensive one so far.
If you take one practical thing from this: before you put an agent on a schedule, ask whether the task needs to remember the last run. If it does not, do not give it a memory. You will pay for it on every turn, of every pass, for as long as the session stays open.