My Config Didn't Contradict Itself. That Was the Problem.
I had written the same four rules twice in the same file, without noticing for 46 days.
Near the top of my vault’s CLAUDE.md, I’d drawn the directory tree with the rules written into its comments:
wiki-vault/├── .raw/ # immutable source documents — never modify├── wiki/│ ├── index.md # master catalog — update every ingest│ ├── log.md # append-only op log, newest at top│ ├── hot.md # ~500-word recent-context cacheAnother 31 lines down, in the Conventions list, I’d written all four again:
- .raw/ contains source documents — never modify them- wiki/index.md is the master catalog — update on every ingest- wiki/log.md is append-only — newest entries at the TOP, never edit past entries- wiki/hot.md is overwritten each session, kept under 500 wordsSame file. Same rules.
I checked when it happened, and it was there in the first commit. June 9.
I found it on July 25, because Anthropic published a post about their own version of the problem.
Neither one was wrong.
That’s worse.
Anthropic had removed over 80% of Claude Code’s system prompt for Opus 5 and Fable 5, with no measurable loss on their coding evals. They found the problem by reading transcripts of their own internal usage, and watching instructions collide inside one request — “leave documentation as appropriate” from one layer against “DO NOT add comments” from another. The model resolves it. It just spends reasoning doing that instead of the work.
A contradiction is loud. It produces behavior strange enough to send you looking, which is exactly how Anthropic found theirs. Two copies that agree produce the correct behavior every single time, so there’s no symptom, and nothing ever points at them.
In our code, we try to avoid duplication. It’s a promise to update both copies forever.
I made that promise four times in one commit and didn’t notice I’d made it.
A transcript only shows you the times something went wrong. A duplicate that agrees never goes wrong, so it never shows up.
I never opened that file. Claude wrote it, Claude updated it, both at my direction.
It gets worse from here. The rules that run my sessions live in a global conventions file, a project file, and a handful of skills. Claude reads all of them stitched together into one request. I can only open them one at a time.
The cleanup ran past that one file. I moved my note-filing rules out of my global conventions file and into a skill, which took it from 108 lines to 62 — about 475 tokens back on every request, in every repo.
That’s the weakest argument here. There’s real research showing models get less reliable as input grows, but it shows up across ranges far larger than 475 tokens. If I told you 475 tokens was hurting my agent’s reasoning, I’d be selling you something.
It’s cheap rent. The expensive part is the day the two copies stop matching.
Then the part I got wrong.
I expected the file to shrink. Instead the commit came back one line longer — 33 out, 34 in — and about 30 percent more characters.
The trim had worked. The part I actually cut, the directory tree, did get smaller.
The same commit added four conventions I’d been meaning to write down, and together they were bigger than everything the trim had taken out.
The commit message only mentions what I took out.
The pointer that replaced my note-filing rules costs more characters than they did. It still comes out ahead, because the rules now load when I’m capturing something instead of every time I open a session.
I’d read delete 80% as an instruction to be terse. It’s closer to an instruction about timing.
One more thing turned up that wasn’t duplication. My CLAUDE.md said to prefer the Obsidian CLI, and noted it was installed.
There is no Obsidian CLI on this machine, and there never was. The line even cites a version string — md.obsidian.Obsidian 1.12.7 — which is the Flatpak ID for the desktop app.
I didn’t write that line either. It went in with the first commit on June 9. Three days later, a plugin script probed the machine for what was actually installed and wrote the result to transport.json. It has said the CLI is absent every day since.
One file asserted a fact into every session. The file next to it disagreed the whole time. Nothing compared them, because nothing was looking.
The line now points at transport.json instead of answering the question itself.
Then I ran /doctor. It weighed what I carry before typing a word: about 9,100 tokens, half of it the list of skills I have installed. Three of them had never once fired.
It also flagged the capture migration — which Claude had already recommended, before I ran the tool.
That’s the thing I’d carry over. /doctor tells you what your context weighs. It doesn’t compare two files to see whether they agree.
What found the duplication was Claude, reading my config as a document because I asked it to. Same for the CLI line.
There’s a plugin ruleset of 134 lines that loads in every session, and I still haven’t read it.
I’d been writing these files for the model to read. It turns out it can read them back.