feat(query): surface bridge nodes reached from two or more seeds - #3285
feat(query): surface bridge nodes reached from two or more seeds#3285andytsai821201-spec wants to merge 1 commit into
Conversation
A multi-term question ("AuthModule BillingDB", "what links X to Y") seats
several seeds, and the node their traversals share is usually the answer.
Today it is rendered as just another depth-1 neighbor — and under a tight
`--budget` it loses to a same-layer hub with more edges, so the connective
node is cut while the incidental hub survives. `path` covers the exact
two-endpoint case, but nothing covers a natural-language question that
resolves to several seeds.
Add `_bridge_nodes`: walk each seed separately inside the node set the
traversal already found (both edge directions, same hop rule as the layering
in `_subgraph_to_text`), and return the non-seed nodes reached from two or
more seeds, ordered by seeds-reached then id. It never adds a node the
traversal did not find, so the answer's node set is unchanged.
`_query_graph_text` names them in the header (`Bridges: [...]`) and passes
them to `_subgraph_to_text`, which renders them directly after the seed
block ahead of the hop/degree-ordered rest. No new flag, no LLM, no extra
scoring pass.
Single-seed questions are byte-identical: no bridges, no header entry,
unchanged ordering.
Tests: `_bridge_nodes` (two seeds -> connector; single seed -> empty; a
seed is never reported as a bridge), renderer places bridges after seeds,
end to end header + survival under a budget that cuts the hub, and the
single-seed no-op guard.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
There was a problem hiding this comment.
Graphify reviewed this change.
Looks safe to merge — no coupling regressions and no blocking issues, checked against the code graph (not a self-assessment).
Formal verification. 2 change(s) tested, no difference found (not proven).
Graphify review — findings
Adds bridge-node detection to graph queries: _bridge_nodes walks each seed's traversal within the already-visited node set and returns the non-seed nodes reached from two or more seeds, ordered by seed-count then id. Multi-term questions now name these connective nodes in a new Bridges: header line and render them in _subgraph_to_text immediately after the seed block, so the node linking the queried entities survives truncation ahead of same-layer hubs. Single-seed or fewer queries get no bridges and the ordering is unchanged.
No blocking issues surfaced. 2 lower-confidence candidates did not survive cross-model review.
Analysis details — impact, health, verification
Impact & health
Graphify review
Impact — 584 functions depend on the 342 functions this change touches.
Health — this change adds coupling hotspots:
- new:
dispatch_command()— 2 callers, 123 callees - new:
_query_graph_text()— 22 callers, 10 callees - new:
_score_query()— 15 callers, 5 callees - new:
_query_terms()— 20 callers, 3 callees - new:
run_benchmark()— 16 callers, 3 callees - new:
_build_server()— 2 callers, 16 callees - new:
_load_graph()— 9 callers, 3 callees - new:
_query_subgraph_tokens()— 7 callers, 3 callees - …and 8 more — each is listed as a finding
Verification — 584 functions in the blast radius were not formally verified this run (proofs are advisory here).
Gate & verification
graphify gate
PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.
Advisory (not blocking):
- verification_scope: 409 function(s) in the blast radius were not formally verified this run
Formal verification
No difference found (not proven): No behavior difference found in \_query\_graph\_text (not a proof).
The verifier ran both versions of \_query\_graph\_text on many inputs and saw identical behavior every time. Strong evidence the change is safe, but evidence, not a proof.
Guarantee: Empirical: differential testing (both versions run on many generated inputs). A divergence on an untested input remains possible, so this is 'no counterexample found', not 'proven equivalent'.
Note: An input the sampler did not try could still differ.
No difference found (not proven): No behavior difference found in \_subgraph\_to\_text (not a proof).
The verifier ran both versions of \_subgraph\_to\_text on many inputs and saw identical behavior every time. Strong evidence the change is safe, but evidence, not a proof.
Guarantee: Empirical: differential testing (both versions run on many generated inputs). A divergence on an untested input remains possible, so this is 'no counterexample found', not 'proven equivalent'.
Note: An input the sampler did not try could still differ.
· 16 more finding(s) on lines outside this diff (see the check run).
Summary
A multi-term question seats several seeds; the node their traversals share is usually what the question is about — yet it renders as an ordinary depth-1 neighbor and, under a tight
--budget, loses to a same-layer hub. This adds_bridge_nodes(non-seed nodes reached from ≥2 seeds), names them in the header, and renders them right after the seed block. ~60 lines, no new flag, no LLM, node set unchanged.Problem
Graph:
AuthModule — SessionStore — BillingDB, plusLogger(a hub offAuthModulewith eight leaves) andTokenCacheoffAuthModule. Query"AuthModule BillingDB"seats both entities as seeds. Today at--budget 70:SessionStoreis the only node connecting the two things the question named, and it is the one dropped.pathhandles the exact two-endpoint case, but not a natural-language question that resolves to several seeds.Fix
_bridge_nodes(G, nodes, seeds, depth): walk each seed separately inside the node set the traversal already found (both edge directions, same hop rule as the layering in_subgraph_to_text); return non-seed nodes reached from ≥2 seeds, ordered by seeds-reached then id. It never adds a node the traversal did not find._query_graph_text: header gainsBridges: [labels]when any exist; bridges are passed to the renderer._subgraph_to_text(..., bridges=None): bridges render directly after the seed block; everything else keeps today's hop/degree order.After:
Byte-identical for single-seed questions: fewer than two seeds ⇒ no bridges, no header entry, unchanged ordering. A seed is never reported as a bridge.
Tests
_bridge_nodes: two seeds → the connector; single seed →[]; adjacent seeds never report each other.bridges=places them immediately after the seeds.SessionStore, and it survives a budget that cuts the hub.Bridges.All watched failing first (ImportError / unexpected kwarg / header without
Bridges).tests/test_serve.py: 152 passed. Full suite: 5,170 passed; the 15 failures present (test_ollama_retry_cap.pymissingopenai,test_skillgen.pyaudit baselines) reproduce identically on pristinev8in this environment.Relation to existing work
Complementary to #1783 (external
--seed-file: bridges apply to externally supplied seed groups too) and to #1184's ask for structured retrieval primitives over the existing graph — "what connects these entities" is one such primitive. Independent of #3284 (same-layer relevance ordering); the two compose but neither depends on the other.🤖 Generated with Claude Code