Repository navigation
Conversation
Two problems at the same comparison, one masking the other. The single-candidate confidence penalty compared raw language tags, so a `.tsx` file calling a `.ts` helper counted as cross-language and every ordinary call in a React codebase was scored 0.5 instead of 0.9. Families already exist for exactly this (`sameLanguageFamily`); using them makes 0.5 mean what it says. With that fixed, 0.5 identifies the real problem: a call resolved to a same-named symbol in an unrelated language on no evidence but the name. Cross-family calls are deliberately allowed — FFI bindings, native bridges are real, which is why the language gate covers `references` and `imports` but not `calls`. But a language's own builtins and macros have no definition of their own in the graph to bind to, so the lone same-named foreign symbol won by default: an ExUnit `test` binding to a test runner's fixture in TypeScript, `dataclasses.field` binding to an unrelated Elixir helper. The matcher already recognised these and wrote the verdict into the edge's metadata as a low confidence — and every consumer then believed the edge anyway, so `callers` and `impact` reported it as fact. On a mixed-language repository that was thousands of impossible edges, concentrated on a small set of generically-named symbols whose reported callers were then mostly or entirely fictional. Matches at or below the floor are now declined rather than recorded. The precise strategies are untouched, which is how a genuine binding keeps its edge: it resolves through an import or a qualified name, well above the floor, never through a bare one. Tunable via `CODEGRAPH_CROSS_FAMILY_CALL_FLOOR`, and `=0` restores the old behaviour. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013Bsi9EH64kMisnik4E1gf7
Contributor
|
Took this into a local integration build (current main + the open fixes I could merge cleanly) and ran the full suite with the native kernel: 245 files, 4323 passed, 0 failed. No regression on the codegraph repo's own index after a forced re-index. Looks good to me. |
colbymchenry
added a commit
that referenced
this pull request
Oct 30, 2026
…one candidate is judged by family (#3052) Name matching compared a candidate's raw language tag with the reference's: the single-candidate confidence in matchByExactName and matchFuzzy, and findBestMatch's same-language skip and +50/-80 score. So a call whose name had several definitions in another of C, C++ and Objective-C scored -80 for each and linked to nothing (an Objective-C file's _glfwPlatformSetTls(), a C++ call into Dear ImGui's imgui.h, which reads as C), and the one definition of a name across TSX and TS, or C and C++, was a 0.5 cross-language guess. The single-candidate confidence now compares language families (sameLanguageFamily). Ranking treats C, C++ and Objective-C as one language (sameRankingLanguage), the very language worth a point that only breaks a tie; other families keep the language itself, because ranking them by family moved excalidraw's Store::scheduleCapture calls to the App.tsx method beside them and gave okhttp 83 wrong Java-to-Kotlin Builder picks. Validation: - __tests__/name-match-language-family.test.ts, 8 tests (default and wasm): 6 fail on the base main; the pin that a .ts call keeps its .ts target over a .tsx namesake beside it passes on both; all pass with the fix. 24 related test files (cross-language, React Native, Expo, Fabric, frameworks, mixed TS/JSX, Objective-C, resolution): 746 pass. - A/B, main vs fix: glfw +4 calls from Objective-C into C, imgui +93 from C++ into imgui.h (read as C) and imstb_rectpack.h, all to the definitions meant; excalidraw, okhttp, leveldb and jq gain or lose no edge (43, 9, 97 and 30 unique-name links go from 0.5 to 0.9 with the same targets); flask and gin byte-identical. - Index time, median of 3 interleaved: imgui 7.9 s main, 7.7 s fix; excalidraw 9.2 s main, 9.4 s fix. Reimplements PR #1662 by ferrine. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Owner
|
Thanks for raising this, @ferrine. The cross-family headline landed with #2032, and the raw language-tag comparison you pointed at is fixed in #3052, with you credited in the changelog. We measured ranking every family as one, and it moved correct TypeScript/TSX and Java/Kotlin picks onto wrong ones, so #3052 judges a lone candidate by family and ranks only C, C++ and Objective-C together. Closing this in favor of #3052; it ships in the next release. |
Contributor
Author
|
Cool |
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/cross-language-call-confidence→mainSize: 3 files, +181/−12 · Opt-out:
CODEGRAPH_CROSS_FAMILY_CALL_FLOOR=0Why
Two problems at the same comparison, one masking the other.
1. The single-candidate confidence penalty compares raw language tags. A
.tsxfile calling a.tshelper counts as cross-language and is scored 0.5instead of 0.9 — that is most calls in any React codebase.
sameLanguageFamilyalready exists two functions above for exactly this.
2. With that fixed, 0.5 identifies the real problem. Cross-family
callsare deliberately allowed — FFI bindings and native bridges are real. But a
language's own builtins and macros have no definition in the graph to bind to,
so the lone same-named foreign symbol wins by default. Observed on a
mixed-language repository: an ExUnit
testmacro binding to a test runner'sfixture in TypeScript; Python's
dataclasses.fieldbinding to an unrelatedElixir helper;
apply,max,min,path.The matcher already recognised these and wrote the verdict into the edge as a
low confidence — and every consumer then believed the edge anyway, so
callersand
impactreported it as fact. On that repo it was thousands of impossibleedges, concentrated on a small set of generically-named symbols whose reported
callers were then mostly or entirely fictional. One helper's
impactreturneddozens of affected symbols, nearly all of them in a language that cannot call it.
It is also unstable under indexing scope: indexing more files of one
language moved thousands of these edges elsewhere, because the fallback fires
only when no same-language candidate exists.
Why this is your rule, not a new one
src/resolution/index.tsalready hardcodes exactly this, for one language:This PR generalizes that from a per-language special case to the rule it is an
instance of.
What changed
language tag.
declined rather than recorded.
CODEGRAPH_CROSS_FAMILY_CALL_FLOOR,=0restores the oldbehaviour — same convention as
CODEGRAPH_AMBIGUOUS_NAME_CEILING.Blast radius, checked
strategy (Strategy 1/2); this gate is in name matching (Strategy 3). The
Cross-language type/import gate (RN name collisions)test passes.qualified name (0.85+), never a bare one.
~3,800 to ~100, with no loss of same-language edges and all
tsx↔tsedges retained.
the repo. Every removed
python↔cppedge pointed at C++ test files andheaders via Python builtins (
bytes,iter); none pointed at the actualbindings file. The real bridge was never captured before or after — that is a
separate coverage gap, not a regression from this change.