I asked Claude to move two files, then raise a PR.
The tiny change:

1988 commits?!
Claude raised the PR against dev instead of master.
It wasn't showing a two-file diff. It was showing every commit on master that hadn't landed in dev.
Months of divergence.
This happened to me before.
The first time I just assumed I'd done something wrong and closed the pull request. Then I got pinged to review a PR when a colleague did exactly the same thing.
After this happened a few times, I added an instruction to my CLAUDE.md.
So why, given an explicit instruction that master is our trunk, did Claude choose dev?
I needed to figure out why.
This time, while everything was in context, I forked the session and asked Claude:
"Why did you raise the PR against `dev` instead of `master`?"
Claude ran:
git remote show origin | grep "HEAD branch"# HEAD branch: master"The trunk branch is `master`, the remote confirms it."
That's nice, but that just told me what I already knew and didn't answer my question.
Now I was more confused. Claude itself determined trunk was master.
So where was dev coming from?
"If the trunk is `master`, then why did you create the PR against `dev`?"
This time, Claude ran:
git branch -r | grep -E "(dev|master|main)"And there it was:
origin/HEAD -> origin/devorigin/devorigin/masterThis shows origin/HEAD was pointing at origin/dev.
It's a file located at .git/refs/remotes/origin/HEAD.
It contains a symbolic ref, a local cache of what the remote considers its default branch.
Git writes it on first clone.
This repository was cloned at a time when dev was the default.
Later, we standardised on master as trunk across all our repos.
But my local ref was never updated.
I suspected Claude was using this local symbolic ref:
git symbolic-ref refs/remotes/origin/HEAD# refs/remotes/origin/devLater, after the Claude Code source code leak, I took a look to confirm what was going on.
Claude Code injects a gitStatus block into the system prompt before every session starts. One field reads: "Main branch (you will usually use this for PRs)." It resolves that value from the local symbolic ref.
There's also a fallback: if origin/HEAD isn't set at all, the harness checks
whether origin/main or origin/master exist as remote branches, and
picks from there. Stale is worse than absent. If the file had never been
written, Claude probably would have landed on the right answer.
So every session started with the harness telling Claude: your trunk is dev.
Before memory, instructions, or anything I typed.
The remote was correct. The problem was local: one file, unchanged since I cloned the repo, had been giving every session the wrong answer.
Super simple:
git remote set-head origin -aThis re-fetches origin/HEAD from the remote and updates the local cache.
After that, git branch -r shows origin/HEAD was updated to origin/master.
origin/HEAD -> origin/masterorigin/devorigin/masterProblem solved.
Your codebase is your prompt.
Everything in your repo is context. Git config, naming conventions, TODOs, READMEs, type signatures, folder structure, variable names. Claude reads all of it. The same way it reads your instructions.
The agent wasn't doing what I wanted, so I tried to correct it by adding an instruction to my CLAUDE.md.
I assumed it was guessing the wrong branch because of its non-deterministic nature.
But that's not what was happening.
Claude read my codebase and found what it needed. It had no way to know the answer was stale.
After I applied the fix, Claude stopped making the mistake and I was able to remove the instruction.
We obsess over prompting. Custom instructions, workflows, memory files. But our code still needs to be clean. We've always cared about code quality because humans need to read it. Now it's doubly important with agents also reading the code.
If the code is confusing to you, it's also confusing to an agent. Agents struggle with ambiguity. They confidently resolve it from whatever signal is loudest. That signal can come from the code.
Clean code has always been important. Now its audience has grown.