Context Isn't Free
Writing your project's rules once with AGENTS.md or CLAUDE.md, delegating the noisy work to subagents, and understanding which part of the token bill each decision pays. With copy-paste templates.
There's an awkward moment that keeps repeating: you open a fresh session with the agent, ask for something, and it hands you back exactly the mistake you corrected yesterday.
It didn't forget. It never knew. Every Claude Code session starts with an empty context window — what you explained last week didn't travel anywhere.
And there are two distinct jobs in there that most people fold into one. The first is writing the rules down once so nobody has to repeat them. The second is splitting the work so one noisy task doesn't eat your whole session.
Both are paid for in tokens. That's the third part of this post, and it's the one almost nobody looks at until the limit warning shows up.
#Part one: the file that always gets read
Two mechanisms carry knowledge across sessions. One you write, one the agent writes itself. I'm interested in the first.
A CLAUDE.md is a markdown file of persistent instructions, loaded at the start of every session. The possible locations, in load order from broadest to most specific:
| Scope | Where it goes | What for |
|---|---|---|
| Managed policy | /etc/claude-code/CLAUDE.md on Linux and WSL, with equivalents on macOS and Windows |
Rules your organization sets |
| User | ~/.claude/CLAUDE.md |
Your preferences, across all your projects |
| Project | ./CLAUDE.md or ./.claude/CLAUDE.md |
What the team shares through version control |
| Local | ./CLAUDE.local.md |
Your own, in this project — goes in .gitignore |
One detail that changes how you organize it: the files don't override each other, they're concatenated. They're ordered from the filesystem root down to your working directory, so the instructions closest to where you launched the session are read last. And within each directory, CLAUDE.local.md is appended after CLAUDE.md.
A CLAUDE.md in a subdirectory below your working directory doesn't load at startup: it comes in when the agent reads a file there. In a monorepo that matters quite a bit.
If you're starting from nothing, /init generates a first one by analyzing the repo. If one already exists, it suggests improvements instead of overwriting it.
#AGENTS.md: one file for every agent
If you were already maintaining an AGENTS.md for other tools, until recently you had to duplicate it or import it. That changed in version 2.1.277, and the changelog says it in one line:
Added AGENTS.md support: in a project with no CLAUDE.md, Claude Code reads AGENTS.md instead; change it under 'Project instructions' in
/config
The condition lives in that sentence and it's worth reading carefully, because it's the number one cause of "I have the file and it isn't reading it":
| What's in the repo | What Claude reads |
|---|---|
An AGENTS.md, and no CLAUDE.md or CLAUDE.local.md in your working directory or above it |
Your AGENTS.md |
An AGENTS.md plus a CLAUDE.md or CLAUDE.local.md in your directory or above |
Your CLAUDE.md files only |
A CLAUDE.md that already imports the AGENTS.md |
Your CLAUDE.md, with AGENTS.md included through the import |
Your ~/.claude/CLAUDE.md, your organization's managed CLAUDE.md, and the files in .claude/rules/ don't count for that check: they keep loading alongside AGENTS.md.
Two practical consequences of that table:
- If you add a
CLAUDE.local.mdwith your personal notes to a project that relies onAGENTS.md, you stop reading theAGENTS.md. To keep both, set Project instructions toclaude-md-and-agents-mdin/config. - An
AGENTS.mdread directly doesn't appear in/memoryor in the Memory files list in/context. To confirm it loaded, look in the conversation for a line likeno CLAUDE.md found; AGENTS.md loaded: /home/you/repo/AGENTS.md.
And if your session is one that can't read it directly — on Amazon Bedrock, say, or with telemetry disabled — the answer is still a CLAUDE.md next to it holding a single line:
@AGENTS.md
That import never makes the file get read twice, so leaving it in place costs nothing. Below the import you can add whatever is Claude-specific.
Three files that are not read, so you don't use them expecting them to work: AGENTS.local.md, AGENTS.override.md, and anything inside an .agents/ directory.
#How to write one that actually gets followed
Something worth saying plainly here: this is context, not configuration. It isn't a rules file the runtime enforces. If you want to block an action no matter what, that's a hook, not a line of markdown.
Which means how you write it changes how much of it gets followed. What's documented:
Size: aim for under 200 lines per file. Longer consumes more context and reduces adherence. And don't fool yourself by splitting it into imports "to make it lighter": imported files are expanded and enter context anyway, at launch. They help organization, not cost.
Specificity: concrete instructions you can verify.
| ❌ | ✅ |
|---|---|
| "Format code properly" | "Use 2-space indentation" |
| "Test your changes" | "Run npm test before committing" |
| "Keep files organized" | "API handlers live in src/api/handlers/" |
Consistency: if two rules contradict each other, the agent may pick either one. That includes rules in nested CLAUDE.md files and in .claude/rules/. Worth reviewing occasionally and removing whatever went stale.
And what doesn't belong there: if an entry is a multi-step procedure, or only matters for one part of the codebase, it isn't content for this file. Procedures go into a skill, which loads when it's invoked. Things that apply to certain files go into a rule with paths: in its frontmatter, which loads only when the agent touches something matching. The difference isn't cosmetic: what sits here is paid for in every session, including the ones it has nothing to do with.
The criterion for deciding what to add is fairly simple, and it's the same one you'd use with someone new on the team: write it down when the agent makes the same mistake a second time, when a code review catches something it should have known about the project, or when you catch yourself typing the same correction you typed last session.
One nice detail to close on: block-level HTML comments (<!-- like this -->) are stripped before the content is injected. You can leave notes for the humans maintaining the file without spending tokens on them.
#Template: AGENTS.md
This is a starting point, not a sacred mold. It's written for a .NET backend, but the structure applies to any stack: commands, layout, conventions, and what's off limits.
# AGENTS.md
Instructions for AI agents working in this repository.
Humans: see `README.md` too.
## Stack
- .NET 8, ASP.NET Core Web API
- EF Core 8 on SQL Server
- xUnit + FluentAssertions for tests
## Commands
- Build: `dotnet build`
- Tests: `dotnet test`
- A single test: `dotnet test --filter FullyQualifiedName~TestName`
- Migrations: `dotnet ef migrations add <Name> -p src/Infra -s src/Api`
Run `dotnet test` before calling any change done.
## Structure
- `src/Api/` — controllers and composition (DI, middleware)
- `src/Application/` — use cases, one handler per operation
- `src/Domain/` — entities and rules, no dependencies on infrastructure
- `src/Infra/` — EF Core, HTTP clients, integrations
- `tests/` — mirrors the structure of `src/`
## Conventions
- Nullable reference types enabled; don't add `!` to silence the compiler
- Async all the way: every method doing IO returns `Task` and takes a `CancellationToken`
- One public type per file
- Request and response DTOs are `record`s, and domain entities are never exposed through the API
- User-facing error messages are localized; logs stay in English
## Don't
- Don't add a new dependency without stating it in the change summary
- Don't touch anything under `src/Infra/Migrations/`, it's generated
- Don't put business logic in controllers
- Don't `git push --force` to shared branches
## Git
- Branches: `feature/<short-description>`
- Commits in the imperative, one line, no emoji prefixes
<!-- Note for the team: long procedures live in .claude/skills/, not here -->
If your project also has a CLAUDE.md, or you work in sessions that can't read the AGENTS.md directly, the file next to it is one line long:
@AGENTS.md
## Claude-specific notes
- Before touching `src/Application/`, read the closest existing handler and follow its shape
#Part two: subagents
A subagent is a specialized assistant that runs in its own context window, with its own system prompt, its own tools and its own permissions. It works on a delegated task and returns a summary to the main conversation.
That's the whole point. The task that was going to flood your session with grep results, logs and file contents you'll never look at again happens in its context, and what comes back to you is the summary.
What a subagent does see at startup: its own system prompt, the delegation message the main agent writes for it, and the CLAUDE.md files from the whole hierarchy. What it doesn't see: the conversation history, the files the main agent already read, or skills that were already invoked. It starts clean.
Files go in one of two directories, and the more specific one wins:
| Location | Scope |
|---|---|
.claude/agents/ |
This project — check it into version control |
~/.claude/agents/ |
All your projects |
The directories are scanned recursively, so you can organize them into subfolders.
#The frontmatter
Two required fields and a long list of optional ones. The four that matter at the start:
| Field | Required | What it does |
|---|---|---|
name |
Yes | Unique identifier, lowercase with hyphens. Cannot contain : |
description |
Yes | When the main agent should delegate to it |
tools |
No | The tools it can use. Omit it and it inherits every tool available to subagents |
model |
No | sonnet, opus, haiku, fable, a full ID like claude-opus-5, or inherit |
On description: automatic delegation is decided from your request, this field, and the current context. Putting "use proactively" in it pushes delegation to happen without you asking. And keep it brief: when all the descriptions combined pass 15,000 tokens, a warning fires.
On tools: the names are the canonical tool names — Read, Grep, Glob, Bash, Edit, Write, WebFetch, WebSearch, TodoWrite, Agent, Skill. Watch this one, it's a classic: the tool for spawning subagents is called Agent, not Task. And if the list ends up empty or resolves to nothing, the subagent usually won't launch and returns an error naming the entries it couldn't resolve.
One thing worth being clear on before writing the body of the file: the subagent's system prompt replaces Claude Code's entirely. It isn't an addition. Everything the subagent needs to know about how to behave has to be in there.
#How to invoke it
Three levels, from least to most deterministic:
# 1. Natural language — the agent decides whether to delegate
"Use the test-runner subagent to fix the failing tests"
# 2. Mention — guarantees it runs for that task
@agent-csharp-reviewer look at the auth changes
# 3. Session default
claude --agent csharp-reviewer
#The three I use
You don't need twenty. These three cover almost everything, and each solves a different problem: reviewing, searching, and running something noisy.
#1. The reviewer
Read-only on purpose. If it can't write, it won't "fix" things on its own while reviewing.
Goes in .claude/agents/csharp-reviewer.md:
---
name: csharp-reviewer
description: Reviews C# changes against the project's conventions and looks for bugs, performance problems and security risks. Use proactively after writing or modifying code.
tools: Read, Grep, Glob
model: sonnet
---
You are a C# code reviewer. You work on the changes you're pointed at,
without modifying files.
For each finding, return three things in this order:
1. What's wrong and why it matters (one or two sentences)
2. The current code, with its path and line number
3. The corrected version
Review, in this order of priority:
- Correctness: unhandled nulls, off-by-one, inverted conditions
- Async: async methods without a `CancellationToken`, `.Result` or `.Wait()`
blocking, `async void`
- EF Core: queries inside loops, `Include` pulling more than needed,
domain entities exposed in API responses
- Security: SQL concatenation, hardcoded secrets, user data in logs
- Project conventions, per the instructions you already have loaded
Order findings most severe first. If you find nothing, say so in one line
and don't pad the report with style observations.
#2. The scanner
This one exists for a very concrete reason: searching a large repo produces a lot of output you'll never read again. haiku is more than enough here, and it's the cheapest option.
Goes in .claude/agents/debt-scanner.md:
---
name: debt-scanner
description: Finds technical-debt markers across the repository and returns a grouped inventory. Use it when a survey is needed before planning.
tools: Read, Grep, Glob
model: haiku
---
You are a technical-debt surveyor. Your job is to find and inventory,
not to judge or fix.
Look for:
- `TODO`, `FIXME`, `HACK`, `XXX` and `WORKAROUND` comments
- `try/catch` blocks that swallow the exception without logging
- Tests marked as ignored or skipped
- Hardcoded values that belong in configuration: URLs, connection strings,
timeouts, absolute paths
- Methods longer than 80 lines
- Dependencies pinned to an old version
Return a table grouped by category, with path and line, ordered by number
of findings. At the end, add the three things that repeat most.
Don't paste long blocks of code: one line of context per finding is enough.
Don't propose fixes unless you're asked for them.
#3. The test runner
This is the textbook case for saving context. Running the full suite spits out hundreds of lines; what you need are the ones that failed.
Goes in .claude/agents/test-runner.md:
---
name: test-runner
description: Runs the test suite and reports only what failed, with the error message and the file. Use proactively after changes that could break tests.
tools: Bash, Read, Grep, Glob
model: sonnet
---
You are the one who runs the tests and reports the result. You don't fix
anything unless you're explicitly asked to.
Run the suite with the project's command. If you don't know which it is,
find it in the repository instructions before improvising one.
Return this and nothing else:
1. A one-line summary: how many passed, how many failed, how long it took
2. For each failing test: the name, the error message, and the stack trace
line pointing at project code (not framework code)
3. If several fail from the same cause, group them and say so
Don't paste the runner's full output. Don't include passing tests. If the
build fails before the tests run, report only the build errors.
#Part three: the bill
Everything above is paid for in tokens, and it's worth knowing which part.
The base mechanism is this: Claude Code sends your full conversation with every request, and each time it uses tools it sends another request carrying that batch of results. With prompt caching, that history is re-read at the cached token rate — but it is re-read. A one-line question in a session that's been open all day still drags the whole conversation along.
Two costs come out of that:
The fixed startup cost. Your CLAUDE.md or AGENTS.md enters context in every session, whether or not it relates to what you're about to do. A 600-line file holding the full migration procedure is also paid for on the day you only change a colour in the CSS. That's why the 200-line limit isn't an aesthetic suggestion, and why moving procedures into skills — which load when invoked — lowers the floor of every session you run.
The variable cost of delegating. Each subagent has its own context window. Delegating isn't free: it trades verbose output in your context for a separate conversation with its own startup. It's worth it when what you delegate generates a lot of noise and returns little — tests, logs, searches, documentation — and it isn't worth it when the task needs the whole thread of the conversation, because the subagent specifically doesn't have it and you'd have to rebuild it for them.
#How to look at the bill
/contextshows current context usage as a grid, with suggestions about what's taking up room./usageshows the session's tokens and estimated cost./costis an alias for/usage, not a separate command.- On Pro, Max, Team and Enterprise plans,
/usagealso breaks recent consumption down by skill, subagent, plugin and individual MCP server, each as a percentage of the total. That's where you find out whether the subagent you built to save context is actually eating the session.
One documented figure, and it's worth being precise about because it's easy to cite wrong: agent teams — which are not subagents, but several coordinated Claude Code instances, each with its own context window — use approximately 7x more tokens than a standard session when the teammates run in plan mode. They're disabled by default. That isn't the subagent number; it's the ceiling of where this can go if you scale up the count of parallel agents.
#The levers that actually move
In order of return on effort:
/clearbetween unrelated tasks. Stale context is paid for on every subsequent message. Use/renamefirst and you'll find the session again with/resume.- The right model for each job. Sonnet handles most of the work well and costs less than Opus. For simple subagent tasks,
model: haiku. - Delegate the verbose stuff. Tests, documentation, log processing. The long output stays in the subagent's context.
- Take out of
CLAUDE.mdwhatever isn't always needed. Into a skill if it's a procedure, into.claude/rules/withpaths:if it only applies to certain files. - Specific requests. "Add validation to the login function in
auth.ts" instead of "improve this codebase", which triggers a broad scan. - Plan mode before anything complex. It costs one exploration pass and saves the entire rework of having started in the wrong direction.
And one detail that explains odd jumps in consumption: your first message after a break longer than the cache lifetime reprocesses your full context. On a subscription that lifetime is an hour; with usage credits it drops to five minutes, the same as the default on an API key or a cloud provider.
#What's left
The three parts are the same idea seen from three sides.
The AGENTS.md is what you don't want to explain again. The subagent is what you don't want in your context. And tokens are the unit those two decisions get measured in.
What doesn't change is that it's still your judgement getting written into those files. The agent won't infer it, and if it isn't written down, it isn't there.
#ai #claude-code #tooling #team-practices