Stale AGENTS.md and CLAUDE.md: why agents follow them, how to catch it
In short: when AGENTS.md or CLAUDE.md disagrees with the code, coding agents tend to believe the file. Add one precedence line to your user-level instructions ("when docs and code disagree, follow the existing code and tell me"), put a short check in CI that fails when a path, script or import in those files no longer exists, keep one source of truth with @AGENTS.md, and delete lines the agent doesn't need.
Why a stale instruction file is worse than none
An instruction file is read at the start of every session, so every line in it carries weight. That's the point when the file is right. When it's wrong, the agent starts every task with a confident, outdated description of your project. The usual ways it goes stale are boring:
- Renamed paths. "Handlers live in
src/handlers/" after you moved them tosrc/api/. The agent creates new files in the old place, or recreates the folder. - Dead commands. "Run
npm run typecheckbefore finishing" after the script was folded intolint. The agent either fails the step or invents a replacement. - Removed dependencies and old conventions. "Use
momentfor dates" or "useprint()for progress messages" after the codebase moved on. The agent adds the old library back, or rewrites working code to match the file. - Contradictions between files. A root file says one thing and a nested one, or a
.claude/rules/file, says another. The Claude Code docs warn that Claude may then pick one arbitrarily.
What the new research says
This topic took off on X this week because of a paper posted to arXiv on 22 October 2026: "Stale Instruction Files Override the Code in Coding Agents: A One-Sentence Fix" by Xiaonan Xu and Wenjing Wu. The authors built 40 small Python projects where the instruction file asked for one convention and the code used another (for example print versus logging), then had Claude Code and Codex CLI add a feature, 320 runs in total. Their reported results:
- Without any fix, 90.1% of the agents' choices followed the stale file.
- Claude Code pointed out the conflict in 79 of 80 runs and still followed the file.
- Codex CLI rewrote existing code to match the file in 20 of 80 runs, and the project's tests then failed in 17 of them.
- One sentence in the user-level instruction file moved code-following from 4.2% to 91.4%, stopped the rewrites, and raised the test pass rate from 89.4% to 100%.
Their explanation: the instruction file reaches the model as an instruction, while the code arrives as tool output, which models are trained to trust less. Keep the scope in mind. These are synthetic Python projects, two agents, and the authors' own runs. Still, the fix costs one line.
Fix 1: add a precedence line
This is the sentence the paper used, word for word:
When a repository's documentation and its existing code disagree, follow the conventions used in the existing code, and in your final reply point out each inconsistency you found between the documentation and the code.
The paper put it in the user-level file, not the project one: ~/.claude/CLAUDE.md for Claude Code and ~/.codex/AGENTS.md for Codex. That's a good default, because it works in every repo without anyone first finding the stale line. Two details:
- For Codex, the docs say
~/.codex/AGENTS.override.mdis used instead of~/.codex/AGENTS.mdwhen it exists, so put the line in whichever one you actually use. - For a team, you can add a project version near the top of the shared
AGENTS.md, e.g. "The code andpackage.jsonscripts are the source of truth; if this file disagrees with them, follow them and tell me". The paper only tested the user-level placement, so treat the project copy as a sensible extra, not a measured result.
One caveat: "code wins" is wrong in the middle of a migration, when the file describes where you're going and the old code is what you're leaving. Say so explicitly: "We're moving from moment to date-fns. New code uses date-fns; don't rewrite old call sites unless asked."
Fix 2: fail CI when the file points at something that's gone
The precedence line covers conventions. Paths and commands can be checked mechanically. This script scans every AGENTS.md and CLAUDE.md in the repo (or the files you pass it) and reports:
- code-formatted paths like
`src/api/`or`scripts/seed.ts`that don't exist, relative to the file or the repo root; @pathimports outside code spans (Claude Code's import syntax) whose target is missing;npm run/pnpm/yarn/bun runscripts missing frompackage.json;maketargets missing from theMakefile.
Save it as scripts/check_agent_docs.py and run it from the repo root. It needs only Python 3, no packages:
#!/usr/bin/env python3
"""Fail when AGENTS.md / CLAUDE.md point at a path, @import, package.json script or make target that is gone."""
import json, pathlib, re, sys
ROOT = pathlib.Path.cwd()
files = [pathlib.Path(a) for a in sys.argv[1:]] or [
p.relative_to(ROOT) for p in ROOT.rglob("*.md")
if p.name in ("AGENTS.md", "CLAUDE.md") and not {"node_modules", ".git"} & set(p.parts)]
pkg, mk = ROOT / "package.json", ROOT / "Makefile"
scripts = json.loads(pkg.read_text()).get("scripts", {}) if pkg.exists() else {}
targets = set(re.findall(r"^([\w.-]+):", mk.read_text(), re.M)) if mk.exists() else set()
ALLOW = {".env", ".env.local"} # fine to be missing: gitignored or created on demand (add PLAN.md etc.)
BUILTIN = {"install", "i", "ci", "add", "remove", "dlx", "exec", "create", "init", "update", "upgrade", "audit"}
EXT = r"\.(md|mdc|json|jsonc|ya?ml|toml|ini|cfg|env|lock|txt|sh|py|js|jsx|ts|tsx|mjs|cjs|go|rs|rb|java|kt|swift|gd|tscn|sql|css|html)$"
PATH = re.compile(r"^(\.{1,2}/)?[\w.-]+(/[\w.-]+)*/?$")
CMD = re.compile(r"\b(?:npm run|npm|pnpm run|pnpm|yarn run|yarn|bun run)\s+([\w:.-]+)")
problems = []
def exists(f, s):
return (f.parent / s).exists() or (ROOT / s).exists()
for f in files:
text = f.read_text()
blocks = re.findall(r"```[^\n]*\n(.*?)```", text, re.S)
prose = re.sub(r"```.*?```", "", text, flags=re.S)
spans = [s.strip() for s in re.findall(r"`([^`\n]+)`", prose)]
for s in spans: # `src/api/`, `scripts/seed.ts`, `.env.example`
if PATH.match(s) and ("/" in s or re.search(EXT, s)) and s not in ALLOW and not exists(f, s):
problems.append(f"{f}: path `{s}` does not exist")
for imp in re.findall(r"(?<![\w`])@([\w./-]*\w)", re.sub(r"`[^`\n]+`", "", prose)):
if ("/" in imp or "." in imp or imp.isupper()) and not (f.parent / imp).exists():
problems.append(f"{f}: import @{imp} does not exist")
for line in spans + [l for b in blocks for l in b.splitlines()]:
for name in CMD.findall(line):
if name not in BUILTIN and not name.startswith("-") and name not in scripts:
problems.append(f"{f}: script `{name}` is not in package.json")
for name in re.findall(r"\bmake\s+([\w.-]+)", line):
if name not in targets:
problems.append(f"{f}: make target `{name}` is not in the Makefile")
print("\n".join(sorted(set(problems))) or f"OK: {len(files)} instruction file(s) checked")
sys.exit(1 if problems else 0)
Given this AGENTS.md, plus a CLAUDE.md that imports a file that was deleted:
# AGENTS.md
- API handlers live in `src/api/`. Old handlers were in `src/handlers/`.
- Setup steps are in `docs/setup.md`.
- Before you finish, run `npm run lint`, `npm test` and `npm run typecheck`.
```bash
pnpm dev
make deploy
```
it prints the following and exits with status 1. We tested it on exactly this sample repo with Python 3.13, then fixed each line and got OK: 2 instruction file(s) checked and exit 0:
$ python3 scripts/check_agent_docs.py
AGENTS.md: make target `deploy` is not in the Makefile
AGENTS.md: path `docs/setup.md` does not exist
AGENTS.md: path `src/handlers/` does not exist
AGENTS.md: script `typecheck` is not in package.json
CLAUDE.md: import @docs/claude-extra.md does not exist
$ echo $?
1
Notice the src/handlers/ result: "Old handlers were in src/handlers/" gets flagged too. That's deliberate. A history note about a path that's gone is just more stale text for the agent to act on; delete it. Paths that are allowed to be missing, like a gitignored .env, go in the ALLOW set at the top. If a line really must mention a missing path, write it without backticks. The script also flags a package name such as @scope/pkg written outside backticks; that's correct, because Claude Code treats any @path outside a code span as an import, in AGENTS.md files it reads too.
Run it as a pre-commit hook:
printf '#!/bin/sh\nexec python3 scripts/check_agent_docs.py\n' > .git/hooks/pre-commit
chmod +x .git/hooks/pre-commit
or as a GitHub Actions job, so it also catches the commit that renames a folder without touching the docs:
name: agent-docs
on: [push, pull_request]
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- run: python3 scripts/check_agent_docs.py
It's deliberately simple. It won't catch a wrong convention ("use print()"), a wrong flag, or a script that exists but now does something else. For those, use the precedence line and the review below.
Claude Code also has a built-in review: /doctor prompt-audit (v2.1.283 or later). Per the memory docs, it looks for instructions written for older models, references to files or commands that don't exist, and files that contradict each other, across CLAUDE.md, CLAUDE.local.md, AGENTS.md and the rules, skills and subagents under .claude/. It proposes edits and changes nothing until you say so. Use it for the judgment calls; keep the script for CI, where nothing should depend on a model.
Fix 3: one source of truth, so the copies can't drift
Two copies of the same rules guarantee that one goes stale. Claude Code reads CLAUDE.md, and Codex, Cursor, Windsurf and Copilot read AGENTS.md. Recent Claude Code versions (v2.1.277+) can read AGENTS.md directly, but by default only when there's no CLAUDE.md. The docs' table is clear:
| Your repo has | Claude Code reads |
|---|---|
AGENTS.md, and no CLAUDE.md or CLAUDE.local.md in the working directory or above it | AGENTS.md |
AGENTS.md and a CLAUDE.md or CLAUDE.local.md in the working directory or above it | The CLAUDE.md files only |
A CLAUDE.md that imports AGENTS.md | CLAUDE.md, with AGENTS.md included through the import |
Note "or above it". A CLAUDE.md, .claude/CLAUDE.md or CLAUDE.local.md in any parent folder, say a forgotten one in ~/code/, counts, and Claude then stops reading your AGENTS.md by default. Only ~/.claude/CLAUDE.md, a managed CLAUDE.md and .claude/rules/ files don't count. CLAUDE.md files above the working directory are also loaded at launch, so an old parent-folder file feeds its own stale rules into every session. If AGENTS.md seems ignored, look up the tree first, then check /memory or /context.
The simple fix works in every case: keep the rules in AGENTS.md and make CLAUDE.md a thin file that starts with the import, with Claude-only notes after it:
@AGENTS.md
## Claude Code
- Use plan mode for changes under `src/billing/`.
The docs confirm the import is never read twice, whatever the Project instructions setting. The full pattern is in AGENTS.md vs CLAUDE.md: one source of truth, and the Codex side (discovery order, nested files, the 32 KiB cap) is in AGENTS.md for Codex: a practical template. The checker above also flags the import if someone renames AGENTS.md.
Fix 4: prune, don't just add
Instruction files only grow unless someone deletes lines. A short habit that keeps them honest:
- Edit the file in the same commit as the change. Renamed a folder, dropped a script, swapped a library? Update or delete the line now. The CI check makes forgetting fail loudly.
- Delete rules the agent already follows. Remove a line, run a typical task in a fresh session, and see if anything changes. If the agent does it anyway (it reads
package.json, it follows the existing style), the line was only costing attention. - Don't describe what the code already says. Directory trees, dependency lists and architecture overviews go stale fastest. The Claude Code docs note that
/doctorproposes cutting exactly that kind of content and keeping pitfalls, rationale and conventions that differ from tool defaults. - Keep history out. "We used to…" lines belong in a decisions log or the git history, not the always-loaded file.
- Re-read it every few weeks, or run
/doctor prompt-audit, and resolve contradictions between root, nested and rules files.
Shorter files also get followed better; the reasons, and how to see what's actually loaded, are in why a long CLAUDE.md gets ignored.
A ten-minute cleanup
- Add the precedence sentence to
~/.claude/CLAUDE.mdand~/.codex/AGENTS.md. - Look for stray
CLAUDE.md,.claude/CLAUDE.mdandCLAUDE.local.mdfiles above your repos. - Make
AGENTS.mdthe single source and startCLAUDE.mdwith@AGENTS.md. - Add
scripts/check_agent_docs.py, run it, and fix or delete every line it reports. - Wire it into pre-commit and CI.
- Delete three lines the agent doesn't need.
FAQ
Why does my coding agent follow an outdated AGENTS.md instead of the code?
The instruction file reaches the model as an instruction, while the code only arrives as the output of a file-reading tool. A paper posted to arXiv in October 2026 argues that models are trained to rank instructions above tool output, so a stale line in the file can beat the code it used to describe, even when the agent notices the conflict.
What line should I add so the agent trusts the code over stale docs?
Something like: when a repository's documentation and its existing code disagree, follow the conventions used in the existing code, and point out each inconsistency in your final reply. Put it in your user-level file (~/.claude/CLAUDE.md for Claude Code, ~/.codex/AGENTS.md for Codex) so it applies to every repo.
How do I check that the paths and commands in AGENTS.md still exist?
Run a small script in CI or as a pre-commit hook. It scans AGENTS.md and CLAUDE.md for code-formatted paths, @imports, package.json scripts and make targets, and fails when one no longer exists. The article includes a tested Python version of about 40 lines.
Does Claude Code read AGENTS.md if I also have a CLAUDE.md?
Not by default. Claude Code reads AGENTS.md only when there is no CLAUDE.md, .claude/CLAUDE.md or CLAUDE.local.md in the working directory or any folder above it. Your ~/.claude/CLAUDE.md doesn't count. If you keep a CLAUDE.md, start it with @AGENTS.md so both tools read the same rules.
How often should I prune CLAUDE.md or AGENTS.md?
Whenever you change the thing a line describes, and in a quick review every few weeks. Delete rules the agent already follows without being told, rules about code that no longer exists, and anything the agent can read from package.json or the code itself.