You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix(code-review): base local diff on the branch fork point, not a stale ref (#181)
* fix(code-review): base local diff on the branch fork point, not a stale ref
Local branch review hardcoded its diff scope to `main...HEAD`. That `main`
is the local branch ref, which every worktree of a clone shares, so a
worktree inherits whatever commit the primary checkout last left it on.
Since `A...B` diffs from `merge-base(A, B)`, a lagging local ref drags the
merge base back past the branch's real fork point and folds unrelated
landed commits into the review diff.
Preferring `origin/<base>` unconditionally would only mirror the bug: that
ref lags whenever the base has unpushed local commits, and branching off
those puts the fork point ahead of it. Both merge bases are ancestors of
HEAD along the base branch, so pick the ref producing the later one — it is
the true fork point under either kind of staleness. A remote-less repo has
no second view and keeps its local ref.
The base branch is now detected (origin/HEAD, then main/master) rather than
assumed to be `main`, so master-default repos resolve correctly too, and
fetch-intent reads commit subjects from the same fork point instead of
attributing someone else's landed commit to the branch under review.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* docs(code-review): changelog + README for v3.6.1 diff-base fix
Document the fork-point base selection and default-branch detection. The
README's scope table, --base flag, and examples described the old
hardcoded `main` base; a new Base ref resolution section covers detection
and the later-merge-base rule.
Also corrects README staleness surfaced by the inventory check: the
architecture tree omitted the skills/ directory, three of four commands,
four of seven reviewer prompts, and scripts/, and the verifier-stats
section named an output artifact as GitHub mode's presenter.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(code-review): address review findings on the diff-base fix
Three findings from the cr-64914 review, all confirmed against the code:
- _resolve_default_base_ref's docstring named a `compute-cache-keys`
subcommand that does not exist; the consumer that origin-qualifies
base_ref is `compute-hashes` (cmd_compute_hashes).
- /start's help hardcoded the base as `(origin/main)`, contradicting the
detection and local-ref fallback this same change added.
- The hermetic git fixture runner was reached by importing a private
helper out of prefix_golden_harness. It is shared test env setup used by
two modules now, so it moves to conftest.py as GIT_IDENTITY_ENV +
git_fixture and both modules import it from there. The harness's
deliberate duplication is its independent walk wrapper (the A/B parity
oracle), not its git plumbing, so sharing this does not weaken it — the
seven byte-parity golden fixtures still pass unchanged.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* test(code-review): pin diff-base selection under local/origin divergence
Coverage previously exercised the two staleness kinds separately (stale
local ref; unpushed local commits). When local main and origin/main
diverge, both happen at once: the branch is cut from one of the two tips,
and _base_rev must pick that tip. Two complementary tests — branch off the
local tip, branch off the remote tip — each pinned by mutation checks: an
always-local base fails the off-remote case, always-origin fails the
off-local case, only the per-run later-merge-base selection passes both.
The v3.6.1 changelog bullet gains one clause stating the divergence case
is handled, since that is a real property of the shipped selection.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: CHANGELOG.md
+7Lines changed: 7 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -4,6 +4,13 @@ All notable changes to the claude-plugins project will be documented in this fil
4
4
5
5
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). Entries are listed newest-first; each plugin section is treated as released when merged to `main`.
6
6
7
+
### code-review v3.6.1
8
+
9
+
#### Fixed
10
+
- **Local branch review no longer bases its diff on a stale ref, which folded unrelated commits into the review.** `resolve-scope` hardcoded the diff scope to `main...HEAD`. That `main` is the local branch ref, which every worktree of a clone shares, so a worktree inherits whatever commit the primary checkout last left it on. Because `A...B` diffs from `merge-base(A, B)`, a local ref sitting behind the branch's fork point dragged the merge base backwards and pulled every commit that landed on the base in between into the diff — reviewing other people's work as if it were the branch's. The base is now chosen per run between the local ref and `origin/<base>`: both merge bases are ancestors of HEAD along the base branch, so the ref producing the later one is the true fork point. This is correct under either kind of staleness — a local ref behind the fork point, or an `origin/<base>` behind it because the base has unpushed local commits (where preferring the remote ref would fold those commits in instead) — and under both at once, when the local ref and `origin/<base>` have diverged: whichever tip the branch was cut from yields the deeper merge base, so it is selected regardless of which ref that is. A repo with no remote has no second view and keeps its local ref, so remote-less reviews are unchanged.
11
+
- The base branch is now detected — `origin/HEAD`, then `main`/`master` remotely, then locally — rather than assumed to be `main`, so repositories whose default branch is `master` (or any name `origin/HEAD` reports) resolve their scope correctly instead of failing against a nonexistent `main`. `--base-ref-override` runs through the same selection, and falls back to the local branch when the named base has no remote-tracking ref rather than emitting an `origin/<ref>` that git cannot resolve.
12
+
-`fetch-intent` now reads branch commit subjects from the same fork point instead of the raw local base ref, so intent classification and injection detection no longer receive commits that landed on the base branch and were never part of the change under review.
@@ -90,34 +105,36 @@ Runs a comprehensive code review. Invokes the full pipeline: diff parsing, hygie
90
105
91
106
| Argument | Behavior |
92
107
|---|---|
93
-
|_(none)_|Diff current branch vs `main`|
108
+
|_(none)_|Review the open PR's diff for the current branch; with no open PR, diff the current branch from its fork point off the default branch|
94
109
|`staged`| Diff only staged (index) changes |
95
-
|`file1 file2 ...`| Diff specific files against `main`|
110
+
|`file1 file2 ...`| Diff specific files from the fork point off the default branch|
96
111
|`123`| Use PR #123's diff (local output, no posting) |
97
112
113
+
**Base ref resolution.** The default base branch is *detected*, not assumed: the helper reads the `origin/HEAD` symbolic ref, then probes `origin/main` / `origin/master`, then the same names locally, falling back to `main` only when nothing resolves. The diff runs from the fork point rather than a fixed ref — a clone holds both a local `<base>` and an `origin/<base>`, and either can lag the other (a stale local checkout, or unpushed local base commits). The helper takes the merge base of each against `HEAD` and uses whichever ref yields the *later* one, since that is the true fork point. This keeps commits that landed on the base branch after the fork out of the review diff under either kind of staleness.
114
+
98
115
**Mode flags:**
99
116
100
117
| Flag | Description |
101
118
|---|---|
102
119
|`--github`| GitHub CI mode: auto-detect PR from branch or accept explicit PR number, post inline comments via file-based handoff |
103
120
|`--github 123`| GitHub CI mode: review PR #123 specifically |
104
121
|`--hygiene-only`| Run only the deterministic hygiene checks. Zero LLM tokens consumed. Fast. |
105
-
|`--base <ref>`| Override the base branch for diffing (default: `main`) |
122
+
|`--base <ref>`| Override the base branch for diffing (default: the repository's detected default branch) |
106
123
|`--since-last-review`| Review only commits added since the last successful review (local mode only) |
107
124
|`--full-review`| Force a full diff even when auto-incremental mode would narrow the scope |
@@ -309,7 +326,7 @@ Overrides survive across runs while the file content matches and the 90-day TTL
309
326
310
327
**Re-assert is best-effort against finding_id drift.** Finding IDs are assigned as `<reviewer>_f<index>` where `<index>` is the reviewer's emission position. Across re-runs the LLM may reorder or drop findings, so an override written against `bha_f3` on run N may map to a different finding (or no finding) on run N+1. The content-hash anchor prevents promoting an unrelated finding at a different line — but the common drift case is the override silently no-ops. Two mitigations: (1) re-assert and re-run immediately so the override is honored against the same emission set, and (2) inspect the verify-prepare manifest for `override_hits` / `override_invalidated` to confirm the override landed.
311
328
312
-
The presenter (local mode`start.md`, GitHub mode`code-review-verifier-stats.md`) surfaces:
329
+
The presenter (local mode: the `present-local` skill, GitHub mode: `github-review.md` Step 6e, which writes `.closedloop-ai/code-review-verifier-stats.md`) surfaces:
0 commit comments