Repository navigation
fix(explore): every dynamic-dispatch link gets its own line, the ones the query names first - #2748
Merged
Merged
Conversation
… the query names first codegraph_explore's "Dynamic-dispatch links among your symbols" section deduped its lines by `source name > target name`. Every interface → implementation link of one method reads the same way (`TrySet → TrySet`), so an interface method with several implementations printed one line, and which implementation it kept was edge order, not the query. On gin, `tryToSetValue setter.TrySet multipartRequest.TrySet setByForm` printed only `TrySet → TrySet @binding/header.go:35` and hid formSource's link. Lines are now deduped by the edge's endpoints (node ids), and the 6-line cap stays. When more links exist than fit: - a link that reaches a test or example file through an end the query didn't name goes last, as explore's answer leaves those files out; the predicate is explore's own `isLowValue`, moved to module scope so both uses share it; - otherwise links go by how pointedly the query named their two ends. A symbol a token picks out (at most three definitions, the same notion as the boundary scan's agent-specific symbols) or a named non-callable endpoint weighs 3, a candidate of an ambiguous token 1, an unnamed symbol 0. An implementation the query names always comes before one it doesn't. The weights also keep an ambiguous token's candidates from crowding out a precise one: prometheus's `Discovery.refresh` matches a `Discovery` in each discovery package, and in `Discovery.refresh fetchApps Decoder.Decode` their links to each other filled the cap and hid the one link into `fetchApps`; - edge order breaks ties. The section takes the rendered Flow chain itself. It leaves out an edge between two of the chain's nodes, as before, and also a line that reads the same as one of the chain's synthesized hops or as a line already listed. gin declares its `Binding` and `StructValidator` interfaces once per build tag (binding.go, binding_nomsgpack.go), so the twin declaration's link printed exactly like the Flow's hop. A same-name link names each end by its owner, `setter.TrySet → formSource.TrySet`, the way an agent writes it in a query. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 21, 2026
Merged
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.
Summary
codegraph_explore's Dynamic-dispatch links among your symbols section deduped its lines bysource name > target name. Every interface → implementation link of one method reads the same way (TrySet → TrySet), so an interface method with several implementations printed one line, and which implementation survived was edge order, not the query.On current main (e450ae3), gin's
tryToSetValue setter.TrySet multipartRequest.TrySet setByFormprints exactly that:formSource's link is hidden. With this PR (multipartRequest is on the Flow since #2453):
What changed (
collectSynthLinksinsrc/mcp/tools.ts)skipInChain's rule; the function now takes the rendered chain itself). Also left out: a line that reads the same as one of the chain's synthesized hops, or as a line already listed. gin declaresBindingandStructValidatoronce per build tag (binding.go,binding_nomsgpack.go), so the twin declaration's link printed exactly like the Flow's hop.A link that reaches a test or example file through an end the query didn't name goes last. Explore's answer leaves those files out (fix(explore): tests and examples the engine knows stay out of explore's answer #2557), and the predicate is explore's own
isLowValue, moved to module scope so both uses share it.Otherwise, links go by how pointedly the query named their two ends:
uniqueNamedNodeIds, the boundary scan's "agent-specific" set), or a named non-callable endpoint;An implementation the query names always comes before one it doesn't.
Edge order breaks ties.
setter.TrySet → formSource.TrySet, with the sameOwner.memberderivation as the completeness note'sfollowUpName. A bareTrySet → TrySetnames neither the interface nor the implementation.Calls I made beyond the original ask (each in its own commit before the squash, each pinned by a test)
Queryable.Querierlisted 4 test doubles (estimate_test.go,federate_test.go,fanout_test.go,estimate_resource_test.go) and cut tsdb'sDB.Querier. That would bring back what fix(explore): tests and examples the engine knows stay out of explore's answer #2557 keeps out of explore's answer.Discovery.refresh fetchApps Decoder.Decode,Discovery.refreshmatches aDiscoverytype in each discovery package. Their links to each other filled the cap and hid the one link intofetchApps, which the query names exactly. Across 1,004 sweep queries, the weights changed 2 outputs, and both are improvements (see below).Validation
All runs are on current main e450ae3. Both arms read one index per repo (built with the native kernel), since the change is query-time only. Base = main's build, fix = this branch. Kit:
D:\cgwork\synlinks.Regression tests:
__tests__/explore-synth-link-endpoints.test.ts(5 tests; TS and Go fixtures)tools.tsSweep: generated queries over gin and prometheus
Query shapes, built from the graph's interface → impl links (X = an interface method with 2+ implementations, I = the one implementation the query names):
X I: no Flow.P X I C: C is called by another implementation, so the Flow bypasses I.P X I C: fix(explore): a Flow through an interface goes through the implementation the query names #2453's shape, with the Flow through I.Engine → Engine @binding/default_validator.go:88, a repeat of the Flow's own hop. The fix drops it and with it the now-empty section; the flag is the extra blank line that leaves.SDConfig.NewDiscoverer. The token keeps 6 of ~20SDConfigtypes and aws's isn't among them: token resolution, upstream of this section.StructValidator, two from prometheus's secondTSDBAdminStatsinterface (web/api/testhelpers/api.gobesideweb/api/v1/api.go); the fix drops all 5. The fix's own 2 are a different interface (histogramAppendable) reaching the implementation the Flow runs through (floatHistogramAppendable's), so they read differently and say something the Flow doesn't.Discovery.refresh fetchApps Decoder.Decode: plain hides the only link intofetchApps; weighted lists it first.Discovery.Run Discoverer.Run Endpoints.Run Discovery.initialize: the namedEndpoints.Runmoves from 4th to 1st.Spot checks (main → fix)
Context.ShouldBindWith Binding.Bind formBinding.Bind mapForm: 1 line (Bind → Bind @protobuf.go:21) → 6 implementations, none repeating the Flow's formBinding hop.Engine.execEvalStmt Queryable.Querier fanout.Querier NewMergeQuerier:Querier → Querier @promql/estimate_test.go:1090(a test double) → 6 links, production first:costSamplingQueryable(tsdb/query_cost.go),sampleAndChunkQueryableClient(storage/remote/read.go),MockQueryable(storage/interface.go),DBReadOnly(tsdb/db.go), and theweb/api/testhelpers/mocks.goones, which aren't test-named files.Discovery.refresh hetzner.refresher.refresh hcloudDiscovery.refresh: 2 → 6 func-field links from the generic refresher. robotDiscovery is not listed in either arm, and no ordering here can list it:hetzner.refresher.refreshresolves to no symbol (see follow-ups), so hetzner'srefresher.refreshis an unnamed bridge on the Flow and its other implementation is not incident to a named symbol.flow.textis charged at the render loop). Totals agree within about 30 characters, and the 6-line cap bounds it.Cost
The Flow builder's median time ratio (fix/main) is 0.99 on gin and 0.97 on prometheus, with p95 1.11 / 1.12. The worst extra is 1.7 ms (9.8 → 11.5 ms on a prometheus query). Each query ran base, fix, fix, base on a shared box.
Suites
Full suite on this commit over main e450ae3, on Windows, with the kernel built,
CODEGRAPH_KERNEL_EXPECT=1and--maxWorkers=4 --minWorkers=1:Follow-ups found
refresh.Discovery.refresh,storage.Queryable.Querier,tsdb.DB.Querier,gin.Context.Nextandrender.JSON.Renderall resolve to no symbol (0 candidates), and so does the two-partbinding.Default, whilerefresh.NewDiscoveryandgin.Newresolve. An agent that qualifies an ambiguous Go type with its package, as it should when the bare name matches many packages, gets nothing for that token. That's token resolution, so it's a separate PR, next.Discovery.Run hcloudDiscovery.refresh(a func field plus an interface) stops atRun. That's a deliberate design choice, Colby's call.🤖 Generated with Claude Code