/code-review defaults to stale local main in git worktrees, causing unbounded/wrong review scope

Status Open
Reported on v2.1.267
Maintainer reply None cached
Activity 0 comments · opened Sep 10, 2026

Environment

  • Claude Code 2.1.267
  • macOS 26.6.2 (arm64)
  • Repo layout: one primary checkout + several git worktree add checkouts (a common pattern for running parallel agent sessions), all sharing the same .git

Summary

The built-in /code-review skill defaults to diffing against the local main branch ref when no fixed point is given. In a git worktree layout this is silently wrong and gets worse over time: local main only exists in the one checkout where it's actually checked out, and nothing in a worktree-based workflow ever touches it, so it never advances. Every subsequent /code-review run in any worktree diffs against an increasingly stale point, producing a review scope that balloons to include unrelated, already-merged history instead of the actual change under review.

Compounding this, the effort level is silently "sticky" (reused from the last invocation, with no confirmation shown before the run starts), so a run that goes wrong at high cost repeats at the same high cost on retry, with no visible warning.

Repro

  1. Set up a repo the way git worktree is meant to be used: one primary clone (where main lives and is occasionally fetched/checked out), and one or more git worktree add ../feature-branch checkouts where all actual work happens.
  2. Do a normal amount of work on main over time (PRs merge, origin/main advances) without ever re-checking-out main in the primary clone. This is the normal case for the pattern above — nobody manually keeps the primary checkout current, because nobody works there.
  3. In a worktree, make some changes and run /code-review with no argument.

Observed in this session:

$ git rev-parse HEAD
0fe7c04d705c79ec82649d3e20471c3c5cb2cbd2
$ git merge-base HEAD main
5c97bef3d4a24faed5f9067faf8509ce48a06755   # local main, ~100 commits stale
$ git fetch origin main && git rev-parse origin/main
ca68339b6a2aa4a6d64e5ee8a09a689db8b829ce
$ git merge-base HEAD origin/main
0fe7c04d705c79ec82649d3e20471c3c5cb2cbd2   # == HEAD's base exactly — this is the correct fixed point

git worktree list for this repo shows exactly why: the primary checkout (where main is actually checked out) sits at an old commit, while every worktree branch has moved on independently:

/path/to/primary-checkout                5c97bef [main]
/path/to/worktrees/eager-iberis          bffdf64 [eager-iberis]
/path/to/worktrees/other-branch          6e56030 [other-branch]
...

Result: two consecutive /code-review runs (no argument) diffed against 5c97bef instead of ca68339/0fe7c04, producing multi-hundred-file diffs dominated by already-shipped, unrelated code — the actual ~450-line change under review was buried in it. Both runs silently ran at max effort (reused from an earlier session), each burning 100k+ subagent tokens over 8–16 minutes, and a third attempt (this time passed origin/main explicitly, which resolved correctly) hit the account's session rate limit before finishing.

Expected

  • The default fixed point should prefer the upstream tracking ref (origin/main, or whatever @{upstream} resolves to) over the bare local branch name — or at minimum, do a quick git fetch / compare local vs. remote-tracking ref and warn/ask when they disagree by a nontrivial number of commits, rather than silently using the stale one.
  • Before running at a reused "sticky" effort level (especially max/ultra), show what level and scope will be used and let the user confirm or override, rather than finding out only in the results message ("Reusing max effort from last time — type /code-review high to change it").

Secondary, related finding: skill name collision

While investigating this, we found a second contributing issue. A community plugin skill (mattpocock-skills:implement) instructs the agent to run "/code-review" as a generic step, expecting a different, plugin-provided mattpocock-skills:code-review skill (which explicitly asks the user for a fixed point rather than defaulting to one — see its SKILL.md). Because the built-in skill is also named exactly code-review with no namespace, invoking the bare name resolves to the built-in one instead, silently overriding the plugin author's intended behavior. There's no error or ambiguity warning — it just runs the other implementation. This seems worth a general policy: either built-in skill names should be reserved/protected from being shadowed this way, or invoking a bare name that collides with a built-in should surface which one actually ran.

Why this matters

This is a "death by a thousand cuts" failure mode for exactly the workflow Claude Code's own worktree tooling encourages (parallel agent sessions each in their own worktree). It doesn't fail loudly — it just quietly reviews the wrong thing, at increasing cost, forever, until someone happens to notice the reported file list doesn't match what they changed.

View original on GitHub ↗