fix(review): join the changeset to native-separator graph paths - #646
fix(review): join the changeset to native-separator graph paths#646tiendungdev wants to merge 2 commits into
Conversation
reviewRulepackMatches builds the changed-file key correctly and then
looks it up with filepath.Clean, which rewrites the one separator a graph
path keeps as '/': the repo prefix. Probed on windows/amd64:
changed map key "repo-a/pkg\widget.go"
target.GraphPath "repo-a/pkg\widget.go" <- identical
filepath.Clean(GraphPath) "repo-a\pkg\widget.go" <- prefix slash lost
hit false
So `review` narrowed the rulepack to an empty target set and reported
zero findings on code its own detector bundle flags — the same false
clean the join was written to fix, reintroduced by the Clean.
Compare in graphpath.Norm form on both sides instead. Norm is the
canonical comparison spelling for a path that is "<prefix>/" plus a
native remainder, and it is filepath.ToSlash, so this is the identity on
POSIX.
Second half, same contract: reviewRepoRelPath feeds the `.gortex.yaml`
rule globs, rankFileRisk's row keys and the forge comment API, all of
which speak '/'. It returned `pkg\widget.go` on Windows, which matches no
glob and anchors no comment. Normalize there too.
Whole package on windows before/after: 27 -> 24 failures, exactly the
three rulepack tests flip, nothing else changes state. Each half is
separately load-bearing: reverting the join fails all three, reverting
the output normalization fails the two that assert m.File.
graphpath.Norm is deliberately a no-op on POSIX — a backslash is an
ordinary filename byte there — so no test can bind this on the
linux/macos matrix. The three names join the existing windows
native-separator step, whose package list already carries internal/mcp.
zzet
left a comment
There was a problem hiding this comment.
Requesting changes after an additional path-provenance validation.
Blocking correctness issue at internal/mcp/tools_review.go:700:
With repoPrefix repo-a and a legitimate Git-relative changed path repo-a/pkg/widget.go, normalization produces the same string. The HasPrefix check then treats it as already graph-prefixed, although the correct graph key is repo-a/repo-a/pkg/widget.go. This misses the changed target and, when pkg/widget.go also exists, can scan that unchanged shadow target instead. The ambiguity already exists on POSIX and this change newly exposes the wrong-target behavior on Windows.
Please address the following:
- Carry explicit path provenance for Git-relative versus graph-keyed paths instead of inferring it with HasPrefix.
- Prefix production MapGitDiff paths unconditionally, or otherwise make the input domain explicit.
- Add a regression fixture containing both pkg/widget.go and repo-a/pkg/widget.go, mark only the nested path changed, and assert that only the changed target is scanned.
- Rerun the focused Windows native-separator validation and the existing CI suite.
Adding both possible graph keys is not a safe workaround because it can still select unchanged code.
Addresses the review on zzet#646. reviewChangedGraphPaths inferred "this path is already graph-keyed" from strings.HasPrefix(f, repoPrefix+"/"). The two domains overlap, so that is not recoverable by inspection: in a repo whose tree carries a top-level directory named like the repo prefix, `repo-a/pkg/widget.go` is a valid git-relative path AND a valid graph key for a different file. The inference skips the real key `repo-a/repo-a/pkg/widget.go`, so the changed file is never scanned - and when a same-named `pkg/widget.go` exists, that unchanged shadow is scanned in its place. Make the domain explicit and travel with the data: changedPathsRepoRelative git's spelling, relative to the working tree changedPathsGraphKeyed the graph's "<prefix>/<rel>" node key All four production callers pass DiffResult.ChangedFiles, which MapGitDiff documents as keeping "the diff-relative paths (callers re-join them with git pathspecs)", so they pass changedPathsRepoRelative and the prefix is now applied unconditionally. Only the test covering a caller that already holds node keys passes changedPathsGraphKeyed. The Norm-based join and output normalization from the first revision are unchanged; this only replaces the inference. Regression test: a fixture carrying both pkg/widget.go and repo-a/pkg/widget.go, only the nested path marked changed, asserting every match reports the changed target. Both files carry the detector fixture, so a wrong-target scan still returns matches and only the reported path separates the outcomes - restoring the HasPrefix inference fails it with "pkg/widget.go" is the unchanged shadow. The ci.yml selector hunk is dropped: zzet#652 removes that job outright.
|
You were right, and the failure mode reproduces exactly as you described. Pushed I verified the claim before changing anything: restoring the — the changed 1 + 2 — explicit provenance, unconditional prefixingThe domain now travels with the data instead of being recovered from it: changedPathsRepoRelative // git's spelling, relative to the working tree
changedPathsGraphKeyed // the graph's "<prefix>/<rel>" node key
On "prefix production MapGitDiff paths unconditionally" — that is now what happens, and the contract already supported it. All four production callers ( 3 — regression fixture
Both files carry the detector fixture deliberately — otherwise a wrong-target scan would return empty and the test would pass for the wrong reason. With both seeded, the wrong target still produces matches and only the reported path separates the outcomes. 4 — validation
One thing I removedThe Also worth flagging since it is upstream of this: the ambiguity is in the data model, not only in this function. |
Problem
reviewRulepackMatchesbuilds the changed-file key correctly, then looks it up withfilepath.Clean— which rewrites the one separator a graph path deliberately keeps as/: the repo prefix.Probed on windows/amd64 with the existing fixture:
The narrowing therefore produced an empty target set and
reviewreported zero findings on code its own detector bundle flags underanalyze --kind review. That is the same false cleanreviewChangedGraphPathswas written to fix — its doc-comment says so — reintroduced one line later by theClean.Change
Two halves of one contract, both in
tools_review.go:The join. Compare in
graphpath.Normform on both sides.Normis the canonical comparison spelling for a path shaped"<prefix>/"+ native remainder, and it isfilepath.ToSlash, so this is the identity on POSIX.The output.
reviewRepoRelPathfeeds the.gortex.yamlrule globs,rankFileRisk's row keys, and the forge comment API — the function's own doc-comment lists them, and all three speak/. On Windows it returnedpkg\widget.go, which matches no glob and anchors no comment. Normalize there too.Effect
Whole
internal/mcppackage on windows/amd64, go1.26.6,-count=1:main(c982cf52)Diffing failing test names: exactly three flip, newly-broken set empty.
Verification
Each half sabotage-verified separately, because a fix that only looks necessary is not:
m.Filefail onNot equal.golangci-lint run ./internal/mcp/...reports 6staticcheckSA5011 findings; pre-existing, identical with this change stashed.git diff --checkclean.Why there is no cross-platform test
graphpath.Normisfilepath.ToSlash, deliberately a no-op on POSIX — the package doc is explicit that a backslash is an ordinary filename byte there and must survive. So a test feeding a backslash path and asserting a slash result would fail on linux/macos, and one built withfilepath.FromSlashasserts nothing on either. The three names therefore join the existing windows native-separator step, whose package list already carriesinternal/mcp— no new step, no new compile.Declared, not swept under
TestReviewPackNeverClaimsNoTestSymbolsWithTestTargetsfails on windows too and I deliberately left it out: it fails ontest_targetsbeing empty, not on the path join, andtestpath.IsTestFilealready normalizes separators itself — so it is a different cause and belongs in its own change rather than riding along here.Branched from
mainatc982cf52. Windows 11, go1.26.6.