Skip to content

fix(search): activate dormant vector+graph+BM25 fusion on jrag search#445

Merged
HumanBean17 merged 1 commit into
masterfrom
fix/search-graph-expand-wiring
Jul 12, 2026
Merged

fix(search): activate dormant vector+graph+BM25 fusion on jrag search#445
HumanBean17 merged 1 commit into
masterfrom
fix/search-graph-expand-wiring

Conversation

@HumanBean17

Copy link
Copy Markdown
Owner

Problem

PR #443 added BM25 as a first-class third RRF list in _graph_expand_merge and shipped it with a measured win (MRR 0.30→0.62, recall@1 0.22→0.49 on identifier queries). But the fusion never ran on any user-facing search path — jrag search and the MCP search tool were still pure vector.

Root cause

search_v2 never passed graph_expand=True to run_search. The 3-list fusion lives in _graph_expand_merge, which is gated behind if graph_expand and key == "java" (search_lancedb.py:1080). search_v2's graph param is the graph object (for lexical fallback), not an expand toggle — and it has no graph_expand param at all. Git history confirms mcp_v2.py has zero commits that reference graph_expand; it's been dormant since #409 introduced _graph_expand_merge.

Tests + the eval stayed green only because they invoke run_search(graph_expand=True) directly, never through search_v2 — that was the blind spot. The design spec's "Current" data-flow diagram asserted search_v2 → run_search → graph-expand was already happening; it wasn't.

Fix

  • search_v2 (mcp_v2.py:1052, 1079): pass graph_expand=(table == "java") at both run_search call sites (the primary path and the hybrid→vector-only FTS-missing retry). run_search already guards fusion to the java single-table path and degrades silently when the graph/FTS index is unavailable, so table=all/sql/yaml and airgapped installs are unaffected. search_payload routes through search_v2, so this covers jrag search + MCP search in one place.
  • Tests (tests/mcp/test_mcp_v2.py): two contract tests through search_v2test_search_v2_enables_graph_expand_on_java and ..._through_hybrid_fts_fallback. Negative control: forcing graph_expand=False makes them FAIL, proving they catch the dormancy (this is the guard that was missing).
  • Docs (2026-07-12-hybrid-bm25-rrf-design.md): added a post-merge correction note under the false "Current" diagram. ARCHITECTURE.md / DESIGN.md claims ("BM25 first-class on the primary path") were false before and are now true — no edit needed.

Evidence

  • Search subset: 22 passed (incl. both new guards)
  • Affected layers (mcp + search + read_payloads): 308 passed, 12 skipped
  • Negative control: guard FAILS with graph_expand disabled, PASSES with the fix
  • Full suite: 1535 passed, 38 skipped, 0 failed

Note for reviewers / operators

This makes #443's feature actually run (~+25-30ms/query on the java path, per the eval). Identifier-style queries (OperatorMessageProcessor, DistributionChunkService) are exactly BM25's strength and should improve. If you're diagnosing a ranking regression that appeared around #443's release, reindex clean first — a stale index (or the cocoindex 1.0.7→1.0.16 bump) is the more likely culprit than the fusion, which was dormant until this PR.

🤖 Generated with Claude Code

PR #443 added BM25 as a 3rd RRF list in _graph_expand_merge, but search_v2
never passed graph_expand=True to run_search — so the vector+graph+BM25
fusion was dormant for every user-facing search (jrag search, MCP search).
Tests + eval stayed green only because they invoke run_search(graph_expand=True)
directly, never through search_v2. The design spec's "Current" diagram claimed
graph-expand was already on the live path; it wasn't (false since #409).

- search_v2 (mcp_v2.py): pass graph_expand=(table == "java") at both run_search
  call sites — the primary path and the hybrid->vector FTS-missing retry.
  run_search already guards fusion to the java single-table path and degrades
  silently when the graph/FTS index is unavailable, so table=all/sql/yaml and
  airgapped installs are unaffected. search_payload routes through search_v2,
  so this covers jrag search + the MCP search tool.
- tests: two contract tests through search_v2 (the wiring gap's blind spot).
  Negative control: forcing graph_expand=False makes them FAIL, proving the
  guard is non-vacuous and catches the dormancy.
- docs: correct the design spec's false "Current" diagram with a post-merge
  note so the next implementer doesn't inherit the premise. ARCHITECTURE.md /
  DESIGN.md claims ("BM25 first-class on primary path") were false before and
  are now true — no edit needed.

Full suite: 1535 passed, 38 skipped, 0 failed.

Co-Authored-By: Claude <noreply@anthropic.com>
@HumanBean17 HumanBean17 merged commit 3d97fe3 into master Jul 12, 2026
10 of 12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant