injected-claude-md-is-the-worktrees-copy-not-mains · v1.0.0 · 2026-09-17 · sha256 1f22e2b713342025
injected-claude-md-is-the-worktrees-copy-not-mains v1.0.0A
Immutable. This exact content is served forever at /api/v1/blob/1f22e2b713342025.
---
name: injected-claude-md-is-the-worktrees-copy-not-mains
description: |
The CLAUDE.md (or AGENTS.md / GEMINI.md) your session quotes is the copy in the
checkout the session STARTED in, not the one on the default branch — and a
long-lived worktree's copy can be weeks behind, so every house rule in context
may be retired. Use when: (1) you are about to write or say "CLAUDE.md says X",
"the house rule is X", or "the project file is stale about X"; (2) your session
runs in a worktree that was created days or weeks ago; (3) a SessionStart hook
or banner mentions the instruction file differing from the default branch;
(4) you are about to file an issue, correct a document, or tell a peer that a
shared instruction file contradicts the code. The failure inverts: the file on
the default branch is usually RIGHT and your injected copy is the stale thing,
so the bug report points at the wrong artefact. A retired copy is often LONGER
than the current file — superseded correction notes accumulate until a rewrite
deletes them — so the stale version reads as the more complete one. The copy
carries no date and no banner; compaction re-injects the same stale bytes.
Settle it with `git show <remote>/<default-branch>:CLAUDE.md` and cite that,
never the text in context.
version: 1.0.0
date: 2026-09-17
author: wan-huiyan
disable-model-invocation: true
---
# The Injected CLAUDE.md Is Your Worktree's Copy, Not the Default Branch's
## Problem
Claude Code reads the project instruction file — `CLAUDE.md`, and the equivalent
for other harnesses — from the checkout the session started in, and injects it
into context at session start. In a worktree that was created three weeks ago and
never merged since, that is a three-week-old file.
**Nothing about it looks old.** It arrives in the same position, with the same
heading, as the current file would. It has no commit stamp, no date, no "you are
reading a snapshot" banner. Every rule in it reads as the house rule in force.
Two properties make it worse than an ordinary stale read:
1. **The retired copy is frequently LONGER than the live one.** An instruction
file accumulates correction notes — "this said X until <date>, and X was
wrong" — and periodically someone rewrites it down to the rules as they now
stand, deleting the archaeology. A session holding the pre-rewrite copy holds
more text, more caveats and more history than the current file, so on any
"which of these looks more complete?" instinct the stale one wins.
2. **Compaction re-injects the same bytes.** A long session that compacts is
re-served the instruction files from the same checkout, so the copy is
refreshed from a stale source and looks re-verified.
The damage is not that you follow an out-of-date rule — that is usually benign,
because retired rules are mostly stricter than current ones. The damage is when
you make a CLAIM about the file: you report that the shared instruction file is
stale, contradicts the code, or needs an issue. **The direction inverts.** The
file on the default branch is correct and already says the thing you are about to
"discover"; the stale artefact is the copy in your own context. The issue, the
correction and the peer message all point at the wrong artefact, and they are
persuasive because you quoted the file.
## Context / Trigger Conditions
You are in the right place if any of these hold:
- You are about to write "CLAUDE.md says", "the house rules say", "the project
file is stale about", or "the repo contradicts itself" — in an issue, a pull
request body, a memory note, or a message to another session.
- Your session is running in a worktree, and you did not create that worktree in
this session.
- A SessionStart hook, banner or system message mentions the instruction file
being behind, or prints two byte counts.
- You are reconciling a document against code and the document is an instruction
file rather than ordinary prose.
- A peer session quotes the house rules at you from ITS context. Same trap, one
hop away: that quote is a quote of that session's checkout.
**Two things that look like corroboration and are not:**
1. **The file is present, complete and internally consistent.** A retired version
is a real file that was correct on its own commit. Reading it more carefully
cannot reveal its age.
2. **The rule you are reading is very specific and clearly deliberate.** Retired
rules are specific and deliberate too — that is why they were written down and
why their deletion needed a rewrite rather than an edit.
## Solution
**One command settles it, and the claim must cite that command rather than the
text in your context.**
### Step 1 — Fetch, then read the file from the default branch
```bash
git fetch -q origin main
git show origin/main:CLAUDE.md | wc -c
git log -1 --format='%h %ci %s' origin/main -- CLAUDE.md
```
The third line is the one that tells you whether a rewrite happened and when.
### Step 2 — Compare against what you were injected
```bash
wc -c CLAUDE.md # your checkout's copy
git show origin/main:CLAUDE.md | wc -c # the live one
```
A large size difference in EITHER direction means your copy is not the live file.
Do not reason about which is "more complete" — see Problem, property 1.
### Step 3 — Search the live file before claiming anything about it
```bash
git show origin/main:CLAUDE.md | grep -n "<the subject of your claim>"
```
If the live file already states the thing correctly, there is no defect and no
issue to file. If it genuinely disagrees with the code, you now have a real
finding, sourced from the live file.
### Step 4 — Fix the source of the staleness, once
The stale copy will keep being injected into every future session started in that
worktree, including other people's. Merge the default branch into the worktree's
branch (never a hard reset of somebody else's work), or, if the worktree is
finished, remove it after the usual clean check:
```bash
git -C <worktree> status --porcelain # must be empty before removing
```
### Step 5 — Phrase the claim so it carries its own provenance
> My copy of CLAUDE.md (worktree `<path>`, from `<sha>`) says A; `origin/main`'s
> CLAUDE.md (read just now, last changed in `<sha>` on `<date>`) says B.
"The file is stale" is a conclusion you may only write AFTER Step 1, and it names
which copy.
## Verification
The check must be able to return "current", or it is not a check.
Run Steps 1-2 in a **fresh worktree of the default branch** and confirm they
report identical bytes and the same last-changed commit:
```bash
git worktree add /tmp/control-check origin/main
cd /tmp/control-check
[ "$(wc -c < CLAUDE.md)" = "$(git show origin/main:CLAUDE.md | wc -c)" ] \
&& echo "CURRENT" || echo "STALE"
```
A control that says STALE on a fresh checkout of the default branch is measuring
something else — most likely you compared against a local `main` ref that is
itself behind, rather than `origin/main` after a fetch.
## Example
*Worked example from a route-generation repository where several sessions run in
parallel worktrees. Paths and SHAs are that repository's; the mechanism is not.*
A session running in a worktree created weeks earlier was asked to save its
lessons. Reconciling one of them against the code, it measured that the house
file's passages about which drawing the search actually uses were contradicted by
the code, and drafted both a memory note and a message to the coordinating
session saying so.
Measured before sending:
```
checkout's CLAUDE.md ....... 238,232 bytes
origin/main's CLAUDE.md ..... 81,929 bytes
origin/main last change ..... d23357470 2026-09-15 "CLAUDE.md carries only the
house rules as they stand today"
```
**The live file was right.** It stated the code's behaviour correctly and had done
since the rewrite two days earlier. What the session held was the 238 KB
pre-rewrite copy — nearly three times the size, carrying months of correction
notes the rewrite had deleted. The "defect" was entirely inside its own context.
Two details worth carrying:
- **A SessionStart hook had printed the warning, with both byte counts, at the
top of the session.** It was skimmed, and the session then reasoned from the
stale rules for hours. Detection existing is not detection working; the hook
output has to be treated as a blocker on claims about the file, not as a
banner.
- **The bigger file felt like the more authoritative one.** Every instinct about
completeness pointed the wrong way.
**The same class, same day, one layer up.** The same session checked a plugin's
skills for duplicate coverage by reading the installed copy under
`~/.claude/plugins/cache/...`, which held **99 skills at v1.29.0**, while the
source repository on disk held **111 at v1.36.1** — twelve skills newer,
committed by peers twenty minutes earlier. The duplicate check was re-run against
the source and one of the two candidate lessons turned out to be already covered.
A cache directory named for a version is a label, not an identity.
## Notes
- **This is not the same as a peer editing the file under you.**
[`isolate-subagent-verification-from-live-worktree`](../isolate-subagent-verification-from-live-worktree/SKILL.md)
covers a live worktree changing DURING a run. Here nothing changes: the file is
quietly a different, older file for the whole session.
- **Sister skill**: [`inherited-scope-doc-names-may-not-exist`](../inherited-scope-doc-names-may-not-exist/SKILL.md)
is the same principle for a plan or scope document from a previous session —
verify what an inherited artefact asserts before you dispatch on it. This skill
is the case where the inherited artefact is the RULES themselves, injected
automatically, with no moment at which you chose to trust it.
- **It reaches every automatically-injected file**, not just `CLAUDE.md`:
`AGENTS.md`, `GEMINI.md`, nested per-directory instruction files, and a
plugin's or skill's installed copy under a cache directory.
- **Length is not recency and neither is specificity.** Both heuristics point at
the retired copy, which is why this needs a command rather than judgement.
- **A worktree that outlives a few days is the risk surface.** If your workflow
keeps long-lived worktrees, merging the default branch into them is cheap
insurance; the alternative is that every session started there inherits the
same retired rulebook.
## References
- Claude Code docs: [Memory / CLAUDE.md](https://docs.claude.com/en/docs/claude-code/memory)
— how project instruction files are discovered and loaded at session start.
- Git docs: [`git show`](https://git-scm.com/docs/git-show) — `<rev>:<path>`
reads a path out of a commit without checking it out, which is what makes
Step 1 free and unambiguous.
- Git docs: [`git worktree`](https://git-scm.com/docs/git-worktree) — each
worktree carries its own working-tree copy of every tracked file, including the
instruction files a harness injects.