You write CLAUDE.md once. Six months later there are eighteen of them on your laptop and you cannot say which one is right.
This is not a hypothetical. I scanned my own machine and found exactly that. What follows is why it happens, and why the obvious fix does not fix it.
The copy is created by the tool, not by you
Every agent reads instructions from a path it owns.
| Tool | Path |
|---|---|
| Claude Code |
CLAUDE.md, ~/.claude/CLAUDE.md
|
| Cursor | .cursor/rules/*.mdc |
| GitHub Copilot | .github/copilot-instructions.md |
| Codex / others | AGENTS.md |
You did not decide to keep four copies. You decided to use four tools. The copies came with them.
Drift is not caused by carelessness
Three mechanics produce it, none of which involve anyone being sloppy.
Editing at the point of use. An agent misbehaves mid-task. You fix the instruction file that is open in front of you. That file is one of four, and the fix lands in one.
The checkout is not the unit. You have the same repo cloned twice — one for the main branch, one for a long-lived branch. Both have instruction files. They diverge the moment either is touched.
Home directory rules are invisible. ~/.claude/CLAUDE.md belongs to no repository. It is in no CI. It is where the rules you did not want to argue about in review end up.
Generating from one source does not close it
The natural response is to stop keeping copies: write AGENTS.md once and generate the rest.
I built that. It works. It is agent-fanout — single-file Python, zero dependencies, MIT.
python3 agent_fanout.py
create CLAUDE.md
create .cursor/rules/from-agents-md.mdc
create .github/copilot-instructions.md
Generated files carry a header so nobody edits them directly, and --check in CI turns a hand-edited derivative into a red build.
That covers this repository, where CI runs. Which sounds like everything until you list what falls outside:
- Global config in the home directory — no repo, no CI
- The window between edits — an agent reads a file the moment it changes; CI notices on push, if there is a push
- Repos with no CI — prototypes, throwaway clones, last week's experiment. The place instructions get rewritten most freely and gated least
- The unit is the machine, not the repo. The agent runs on a laptop holding many checkouts, several copies of the same repo, and a home directory. Anything scoped to one repository cannot see across that.
So measure, do not only prevent
Prevention is policy. Detection is measurement. Policy gets bypassed in ways you cannot see unless you measure.
agent-drift scans paths rather than repos, and groups by content similarity rather than filename:
python3 agent_drift.py ~/work ~/side-projects
Scanned 47 instruction files.
DRIFT: 2 documents, 5 distinct versions between them.
claude-code:CLAUDE.md
6 copies, 3 versions
9b01aeaa204d 3 files, 406 lines
2dc3c616c279 2 files, 411 lines
Filename matching is useless here. Unrelated projects having different CLAUDE.md files is correct, and a tool that reports that as drift stops being used. Point it at your whole working directory, not one project — the interesting results cross repository boundaries.
Two jobs, not one
| Scope | Question it answers | |
|---|---|---|
| agent-fanout | one repository | are the derived files current |
| agent-drift | whole machine, many paths | where did copies diverge |
Generation removes the reason copies exist. Detection catches the copies that exist for reasons you did not anticipate. Doing only one leaves you feeling covered, which is worse than knowing you are not. I walked into that myself.
Where this ends
Honestly, the end state is that neither script is needed — assets do not sit scattered as files on a machine, and one reviewed copy reaches every machine. That is what I am building at untactit, currently pre-launch.
The scripts do not depend on it. Take whichever part is useful.
Top comments (0)