·10 min read

Claude Code AGENTS.md: when the rules pile up

I let Claude Code agents run a side project for a week, adding a rule after every mistake. Then development stopped. Half the commits on main were rules, and one rule had hidden finished work for four days.

aiclaude-codecontext-engineeringworkflow

Contents

For about a week in September I let Claude Code run a side project of mine, a visual game builder, more or less on its own. One session acted as coordinator. It wrote GitHub issues, released them to agent "lanes" (fourteen roles, one agent each), reviewed what came back and merged it. Whenever something went wrong, it wrote down a rule so it would not go wrong again.

Then development stopped. Main was green, CI passed, and nothing moved. Four days later I opened a fresh session and told it I was stuck.

Most Claude Code AGENTS.md best practices are about what to put in the file. This is about what happens when you keep putting things in, each one for a good reason.

What stuck looked like

The project had no file named AGENTS.md. Its instructions for agents lived in three places: a dispatch brief handed to every lane, a scheduling document for the coordinator, and a process-feedback document. Same job as an AGENTS.md, spread over three files, and the spread mattered less than what was in them.

The review measured before it suggested anything. The first number came from git log:

$ git log -40 --pretty=%s main | awk '{split($1,a,/[(:]/); print a[1]}' | sort | uniq -c
   1 chore
  21 docs
   8 feat
   7 fix
   3 refactor

21 of the last 40 commits on main were docs: and 8 were features. Those 40 covered only two days, the 25th and the 26th. On the 26th alone it was 9 docs commits to 1 feature. Over the whole stretch since the 20th the split was healthier, 91 feat to 62 docs out of 236, so the ratio had been sliding for a while and crossed over at the end.

The docs commits were mostly not documentation in the usual sense. Of the thirteen in the last 25, ten add or sharpen a rule; the other three are roadmap status. Paraphrased, a sample of what they said:

  • a contract has to check its criteria against the facts it cites
  • a deliberate breakage that names a file path asks for a branch nobody needs
  • an issue is a reading order, not a list of facts
  • name the one property a test fixture needs, not the whole fixture
  • a table row that names a near-miss is worse than no row
  • one rule's table had been incomplete every time anyone used it

The rest of the picture, from the same review:

  • The scheduling document was 345 lines, and its numbered rules had reached 18.
  • roadmap.md was 732 lines and had changed 13 times since the 20th, because every feature PR updated it. git merge-tree showed it as the single conflict blocking the oldest open PR.
  • That PR added a "+ Screen" button. Its description reported 17 perturbation runs (code broken on purpose, to prove the tests notice) and ran to about three pages.
  • Of 36 open issues, 15 were trackers, one per agent role. The real queue was about four items.
  • Finished work for one issue sat in a local worktree: four commits, 78 of 78 tests passing, never pushed. It had been there since the 26th.

Every rule was earned

None of these rules was speculative, and that is the uncomfortable part. Each one is tied to an incident, usually by issue number. The newest rule, that a contract must check its own criteria against the facts it cites, landed an hour before the coordinator used it on the next issue and caught an impossible criterion: it asked an agent to "swap any two layers' z-index values" when two of the three layers had no z-index to swap. The rule worked the first time it was applied.

The engine was a process document whose stated purpose was that every finished piece of work should improve the process as well as the product. Every lane had to close its handoff with a process-feedback section, required even when the agent had nothing to suggest. Fourteen lanes, each reporting friction at the end of every task, and a coordinator that took every report seriously. The rules followed.

Some of them I would write again tomorrow. Early in the week the coordinator was running three or four heavy agents at once, and the Claude Code session limit cut them off three times in one run, each time before the lanes had pushed anything:

You've hit your session limit · resets <time>

The fix was a capacity rule, two concurrent implementation lanes by default, and moving the dispatch brief out of the coordinator's scratchpad and into the repo where the next session could find it. Both were right.

The rule that hid finished work

The one that hurt was step zero of the dispatch brief:

Complete your self-review pass first. Read every file you changed, end to end, as a reviewer would, before you push.

Its reason was real. CI ran on a shared queue, and lanes had been pushing at the first green commit, finding a problem in their own review, and pushing again, which spent a CI cycle each time. The brief said commits should stay cheap and frequent. Only the push had to wait.

So the lane on that issue did its work, committed it, and its session ended before it pushed. Four commits stayed on my laptop for four days. Nothing on GitHub showed them, so nothing told the coordinator they existed.

Put that next to the session limits. After the first one, the coordinator's own note was that in-progress work that isn't pushed is lost when a session dies. Meanwhile the brief told every lane to keep its work local until a review pass was done. Each rule made sense in the document it lived in. Nobody read the two together, me included.

What the review changed

The unblocking part took one session. It resolved the "+ Screen" PR's roadmap conflict by keeping main's copy plus one paragraph from the branch, pushed the stranded work, made three smaller fixes, and merged five PRs, all green on CI. Main still passed afterwards, 2,791 tests. Closing finished or stale issues and parking the off-roadmap ones took the open count from 36 to 24.

Then the process change, which is the part that matters:

- Push the work branch and open a draft PR at the first commit that typechecks.
- The target is the roadmap step's "you can" sentence in the installed app; nothing else.
- Test the named behaviour, with at most 3 deliberate code-breaking checks per slice.
  Issue body at most 1 page, PR body at most 1 screen.
- Process feedback is optional. No new rules in the process docs before v1.
- Feature PRs don't edit roadmap.md; the coordinator updates it after merging.
- Each coordinator session starts by pushing or closing any unpushed agent branches.

Each line answers one of the measurements. The first replaces step zero: work is visible from the first commit that typechecks, and the self-review can happen on a draft PR. The roadmap line removes the file every PR fought over. The caps put a ceiling on what a button has to prove.

Claude Code could not apply it. Its edit to the agent-instruction docs was blocked, because other agents read those files, so it handed me the text to paste. It saved the same rules to its own memory instead, so the next coordinator session starts by looking for unpushed branches.

What I have not measured

Whether any of this fixed the stall. The diagnosis comes from git log, git merge-tree and the issue list, and the five merges are on main. But there has been no coordinator session on that project since, so there is no second git log to compare. And the dispatch brief in my checkout still has the old step zero, with nothing new at the top. The lean rules exist in a session transcript and in the agent's memory, and not yet in the file.

So what I have is a diagnosis and a proposed fix. If the next week of commits looks different, I will add the numbers here.

How to make Claude Code read AGENTS.md

This part is short, because it is the easy part. This site's repo keeps its rules in a root AGENTS.md, and CLAUDE.md is one line that imports it:

@AGENTS.md

Claude Code loads CLAUDE.md when a session starts and follows the @ import, so the rules have one home. That is the whole answer to how to make Claude Code use AGENTS.md, as far as this repo needs one. I have not tried a Claude Code AGENTS.md symlink instead, so I can't say whether it behaves the same.

The Claude Code AGENTS.md location that has worked for me is the repo root for the map, with the detail next to the work it governs: writing rules in content/blog/README.md, branching in CONTRIBUTING.md. I made the case for that layout in I stopped writing prompts. I write the repo, and for which changes an agent may make alone in the blast radius rule.

Where the rules end up

As a Claude Code AGENTS.md example, mine is 207 lines, and it has a section called "How to add a check" that opens: "Every rule in scripts/verify.mjs exists because something got past a review." That is the policy the game builder followed too. Both repos add a rule only after something actually went wrong.

The difference is where the rule lands. Here, most of them become code. A check in verify.mjs costs nothing while it passes; nobody reads it until it fails, and then it names the fix. It still has to be tested, but it does not have to be read. In the game builder the rules became prose that every lane read and every issue had to satisfy. A prose rule costs something on every task, whether or not the problem it guards against is anywhere near. My guess, and it is only a guess, is that eighteen of them read by fourteen lanes is how a "+ Screen" button ends up reporting 17 perturbation runs.

What I would now hold a Claude Code AGENTS.md file to, none of it measured beyond this one project:

  • A rule that can be a check should be a check. The prose that is left should be short enough to read in one sitting, because every agent reads all of it.
  • Findings go in the PR, not the rules file. A lesson from one PR is usually about that PR.
  • Count the docs commits. git log and uniq -c take a second, and the ratio is the earliest number I have found that says the process is eating the work.
  • No file that every PR edits. A shared status document is a merge conflict waiting for its turn.
  • Push early. A rule that keeps finished work off the remote has to justify itself against a session that dies before the push.
  • Read a new rule against the old ones. Step zero and the session-limit fix were each sensible, and together they parked a finished feature for four days.

The idea that the process should improve itself after every task sounded right when I set it up. It did improve itself, every single time. It never once got cheaper.

More posts

New writing when there is something worth saying. By email or by RSS, whichever you prefer.