Skip to content

feat(query): address dotted symbol targets through an indexed tail lookup - #942

Closed
merlincat11 wants to merge 1 commit into
tirth8205:stagingfrom
merlincat11:feat/dotted-symbol-query-targets
Closed

merlincat11 wants to merge 1 commit into
tirth8205:stagingfrom
merlincat11:feat/dotted-symbol-query-targets

Conversation

@merlincat11

Copy link
Copy Markdown

Summary

A dotted symbol target could not be addressed by short name. Details.QueryHandler.Handle,
Handler.Process and pkg.Handler.Process all returned not_found.

The cause is that _looks_like_java_method_fqn() is a purely lexical test, so every
dotted target looks Java-FQN-shaped regardless of which language defines it.
_java_fqn_candidates() then withheld the match from Go, Python, Kotlin, TypeScript and
C# alike. That guard exists to stop a globally unique method name in another language
being selected for a Java FQN, but it was over-blocking far beyond Java.

  • resolve dotted targets against an exact qualified-tail match
  • back that lookup with an indexed nodes.symbol column instead of a table scan
  • keep Java FQN precedence exactly as it is today
  • report honest ambiguity counts past the candidate cap

Split out of #937, which is the C# nested-type identity fix (#934). This is the
query-resolution half; the two are independent and this branch is cut from main.

Java precedence is unchanged

The tail lookup is consulted only when no Java candidate exists. A Java-shaped target
with real Java matches still resolves against Java:

case main this PR
Java com.example.Handler.process ok → Java ok → Java
Java + unrelated Python process ok → Java ok → Java
Java + same-tail Go node ok → Java ok → Java
Java + same-tail C# node ok → Java ok → Java
two Java Handler.process ambiguous ambiguous
Go Handler.Process / pkg.Handler.Process not_found ok
Python / Kotlin / TypeScript Handler.process not_found ok
C# Details.QueryHandler.Handle not_found ok

Every Java row is identical to main. The only behavior change is that a dotted target
with an exact qualified-path match now resolves where main returned not_found. An
exact match on the entire symbol path is different in kind from the fuzzy
globally-unique-name fallback the guard was written to block, and cannot mislink.

Indexed lookup

Matching with substr(qualified_name, -n) = ? cannot use any B-tree index; SQLite planned
it as SCAN nodes on every dotted lookup, plus a second scan for the ambiguity count.

Nodes now carry an indexed symbol column holding the portion of qualified_name after
the first ::. symbol is stored rather than reconstructed from parent_name || '.' || name
because qualified_name is built from identity_name or name, so the reconstruction would
miss C++ overload identities.

before:  SCAN nodes
after:   SEARCH nodes USING INDEX idx_nodes_symbol (symbol=?)
         SEARCH nodes USING INDEX sqlite_autoindex_nodes_1 (qualified_name=?)

Migration v10 backfills and indexes the column. The index is created in the migration,
not the schema script
: _init_schema runs before run_migrations, and
CREATE TABLE IF NOT EXISTS cannot add a column to an existing table, so indexing symbol
from the script raised no such column: symbol and every pre-v10 database failed to open.
SUPPORTED_SCHEMA_VERSION in the VS Code backend moves to 10 to match.

Merge note: #939 also defines a _migrate_v10. Whichever lands second must renumber
to v11; run_migrations() iterates sorted(MIGRATIONS) and tolerates the gap.

Bounded-result honesty

candidate_count used len(candidates) for Java-shaped targets, so 101 exact matches
reported matches 100 node(s) with candidates_truncated: false — both wrong, on fields
that exist for agent-facing transparency. It now counts the population candidates were
drawn from.

