Home › Articles

Stale AGENTS.md and CLAUDE.md: why agents follow them, how to catch it

Updated 2026-10-26

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:

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:

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:

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:

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 hasClaude Code reads
AGENTS.md, and no CLAUDE.md or CLAUDE.local.md in the working directory or above itAGENTS.md
AGENTS.md and a CLAUDE.md or CLAUDE.local.md in the working directory or above itThe CLAUDE.md files only
A CLAUDE.md that imports AGENTS.mdCLAUDE.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:

  1. 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.
  2. 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.
  3. Don't describe what the code already says. Directory trees, dependency lists and architecture overviews go stale fastest. The Claude Code docs note that /doctor proposes cutting exactly that kind of content and keeping pitfalls, rationale and conventions that differ from tool defaults.
  4. Keep history out. "We used to…" lines belong in a decisions log or the git history, not the always-loaded file.
  5. 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

  1. Add the precedence sentence to ~/.claude/CLAUDE.md and ~/.codex/AGENTS.md.
  2. Look for stray CLAUDE.md, .claude/CLAUDE.md and CLAUDE.local.md files above your repos.
  3. Make AGENTS.md the single source and start CLAUDE.md with @AGENTS.md.
  4. Add scripts/check_agent_docs.py, run it, and fix or delete every line it reports.
  5. Wire it into pre-commit and CI.
  6. 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.

Sources

Official docs, checked 2026-10-26.

Get the free rules sampler + new tips by email

Confirm your email and we'll send you straight to the sampler download. Then one short email every two weeks covering tool format changes (Cursor, Claude Code, Codex, Windsurf, Copilot), new rules and kit updates. No spam.

We store only your email, with our email provider Resend. See the privacy policy.

Unsubscribe anytime from any email. Rather skip email? Get the sampler on GitHub or download the zip directly.