Skip to content

The weight of standing agent instructions

Two hikers on a hill, one in the foreground with a heavy rucksack and another in the background with a smaller daypack.

One file of agent instructions grew to over 1,700 lines and cost 25,000 tokens before I'd typed a single prompt. The BLOAT, and how to fight it.

· 7 min read

Earlier this month, the file of standing instructions for this blog's AI agent was 1,734 lines long. Claude would read every one of them before I'd typed a single word. This was a problem.

The file is called CLAUDE.md. It's a plain text file that sits at the root of a project, and Claude Code loads it at the start of every session. Its job is to save you from re-explaining the project each time. House conventions go in there, along with decisions you've already settled and the idiosyncratic reasons why this has a certain colour or that has a specific font.

Sounds useful, right? Once you've been working with an agent over months rather than minutes, a set of standing instructions is absolutely essential. Claude is diligent enough to amend the file itself, so the next session (and the session after that) always starts from the latest picture.

But there's a catch. If nobody audits the file, it grows unchecked like a rampant weed, and past a certain size it starts eating into the sessions it was meant to speed up. The advice to keep standing instructions short is already well documented (opens in a new window). I hadn't read it though. I found out the hard way.

Chasing a Lighthouse score

It all began with a performance audit of this website.

PageSpeed Insights (opens in a new window) is Google's free page audit tool. It runs Lighthouse, an automated test that loads your page as though it were on a throttled mobile device and scores it for performance, accessibility, best practices and SEO.

In a test run, the site's performance score sat at orange, in the high 80s. The goal is to have all lights green, in the high 90s. The causes were easy enough to diagnose:

  • Font files preloading, regardless of whether a page needed them.

  • Every grammar for Shiki, the syntax highlighter, traced into the deployment, 260 of them, because it'd been imported whole rather than one language at a time.

  • Open Graph cards (the preview images that appear when someone shares a link) rendered at build time for every post and baked into every deployment. That came to about 19 MB of images per deploy, for pictures a link scraper fetches once.

Fixing them, however, was the slow part. Each session with Claude seemed to end sooner than the last, and I was hitting my plan's usage limit earlier than expected. The to-do list wasn't being ticked off as quickly as usual.

The cause turned out to be the same on both sides. The website was loading things visitors didn't need, up front, just in case. The AI agent was doing the same thing.

Where did the tokens go?

CLAUDE.md began on 27 May as 20 lines of notes from a Next.js audit. By 5 September it was 1,734 lines and 103,909 characters. By then the file had been touched by 98 commits, mine and Claude's, which works out at roughly 18 lines a time.

Every one of those additions was perfectly reasonable when it was made. A rule about heading sizes, for example. Or a note explaining why a cache wrapper takes no optional parameters. Something else I'd already explained to Claude twice and didn't want to explain a third time.

The problem was, nothing was ever deleted or tidied away. Notes hung around like so much digital cruft long after the code they were describing had moved on.

What made it concrete was another agent totalling the file and telling me it came to about 25,000 tokens. A token is the unit an AI model reads and writes in, whether it's a single word or a chunk of a longer one. My file worked out at roughly four characters per token.

Those tokens land in the context window, which is the model's working memory for a conversation, and they're there from the first message of every session. On my Pro plan, usage limits are shared across Claude and Claude Code (opens in a new window), so the instructions were drawing on the same allowance as the actual work.

I can't tell you exactly how much of my allowance the file consumed, because the plan doesn't itemise it. What I can tell you is that it was loaded up, in full, at the start of every session.

I'd spent a few days making the website stop loading things its visitors didn't need. Meanwhile the agent was loading a hundred kilobytes of instructions it mostly didn't need, every time I started a conversation.

Rules in one file, arguments in another

It's a horror-show of unforced errors, but there is a solution, of sorts. On 5 September I split the file in two.

One file keeps the rules. "Do not add a fifteenth dependency without saying what it does that the fourteen cannot." "Every data fetcher is wrapped in React's cache(), no exceptions." "The locale is en-GB everywhere." Short, flat, no argument attached, because an agent about to write code only needs the rule. It almost never needs the reasoning behind it.