Testing

  • uv run pytest tests/ --tb=short -q (2996 passed, 9 skipped, 2 xpassed)
  • cross-language dotted-target matrix (Go, Python, Kotlin, TypeScript, C#)
  • Java precedence: a same-tail C# node does not displace a real Java match
  • truncation count at _MAX_DOTTED_TARGET_CANDIDATES + 1
  • v10 upgrade of a database with the symbol column stripped, reproducing a genuine pre-v10 graph
  • EXPLAIN QUERY PLAN asserts indexed SEARCH for both lookups
  • uv run ruff check code_review_graph/
  • uv run mypy code_review_graph/ --ignore-missing-imports --no-strict-optional

…okup

A dotted target such as Details.QueryHandler.Handle, Handler.Process or
pkg.Handler.Process could not be resolved by short name. _looks_like_java_method_fqn
is a purely lexical test, so every dotted target looks Java-shaped whatever
language defines it, and _java_fqn_candidates then withheld the match from Go,
Python, Kotlin, TypeScript and C# alike.

Resolve dotted targets against an exact qualified-tail match, consulted only
when no Java candidate exists. A Java-shaped target with real Java matches still
resolves against Java, so the established anti-mislink contract is unchanged;
an exact match on the whole symbol path is not the fuzzy globally-unique-name
fallback that guard was written to block.

The lookup is backed by an indexed nodes.symbol column holding the portion of
qualified_name after the first "::". Matching with substr(qualified_name, -n)
instead planned as SCAN nodes on every dotted lookup, with a second scan for
the ambiguity count. symbol is stored rather than reconstructed from
parent_name and name because qualified_name is built from identity_name or
name, so the reconstruction would miss C++ overload identities.

Migration v10 backfills and indexes the column. The index is created there
rather than in the schema script: _init_schema runs before run_migrations and
CREATE TABLE IF NOT EXISTS cannot add a column to an existing table, so
indexing symbol from the script would fail to open every pre-v10 database.

candidate_count now counts the population candidates were drawn from, so more
than _MAX_DOTTED_TARGET_CANDIDATES matches no longer report the capped slice
with candidates_truncated false.
@github-actions

Copy link
Copy Markdown

code-review-graph review

Overall risk: 0.85 (CRITICAL) — 18 changed function(s)/class(es), 24 affected flow(s), 7 test gap(s)

Risk-scored changes

Risk Level Symbol Location Tested
0.85 critical code-review-graph-vscode/src/backend/sqlite.ts::SqliteReader.checkSchemaCompatibility code-review-graph-vscode/src/backend/sqlite.ts:194 no
0.80 high code_review_graph/tools/query.py::_java_fqn_candidates code_review_graph/tools/query.py:66 no
0.75 high code_review_graph/tools/query.py::query_graph code_review_graph/tools/query.py:251 yes
0.55 medium code_review_graph/migrations.py::_migrate_v10 code_review_graph/migrations.py:241 no
0.40 medium code_review_graph/graph.py::GraphStore.search_nodes_by_qualified_tail code_review_graph/graph.py:1231 yes
0.35 low code_review_graph/graph.py::_symbol_of code_review_graph/graph.py:57 no
0.35 low code_review_graph/graph.py::GraphStore.count_nodes_by_qualified_tail code_review_graph/graph.py:1246 yes
0.30 low code_review_graph/graph.py::GraphStore code_review_graph/graph.py:202 yes
0.30 low code_review_graph/graph.py::GraphStore.upsert_node code_review_graph/graph.py:248 yes
0.25 low code-review-graph-vscode/src/backend/sqlite.ts::SqliteReader code-review-graph-vscode/src/backend/sqlite.ts:160 no

Affected execution flows

  • activate — criticality 0.76, 37 node(s) across 8 file(s)
  • registerBlastRadiusCommand — criticality 0.65, 12 node(s) across 3 file(s)
  • registerReviewCommand — criticality 0.64, 12 node(s) across 3 file(s)
  • registerNavigationCommands — criticality 0.63, 11 node(s) across 3 file(s)
  • run — criticality 0.61, 14 node(s) across 3 file(s)
  • ...and 19 more affected flow(s)

Test gaps

  • code-review-graph-vscode/src/backend/sqlite.ts::SqliteReader (code-review-graph-vscode/src/backend/sqlite.ts:160)
  • code-review-graph-vscode/src/backend/sqlite.ts::SqliteReader.checkSchemaCompatibility (code-review-graph-vscode/src/backend/sqlite.ts:194)
  • code_review_graph/graph.py::_symbol_of (code_review_graph/graph.py:57)
  • code_review_graph/migrations.py::_migrate_v10 (code_review_graph/migrations.py:241)
  • code_review_graph/tools/query.py::_java_fqn_candidates (code_review_graph/tools/query.py:66)
  • ...and 2 more without direct tests

Token savings: this graph-backed report used ~33,190 fewer tokens (~64%) than reading every changed file in full (estimated, chars/4 approximation).


Powered by code-review-graph — local-first analysis; no code leaves the CI runner.

@tirth8205

Copy link
Copy Markdown
Owner

Integrated on integration/token-efficiency-hardening as fe9a554, 78a2f9e. Exact dotted symbol tails now use an indexed lookup, retaining disambiguation and existing Java precedence. Legacy-schema and cross-language regressions passed, and the integrated bulk writer from #858 maintains the new symbol column.

@tirth8205

Copy link
Copy Markdown
Owner

Merged into staging through the integration PR #988 (merge commit 3030ca8). Your change is carried by commit fe9a554, which keeps you as the author (MerlinH). It will be promoted staging → testing → main and released from there. Thank you for the contribution! Closing this PR since its content is now on staging.

@tirth8205 tirth8205 closed this Sep 15, 2026
Bakul2006 pushed a commit to Bakul2006/code-review-graph that referenced this pull request Sep 15, 2026
…s through an indexed tail lookup

Port the reviewed token-efficiency implementation with targeted regression coverage.

Source-PR: tirth8205#942
Maintainer corrections and scope extraction applied where needed.
Bakul2006 pushed a commit to Bakul2006/code-review-graph that referenced this pull request Sep 15, 2026
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.

3 participants