fix(node): interleave same-named definitions before truncating - #1657
Open
ferrine wants to merge 2 commits into
Open
fix(node): interleave same-named definitions before truncating#1657ferrine wants to merge 2 commits into
ferrine wants to merge 2 commits into
Conversation
`callers`, `callees` and `impact` each carried their own symbol filter,
comparing the user's query against the BARE node name only:
node.name === symbol || node.name.endsWith('.' + symbol)
That fails in two opposite directions in the same repository.
A bare name OVER-reports. Every same-named definition passes the filter
and their results are unioned under one heading — "Callers of group" can
list callers that belong to an entirely different `group`, with nothing
saying the name was ambiguous. Collisions cluster on short generic names
(`group`, `num`, `parse`), so this bites hardest exactly where the verbs
would otherwise be most useful.
A qualified name UNDER-reports. `Foo.Bar.baz` can never equal a bare
`baz`, so every candidate fails the filter, and the guarded fallback
takes whichever node full-text search ranked first — or reports "not
found" for a symbol that plainly exists. It only ever appeared to work
when FTS happened to return exactly one hit.
All three now resolve through graph/symbol-lookup, which the MCP tools
share, so a verb cannot drift from the matcher again:
- the exact-name index is consulted first and is authoritative. It is
complete and uncapped, whereas FTS ranks, truncates, and tokenises
`::` away — so resolution no longer depends on search ranking. FTS
stays as the fallback for the fuzzy cases it is good at.
- `matchesSymbol` gains a boundary-aligned suffix match under a
canonical separator. Splitting on every separator assumes no scope
component contains one, which is false for any language whose module
names are themselves dotted: the stored `A.B::c` can never equal the
split-and-rejoined `A::B::c`, so a precise query resolved to nothing.
- an ambiguous bare name still aggregates (an interface method and its
overrides are usually all wanted) but the union is now disclosed, with
the matched definitions named and a qualified spelling that narrows
it. `--json` gains a `targets` array and an `ambiguous` flag.
`matchesSymbol` moves out of mcp/tools.ts unchanged apart from the new
stage; the tool path delegates to it, so its existing coverage applies.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013Bsi9EH64kMisnik4E1gf7
`codegraph_node` on an ambiguous bare name renders as many full bodies as fit a char budget, lists up to 20 more by file:line, then summarises the rest as "+N more". The definitions arrive in index order — `(file_path, start_line)` — and nothing reorders them, so a name defined many times under an early-sorting directory consumes the body budget AND the overflow list before a definition under a late-sorting one is reached. That is not a ranking nicety when one name exists in several languages. Measured on a fixture with 40 definitions under `aaa_client/` and one under `zzz_server/`: the lone definition landed past both caps, so the answer named a single language and gave no sign the other existed. The caller cannot tell a truncated answer from a complete one, and "+N more" reads as "nothing you care about" precisely when the tail is the only definition in some language. Round-robin across languages, and within a language across files, keeping original order inside each bucket, before anything truncates. Every source that defines the name is then represented in the first few entries however the paths sort. Inert when there is nothing to interleave, so ordinary single-file overload sets are unaffected. The final "+N more" now names the languages it dropped, so a truncation that does happen is legible rather than silent. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013Bsi9EH64kMisnik4E1gf7
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.
Branch:
ferrine:fix/multi-definition-diversity→mainStacked on:
fix/symbol-lookup-consistency(PR #1) — its commit shows inthis diff until that one merges.
Size: +132 over #1
Why
Looking up a name that is defined many times could hide whole languages and
whole directories, and never say that it had.
Results were ordered by file path and then truncated. So a name defined 40 times
under an early-sorting directory pushed the single definition under a
late-sorting directory past every cap. The answer looked complete, covered one
language, and never mentioned the other existed — which is the worst shape for
an agent, because there is nothing in the response to suggest a second look.
What changed
trimmed, so each source gets representation within the cap instead of the
first-sorting one consuming it.
so a truncated answer is visibly truncated.
Notes for review
Ordering only — no change to which definitions are found, just which survive the
cap and what the caller is told about the rest.