The other file, docs/decisions.md, keeps the arguments. They moved across more or less whole rather than being rewritten, because the reasoning was fine. It just didn't need reading in full at the start of every session by an agent that had arrived to fix a typo.

Nothing imports docs/decisions.md, so nothing loads it automatically. The agent reads the rules when a session starts, and opens the arguments only when it's about to change something one of them covers, or when I ask it to audit. The cost moved from every session to the few that actually touch the reasoning.

Holding the two files together is a marker. A rule cites its argument by a stable key, [→ locale], and the argument carries <!-- key: locale --> above it. Keys rather than headings, so a heading can be reworded without breaking the link. There are 56 keys and 60 citations so far.

CLAUDE.md on the left holds three short rules, each ending in a citation marker, with arrows running to the matching keys in the much longer docs/decisions.md on the right.
Each rule cites its argument by a stable key, so the reasoning stays one search away without being loaded every session.

The agent follows a marker because the rules file tells it to. The opening lines of CLAUDE.md, above every entry, say that this file is the rules and docs/decisions.md is the arguments, that a [→ key] points at the entry explaining why, and that the arguments get read before an audit of any kind and before changing anything a rule names. There's no inference involved.

Path scoping was a no-go

Before adopting this setup, I also tried path scoping, where rules live in separate files and load only when the agent is working on the files they describe. Claude Code supports it through a .claude/rules/ folder, with a line at the top of each rule file naming the paths it applies to.

It didn't work for me. I tested four variations of that line and checked each with /context, a command that lists what's currently loaded. Rules scoped with paths: never loaded at all. Rules scoped with globs: loaded at the start of every session, ignoring the scope entirely. Either way the failure was silent.

Other people have reported the same behaviour, one bug report (opens in a new window) describing rules that load everywhere and another (opens in a new window) describing rules that never load.

Holding the line at 280

The remedy requires a second component, however, because there's nothing to stop the file ballooning to its previous size without guardrails.

So the rules file has an arbitrary budget of 280 lines, and a test fails the build if it goes over. A second test fails if a rule points at a key that was cut from the decisions file, which is the other way a split like this can begin to rot.

Both tests caught real problems on the way in. The key check was reading only the first key in each citation and ignoring the rest, so any rule citing two arguments was half unchecked. And ten keys turned out to have no rule pointing at them, arguments stranded with no way in.

Each test now ships with a deliberately broken example that it must fail on, to prove it can.

Where are we now?

The split landed the rules file at 259 lines, 21 under the ceiling. Three days later it was at 279.

What filled it was the performance work from the Lighthouse audits, arriving as new rules. Import Shiki grammars one at a time. Render Open Graph cards on demand. Replace a date-formatting library with the browser's built-in one. Keep page furniture out of the search index. Each was worth writing down, and together they used up all 21 lines of headroom in 72 hours.

Then the file sat at 279 for eight days, because the build wouldn't let it past 280.

This week I reviewed those 20 new lines and asked of each one whether it was a rule, or an argument pretending to be one. 11 were rules. Nine were justifications already written out in full in docs/decisions.md and then repeated in the file that gets read every session (which is exactly the habit the split was meant to break). Deleting them brought the file back down to 273.

Meanwhile docs/decisions.md has kept growing. At the time of writing, it's 2,260 lines and 129,692 characters, bigger than CLAUDE.md ever got. The bloat didn't disappear. It moved somewhere that costs nothing until it's called upon. That counts as a real improvement, and still isn't the same as solving it.

In the next post, I'll explain how I automate routine audits of the standing agent instructions.

The rules file now costs roughly 3,700 tokens at the start of a session, against the 25,000 that set this off. As I write this, it sits at 269 lines.

The Wanderers
gouache illustration, one hiker bent almost double climbing a grassy hill under one enormous overstuffed rucksack piled with household objects, a second hiker with a small daypack further up the ridge, bright cobalt blue sky with a few big puffy white cumulus clouds, crisp sunlit edges, bright midday light, editorial illustration, visible paper texture

About the author

Read Next