Your CLAUDE.md Is Probably a Changelog: Agent Instruction Files That Scale
I opened a project's CLAUDE.md a few months into the work and read it the way the agent does: top to bottom, every word, as if all of it were true right now. It wasn't. Most of the file was a diary. Session after session had appended what it closed, what it fixed and what it decided, and the actual rules were buried somewhere in the middle, between a test count that was three refactors out of date and a decision I had reversed weeks before. The agent was obeying that reversed decision. Not because it was careless, but because I had written it down and never taken it back. That was the day I started treating CLAUDE.md best practices as a docs architecture problem instead of a writing problem.
This post is the recipe I use now. It applies to any repo-level instruction file an agent loads on every session: CLAUDE.md for Claude Code, AGENTS.md for Codex and the tools following the AGENTS.md convention, or .cursor/rules in Cursor. The names differ. The failure mode is identical.
The Symptom: Agents Following Rules You Reversed
Instruction files grow by accretion. Nobody sits down and decides "this file should be 70% history." It happens one helpful sentence at a time:
- "Fixed the auth redirect loop, see commit abc123."
- "We now use the queue for emails (decided after the timeout issue)."
- "Tests: 214 passing."
- "Migrated from X to Y, the old adapter is gone."
Each line made sense the day it was written. Together they produce a file where the agent can't tell a standing rule from a historical note. And the agent can't ask. It loads the file at the start of every session, in Claude Code's memory system that means before it has read a single line of your code, and it treats every sentence with the same weight.
That's why this file matters more than any other doc in the repo. A stale design doc gets read once in a while. A stale instruction file gets read every single session, by every agent, and it costs context tokens every time. It's the highest-leverage, most-read piece of documentation you have, and most of us maintain it with less care than a README.
Three concrete failures show up again and again:
- Zombie decisions. A choice that was later reversed still reads as current, so the agent reintroduces the thing you removed.
- Contradicting numbers. Two sections state different counts of the same thing. The agent picks one, and you can't predict which.
- Lost rules. The real constraint ("never call the payment API from a client component") is a subordinate clause inside a paragraph about a bug fix, so it gets the same attention as the bug fix.
Audit Your Instruction File: CLAUDE.md Best Practices Start With Measuring
Before restructuring anything, I classify every section of the file into one of four buckets. You can do it with a highlighter, or ask the agent to do a first pass and then check its work.
| Bucket | What it is | Example | Belongs in the instruction file? |
|---|---|---|---|
| Rule | A standing constraint or convention that applies to future work | "All DB access goes through the repository layer" | Yes |
| Map | Where things live and where to look next | "Specs live in docs/specs/, decisions in docs/adr/" | Yes, briefly |
| Status | A fact about the current state that changes over time | "Tests: 214 passing", "Phase 3 in progress" | Only if generated |
| History | What happened, when, and why | "Fixed X on Tuesday after Y broke" | No |
Then look at the ratio. There's no magic number, but a healthy instruction file is mostly rules and map, with little or no hand-written status and essentially zero history. If you find that history is the biggest bucket, you have a changelog that happens to be loaded into every prompt.
To make that concrete with an illustrative example (not a measurement of any real repo): say a 400-line file breaks down as 40 lines of rules, 30 of map, 30 of status and 300 of history. After the refactor, the rules and map survive almost intact, status becomes a short generated block, and the history moves out. You end up with something under a hundred lines that says more, because the rules are no longer drowned.
History Goes Out: Dated Changelog Notes
The history bucket doesn't get deleted. It gets moved to where history belongs: dated notes in a changelog folder, one per session or per meaningful change.
docs/changelog/
2026-05-12-auth-redirect-fix.md
2026-05-19-email-queue-migration.md
2026-06-02-remove-legacy-adapter.md
The agent can still find them when it needs context on why something is the way it is. It just doesn't pay for them on every turn.
The important step is the one people skip: read each history entry and extract the rules hiding inside it. "We now use the queue for emails, decided after the timeout issue" contains a rule ("outbound email goes through the queue, never inline") and a story (the timeout). The rule gets promoted to the instruction file or to a method file. The story goes to the changelog note. If you move history out wholesale without this pass, you lose rules you didn't know you had.
Decisions as a Graph: Splitting the Decision Log Into ADRs
A lot of repos that use agents end up with a single DECISIONS.md that grows the same way the instruction file did. It's better than nothing, but it has a fatal flaw: when decision 47 reverses decision 12, decision 12 still sits there in the same file reading like current policy, unless someone remembered to go back and edit it.
The fix is the old, boring, proven one: Architecture Decision Records, one file per decision, ideally following a lightweight template like MADR. I've written about the ADR format I use for agents and how it pairs with session handoffs, so here I'll focus on the part that's specific to splitting an existing log: supersession.
Each ADR gets a small frontmatter block. This is a generic sketch, not a schema from any particular tool:
---
id: ADR-0031
status: accepted # proposed | accepted | superseded | rejected
date: 2026-06-02
supersedes: [ADR-0012]
---
Now decisions form a graph instead of a list:
Two rules make this graph trustworthy.
Write the edge in one direction only, and derive the inverse. The newer ADR declares supersedes. The older ADR's superseded_by and its status: superseded are computed by a script, never typed by hand. If humans or agents maintain both directions, they will drift. I've seen it happen: someone writes "this replaces the approach in 0012" in prose, someone else reads "replaces" the wrong way round, and you end up with an older decision claiming to supersede a newer one. A time-inverted edge like that is easy to catch mechanically (a decision can't supersede one dated after it) and almost impossible to catch by eye.
The agent reads only the leaves. The instruction file tells the agent: "Accepted ADRs are binding. Superseded ADRs are history; never act on them." Now a reversed decision can't come back to life, because its status says so and the link points to what replaced it.
When you split an existing log, expect the supersession pass to take longer than the split itself. Many old entries reverse earlier ones implicitly ("we went back to..."), and those are exactly the edges that matter.
Never Hand-Write a Number: Generated Status Blocks
Stale counts are worse than no counts. "No test count" makes the agent run the tests. "214 passing" when the real number is 190 makes the agent believe something false, and it'll happily cite it in a PR description.
So my rule is: any number in an instruction file is generated, or it isn't there. Status lives in a fenced block that a script owns:
<!-- status:begin (generated, do not edit) -->
- Open specs: 4 · Accepted ADRs: 38 · Superseded: 9
- Last generated: 2026-08-19
<!-- status:end -->
The figures above are placeholders. The point is the shape: clear markers, a "do not edit" warning that the agent will respect, and a regeneration step that runs in a pre-commit hook or CI. If the script can't compute a number, the number doesn't belong in the file. "Phase 3 in progress" is the same problem in prose form; derive it from the spec statuses or drop it.
Give Agents a Map: Index, Links and a Method File
Once history and status are out, what remains is small. The next problem is discovery. Without guidance, agents find docs by grepping, which works until two notes use different words for the same thing.
I give the agent three things instead.
An index note. One file that lists every doc area with a one-line description: specs, ADRs, changelog, architecture notes, runbooks. The instruction file links to the index rather than duplicating it. This is the same progressive disclosure idea that makes Claude Code skills efficient: load a short pointer always, load the full content only when it's relevant.
Links between notes. Whether you use plain Markdown links or Obsidian-style [[wikilinks]], every ADR links to the specs it affects and every spec links to the ADRs it depends on. The agent follows edges instead of guessing keywords.
A method file. A short document that sets the rules for how agents read and write docs. Mine covers things like: where a new decision goes, when to open a changelog note, that status is never hand-edited, that a superseded ADR is never modified except by the generator. The instruction file says "follow docs/METHOD.md when writing docs," and the method file carries the detail.
Here's the layout I converge on:
If you work spec-driven, the specs folder is already there, and this structure slots around it. My public spec-dev-ralph repo uses the same principle of keeping the agent's always-loaded instructions short and pushing detail into files it reads on demand. Claude Code also supports nested CLAUDE.md files in subdirectories and @path imports, which are useful for the same reason: scope rules to where they apply instead of piling everything into the root.
Where Does This Sentence Belong?
When I'm unsure, I run each sentence through this table:
| If the sentence... | It goes in |
|---|---|
| Constrains all future work in the repo | Instruction file (rules) |
| Constrains work in one package or folder | Nested instruction file in that folder |
| Explains how to write or organize docs | Method file |
| Records a choice between alternatives, with a reason | New ADR |
| Reverses an earlier choice | New ADR with supersedes |
| Describes what happened in a session | Dated changelog note |
| States a count, phase or percentage | Generated status block, or nowhere |
| Explains where something lives | Index, with a one-line pointer in the instruction file |
Make the Refactor Safe: Reversible, Verified, Linted
Restructuring the docs an agent depends on is itself risky. If you lose a rule in the move, the agent's behavior changes and you may not notice for weeks. Three safeguards:
Make the split reversible. When you break a monolithic file into pieces, do it with a script that records where each block went, so you can reassemble the original exactly. If concatenating the pieces in order doesn't reproduce the source byte for byte, something got dropped or rewritten. Do the mechanical split first, commit it, and only then edit content in a separate commit. Mixing "move" and "rewrite" in one step makes review impossible.
Verify against the original. After the content pass, diff the old file's rules against the new location. I literally ask a fresh agent session: "Here is the old instruction file and the new docs tree. List every rule in the old file and where it lives now. Flag anything missing." It's a cheap check and it catches promoted-rule misses from the history pass.
Lint the links. A small linter in CI that checks: every wikilink or relative link resolves, every supersedes target exists, no ADR supersedes one dated after it, and no generated block was hand-edited. Broken links are how a docs graph slowly decays back into a pile of files.
This is the same discipline I'd apply to any agent-facing artifact. You evaluate what an agent does, not what it says; likewise, you verify a docs refactor by what the agent can still find, not by how tidy the folder looks.
Checklist: A Lean Instruction File in an Afternoon
This is the audit-and-refactor recipe, in order. It works for CLAUDE.md, AGENTS.md or Cursor rules.
- Copy the current instruction file somewhere safe as the reference original.
- Classify every section as rule, map, status or history.
- Mechanically move history into dated notes under
docs/changelog/. Commit. - Read each history note and promote any embedded rule to the instruction file or method file.
- Delete every hand-written number. Add a generated status block only for numbers a script can compute.
- If there's a decisions log, split it into one ADR per decision with
id,status,date,supersedes. - Write
supersedesonly on the newer ADR; generatesuperseded_byandstatus: superseded. - Add a rule to the instruction file: superseded ADRs are history, never act on them.
- Create
docs/INDEX.mdand point to it from the instruction file. - Create
docs/METHOD.mdwith rules for how agents read and write notes. - Move folder-specific rules into nested instruction files.
- Verify with a fresh session that every old rule has a new home.
- Add the link and supersession linter to CI.
- Add one line to the method file: "Never append session summaries to the instruction file."
That last item is what keeps you from doing this again in six months.
Trade-offs I'd Be Honest About
This structure has costs. More files means more places to look, and a human skimming the repo for the first time may find a single long file friendlier than an index plus a graph. The generator and linter are code you now maintain. And ADR discipline only works if people (and agents) actually write a new ADR instead of quietly editing an old one; the method file and linter help, but they don't replace review.
There's also a temptation to over-engineer: frontmatter fields nobody reads, link types nobody follows. Start with id, status, date and supersedes. Add more only when a real question can't be answered without it.
Conclusion
An instruction file is a prompt you send on every session, so it deserves prompt-level care: short, current, and made of rules rather than stories. Move history out, turn decisions into a graph where reversals are explicit, generate every number, and give the agent a map instead of a diary. The result is cheaper sessions and, more importantly, an agent that stops resurrecting decisions you already buried.
If you're setting up the rest of the system around this, start with ADRs and session handoffs for the memory side, and the spec-driven development primer for where specs fit in.