fix(mcp): keep path anchoring when the server has no multiIndexer - #645
Merged
zzet merged 1 commit intoAug 21, 2026
Merged
Conversation
resolveSymbolID returned early on a nil multiIndexer, before the
graphRelID rung. Only the cwd rung needs the multi-repo index — it maps a
directory to a repo prefix — so the early return also discarded the path
anchoring that works without one.
Measured on windows/amd64 with the move/inline fixture, which builds its
Server through NewServer(...) with no MultiRepoOptions:
multiIndexer == nil: true
GetNode("pkga/a.go::Foo"): false
resolveSymbolID("pkga/a.go::Foo") "pkga/a.go::Foo" <- unchanged
graphRelID("pkga/a.go::Foo") "pkga\a.go::Foo"
GetNode(graphRelID): true <- resolves
graphRelPath/graphPathSpelling already reconcile the spelling, and their
doc-comment names this exact failure: a forward-slash path is "the
spelling every agent writes", and without the rewrite it "missed every
node below the repo root on Windows". The rung was simply unreachable
for a server built this way.
Scope the guard to the cwd rung. The change is additive: the graphRelID
result is still gated on GetNode(rel) != nil, so it can only turn a miss
into a hit, never redirect a resolvable id.
Whole package on windows before/after: 40 -> 27 failures. Diffing the
failing test names, exactly the 13 TestMoveSymbol_* / TestInlineSymbol_*
cases flip and nothing else changes state.
The regression test uses an absolute-path id rather than the Windows
separator, so it exercises the rung on every platform and the
linux/macos matrix protects the fix without a new windows CI step.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Root cause
resolveSymbolIDhas three rungs: exact hit, cwd-prefix, thengraphRelID. ThemultiIndexer == nilearly return sits above the last two — but only the cwd rung actually needs the multi-repo index (it maps a directory to a repo prefix).graphRelIDdoes not, and it was being skipped for free.Probed on windows/amd64 with the move/inline fixture, which builds its
ServerthroughNewServer(...)with noMultiRepoOptions:The machinery was already there and already deliberate —
graphPathSpelling's doc-comment names this exact failure:The rung was simply unreachable for a server built without
MultiRepoOptions.Change
Scope the guard to the cwd rung. Additive by construction: the
graphRelIDresult stays gated onGetNode(rel) != nil, so it can only turn a miss into a hit — it can never redirect an id that already resolves, and rung 1 still short-circuits an exact match before anything else runs.Effect
Whole
internal/mcppackage on windows/amd64, go1.26.6,-count=1:main(b8b13ca7)Diffing the failing test names rather than the counts: exactly 13 flip, and the newly-broken set is empty.
Test
TestResolveSymbolID_WithoutMultiIndexer_StillAnchorsThePathuses an absolute-path id rather than the Windows separator. That exercises the same rung on every platform, so the linux/macos matrix protects this fix and no new windows CI step is needed.Sabotage-verified: restoring the early return fails both the new unit test (on the anchoring assertion) and
TestMoveSymbol_SamePackage_FunctionRelocated; reverting the sabotage returns both to green.Scope of the user-visible impact — stated narrowly on purpose
The real daemon is not affected:
internal/serverstack/shared_server.go:620passesmultiOpts.... The production call sites that build aServerwithoutMultiRepoOptionsarecmd/gortex/eval_recall.go:247andcmd/gortex/eval_server.go:71. So on Windows this cost theevalsurfaces their path anchoring, and it cost the test suite 13 tests. I would rather say that plainly than sell it as a broken daemon.Verification
golangci-lint run ./internal/mcp/...reports 6staticcheckSA5011 findings — pre-existing, byte-identical with this change stashed. Untouched here.git diff --checkclean.Branched from
mainatb8b13ca7. Windows 11, go1.26.6.Related, not included: the remaining 27 windows failures in this package split into symlink-privilege cases (needs
SeCreateSymbolicLinkPrivilege, environment rather than code) and further POSIX-spelled assertions, e.g.TestPathToFileURI_Absoluteexpectingfile:///work/main.gowhere Windows resolvesfile:///D:/work/main.go. Happy to take those separately if they are worth having.