fix(mcp): scope detect_changes seed detection to changed line ranges - #1372
Conversation
detect_collect_seeds treated every definition in a changed file as a BFS seed, regardless of which lines actually changed. A one-line edit inside a single method seeded every other definition in the file too, producing an impact report an order of magnitude larger than what the edit actually touched. Fetch `git diff --unified=0` hunks (cbm_parse_hunks already existed in pass_gitdiff.c but had no caller) alongside the existing file list, and scope seeds to definitions whose line range overlaps a hunk. Any failure fetching hunks (new/untracked files, a transient git error) falls back to the previous whole-file behavior, so this is a precision improvement with no new failure mode. Verified on microsoft/qlib (an unrelated third-party repo): a same- line-count edit inside one method now seeds 2 symbols (the method + its containing class) instead of 25 (every definition in the 24-def file), matching the equivalent output of a different code-graph tool (GitNexus) on the identical scenario. Reproduced on a second file with a different definition count (42 defs -> seeds 43 before the fix, 2 after) to rule out coincidence. cbm_changed_hunk_t / cbm_parse_hunks move from pipeline_internal.h (explicitly not a public header) to pipeline.h, since detect_changes now needs them from src/mcp/mcp.c. cbm_detect_node_in_hunks is exposed non-static via mcp_internal.h, matching this file's existing white-box test-hook pattern, so the overlap logic has a direct unit test independent of the git/subprocess/index plumbing around it. Fixes DeusData#1363 Signed-off-by: lishixiang <lishixiang@gmail.com>
|
Thanks for opening this — it has been seen, and it is queued. This note is automated, but it is not a brush-off: it exists so you know where your PR stands instead of having to guess from silence. Current review status: working through a backlog. What that means for this PR, concretely:
Things that will genuinely speed it up whenever review does happen:
If this fixes a bug, a reproduction we can run is worth more than a description of the symptom. Thanks for contributing, and sorry in advance for the wait. |
|
Reviewed in full, and this is a good find. The bug is real and I verified it on main: On the direction: narrowing to changed line ranges is noise removal rather than a findability cut, which is the important question for us — the seeds you drop are definitions in a changed file whose lines did not change. Deletion safety is handled correctly too: One CI red is genuinely yours, and it is a small fix. The Windows shard fails at Two recall edge cases I would like closed before merge, both inside the shape you already have:
And one worth a comment rather than a fix: the coordinate systems are mixed — Security review is clean: no new dependencies, no CI changes, Ping me when the fixture and the two edge cases are in and I will pick it straight back up. |
Three items from the DeusData#1372 review. The Windows shard failed in the fixture, not the fix: `system("cd '<dir>' && git init …")` used POSIX single quotes, which cmd.exe cannot parse. Switched to the portable `git -C "<dir>"` shape already used by wt_git in tests/test_watcher.c, with identity, default branch and signing passed via -c so the fixture no longer depends on the machine's global git config. A changed file with hunks but no overlapping definition now keeps whole-file seeding instead of dropping out. Import-only edits, module-level constants and anything above the first definition land outside every definition's line range, so scoping alone would have removed the file from the seed set entirely — strictly worse recall than the behavior being replaced. The overlap probe has to apply the same label filter as the seeding loop: a Module node spans lines 1..EOF, so probing raw nodes reports an overlap for every hunk and defeats the fallback. Extracting detect_is_seedable_label lets both sites share one definition. The new regression test caught exactly this — it failed with seed_symbols 0 until the probe was filtered. A filled hunk buffer is now treated as a truncated diff: hunks past HUNK_CAP are gone, so files captured only partially would still look scoped and silently under-seed. Scoping is dropped for the whole request and the decision logged, rather than under-reporting a large refactor. Documented the mixed coordinate systems in the fetch block: base...HEAD hunks are HEAD-side lines, worktree hunks are worktree-side, node lines come from the indexed snapshot. They agree while the index is fresh; a stale index plus earlier-in-file insertions can mis-scope, bounded by the zero-overlap fallback. Signed-off-by: lishixiang <lishixiang@gmail.com>
|
Thanks — the two edge cases were both real, and the first one was sharper than I Windows fixture. Switched to the Zero-overlap fallback — and a bug in my first attempt at it. Implemented as Worth recording that the regression test is what caught it: it asserted HUNK_CAP truncation. Coordinate systems. Documented in the fetch block rather than changing Local: 6792 passed / 0 failed, |
|
Merged as The bug you caught in your own fix is the part worth dwelling on. I confirmed it independently: The fix is also structurally right, not just correct: the probe and the seeding loop now call the same The other two hold up too. The Windows fixture passing identity, Two small things for whenever you are next in this code — neither blocking, neither worth a PR on its own: A comment overstates a bound. A rationale that does not match. One acknowledged gap: the Thank you — this was a good find made better by a careful second pass on your own work. |
What does this PR do?
Fixes #1363:
detect_changesscoped its BFS seed set to every definitionin a changed file, regardless of which lines actually changed. A one-line
edit inside a single method seeded every other definition in the file too,
producing an impact report an order of magnitude larger than what the edit
actually touched.
cbm_parse_hunks(inpass_gitdiff.c) already existed to parsegit diff --unified=0hunks but had no caller. This PR fetches those hunksalongside the existing changed-file list and scopes seeds to definitions
whose line range overlaps a hunk. Any failure fetching hunks (new/untracked
files, a transient git error) falls back to the previous whole-file
behavior, so this is a precision improvement with no new failure mode.
cbm_changed_hunk_t/cbm_parse_hunksmove frompipeline_internal.h(explicitly not a public header) to
pipeline.h, sincedetect_changes(in
src/mcp/mcp.c) now needs them.cbm_detect_node_in_hunksis exposednon-static via
mcp_internal.h, matching this file's existing white-boxtest-hook pattern, so the overlap logic has a direct unit test independent
of the git/subprocess/index plumbing around it.
Verified against v0.9.1-rc.1 — the RC's tree-format rewrite (this
release's "real
detect_changesimpact reporting") is a different, earlierpart of this same path: it made the BFS traversal itself real instead of a
placeholder, but the seed set feeding that traversal was still whole-file.
I confirmed the bug still reproduces on the
v0.9.1-rc.1release binarybefore opening this PR:
Also verified on
microsoft/qlib(an unrelated third-party repo): asame-line-count edit inside one method now seeds 2 symbols (the method +
its containing class) instead of 25 (every definition in the 24-def file),
matching the equivalent output of a different code-graph tool (GitNexus) on
the identical scenario. Reproduced on a second file with a different
definition count (42 defs -> seeds 43 before the fix, 2 after) to rule out
coincidence.
This branch is rebased onto current
main(on top of v0.9.1-rc.1 plus thesubsequent commits); local
scripts/test.sh SANITIZE=andscripts/lint.sh --ciboth pass clean.Checklist
git commit -s) — required, CI rejectsunsigned commits (DCO, see CONTRIBUTING.md)
make -f Makefile.cbm test)make -f Makefile.cbm lint-ci)