Your CLAUDE.md Is Probably a Changelog: Agent Instruction Files That Scale

12 min read
Claude CodeAI AgentsSoftware EngineeringSpec-Driven DevelopmentAI-First SDLC

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:

  1. Zombie decisions. A choice that was later reversed still reads as current, so the agent reintroduces the thing you removed.
  2. Contradicting numbers. Two sections state different counts of the same thing. The agent picks one, and you can't predict which.
  3. 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.

BucketWhat it isExampleBelongs in the instruction file?
RuleA standing constraint or convention that applies to future work"All DB access goes through the repository layer"Yes
MapWhere things live and where to look next"Specs live in docs/specs/, decisions in docs/adr/"Yes, briefly
StatusA fact about the current state that changes over time"Tests: 214 passing", "Phase 3 in progress"Only if generated
HistoryWhat 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:

Loading diagram...

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.

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:

Loading diagram...

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 repoInstruction file (rules)
Constrains work in one package or folderNested instruction file in that folder
Explains how to write or organize docsMethod file
Records a choice between alternatives, with a reasonNew ADR
Reverses an earlier choiceNew ADR with supersedes
Describes what happened in a sessionDated changelog note
States a count, phase or percentageGenerated status block, or nowhere
Explains where something livesIndex, 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 supersedes only on the newer ADR; generate superseded_by and status: superseded.
  • Add a rule to the instruction file: superseded ADRs are history, never act on them.
  • Create docs/INDEX.md and point to it from the instruction file.
  • Create docs/METHOD.md with 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.