Skip to content

feat: persist precise (LSP) call resolution — pass-2 (stacked on #165) - #167

Merged
dgtalbug merged 2 commits into
mainfrom
feature/precise-resolution
Jun 7, 2026
Merged

feat: persist precise (LSP) call resolution — pass-2 (stacked on #165)#167
dgtalbug merged 2 commits into
mainfrom
feature/precise-resolution

Conversation

@dgtalbug

@dgtalbug dgtalbug commented Jun 7, 2026

Copy link
Copy Markdown
Owner

Summary

Builds pass-2 precise resolution — the persisted half of the LSP tier. dextree
already resolves precise callers/callees via the user's language server, but only
live, per selected node; the result was never written to the edge table, so the
whole projected graph stayed heuristic. This is the depth gap behind "a graph-DB
indexer projects more than us": SCIP/graph-DB tools persist semantic edges. The
edge.target_id schema comment already said "pass-2 LSP fills them in" — this
fills them in.

A new opt-in command, Dextree: Resolve Precise Edges (LSP), runs a
workspace-wide pass: enumerate heuristic/unresolved CALLS sites → ask the LSP at
each source location → match the result back to a stored symbol → persist
target_id + metadata.resolution='precise'. The whole graph then projects
precise.

Stacked on #165 (feature/graph-repository). It extends GraphRepository,
which only exists on that branch — so this PR's base is feature/graph-repository,
and it should merge after #165.

Linked spec / issue

Affected surfaces

  • packages/core (interface + adapter + fake + indexer orchestration + types)
  • packages/extension (command + wiring + Indexer mocks)

Commits

  1. 823e2ed core write pathGraphRepository.getUnresolvedCallSites /
    findSymbolIdAt / persistPreciseEdges in storage/preciseResolution.ts +
    FakeGraphRepository; real-DuckDB tests (list/match/persist, idempotent, no-downgrade)
  2. c138bc1 orchestration + commandIndexer.resolvePreciseEdges(workspaceRoot, resolver, {onProgress, isCancelled}) runs the loop; createResolvePreciseEdgesCommand
    drives it under a cancellable withProgress; registered in activate + package.json;
    integration + command tests

How it stays safe

  • Opt-in — nothing runs unless the command is invoked; the default index is unchanged.
  • Idempotent — precise edges drop out of the work-list; stampResolutionTier already
    guards <> 'precise', so a heuristic re-stamp never downgrades them (proven by test).
  • Graceful — a cold/missing server (resolver returns []) or an unmatched location
    leaves the edge heuristic; the pass reports upgraded-vs-total.
  • Cancellable — a cancelled run persists what it already resolved.
  • Layering — the host owns only the LSP resolver (RULE-ARCH-005); all DB access goes
    through GraphRepository (RULE-ARCH-003/010).

Test plan

  • pnpm check green across all packages: core 282, exporters 161, extension 643
  • Core integration (real in-memory DuckDB): heuristic CALLS edge upgrades to precise;
    re-run shrinks the work-list (idempotent); cold resolver upgrades 0; cancellation
    persists partial
  • Adapter unit tests for the work-list query, file+line matcher, and precise UPDATE
  • Command tests: upgrade count message, empty case (no refresh), cancellation message
  • No-downgrade by a later stampResolutionTier proven by test

Out of scope (follow-ups)

  • Auto-run after index — this ships the explicit command only.
  • SCIP ingest — the other persisted-precise path (scipIngest.ts), not wired here.
  • Non-CALLS kinds — inheritance/implements precise resolution.

🤖 Generated with Claude Code

@coderabbitai

coderabbitai Bot commented Jun 7, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: e9b75237-b109-4910-aa0c-360ade143f52

📥 Commits

Reviewing files that changed from the base of the PR and between c138bc1 and 069d605.

📒 Files selected for processing (17)
  • packages/core/src/index.test.ts
  • packages/core/src/index.ts
  • packages/core/src/resolution/types.ts
  • packages/core/src/storage/adapters/duckdbGraphRepository.ts
  • packages/core/src/storage/fakeGraphRepository.ts
  • packages/core/src/storage/graphRepository.ts
  • packages/core/src/storage/preciseResolution.test.ts
  • packages/core/src/storage/preciseResolution.ts
  • packages/core/src/types.ts
  • packages/extension/package.json
  • packages/extension/src/commands/indexFile.test.ts
  • packages/extension/src/commands/indexWorkspace.test.ts
  • packages/extension/src/commands/resolvePreciseEdges.test.ts
  • packages/extension/src/commands/resolvePreciseEdges.ts
  • packages/extension/src/extension.test.ts
  • packages/extension/src/extension.ts
  • packages/extension/src/watcher/workspaceWatcher.test.ts

📝 Walkthrough

Summary

User-visible / Reviewer-relevant changes:

  • Adds opt-in VS Code command "Dextree: Resolve Precise Edges (LSP)" that upgrades persisted heuristic CALLS edges to precise tier by querying the language server at each unresolved call site, matching results back to stored symbols, and persisting the resolved target_id with metadata.resolution='precise'.
  • Extends Indexer and GraphRepository interfaces with new resolvePreciseEdges and call-site lookup methods; implementations delegate to new storage functions in preciseResolution.ts (query unresolved sites, map LSP locations to stored symbol IDs, persist upgrades in a single transaction).
  • Idempotent workflow: repeated runs are safe (stamping with resolution='precise' prevents downgrades); operation is cancellable and persists work-in-progress.
  • Test coverage includes real in-memory DuckDB round-trip tests validating listing, matching, persisting, idempotency, and cancellation behavior.

Risks:

  • Patch coverage reported at 68.5% with 15 lines missing in fakeGraphRepository.ts (11.76% coverage); while test coverage is otherwise reasonable, the test double may not exercise all paths in production usage.
  • findSymbolIdAt returns null on ambiguous matches (multiple symbols at same line); this degrades gracefully but does not flag the ambiguity to the user, potentially leaving some resolvable edges unresolved.
  • LSP resolver latency and timeout behavior are not specified; slow or unresponsive resolvers could block the UI progress dialog without user-visible feedback on per-site duration.

Deferred validation:

  • Auto-run after indexing is explicitly out of scope; users must manually invoke the command.
  • SCIP ingestion and non-CALLS edge kinds are deferred; only CALLS edges are supported in this pass.
  • Schema-level validation: no explicit check that seeded test symbols or persisted resolutions conform to existing schema constraints (e.g., symbol ID existence).

Package-boundary / schema impact:

  • Adds three new methods to the GraphRepository interface (getUnresolvedCallSites, findSymbolIdAt, persistPreciseEdges); all implementations centralize DB access via the existing held DuckDB connection.
  • Extends Indexer interface with resolvePreciseEdges method and two new option/summary types.
  • New types exported from core: PreciseLocationResolver (caller-supplied), PreciseResolution, UnresolvedCallSite, PreciseResolutionOptions, PreciseResolutionSummary.
  • Database updates: edge.metadata JSON is merged with {"resolution":"precise","confidence":1.0} on upgrade; existing edge.target_id is overwritten; no schema migration required (existing edges can remain unresolved indefinitely).

Walkthrough

Adds a second pass of precise (language-server) call-site resolution to Dextree. Introduces storage contracts, persistence functions, indexer implementation, and a cancellable VS Code command that upgrades persisted heuristic CALLS edges to precise tier using LSP-backed results.

Changes

Precise edge resolution system

Layer / File(s) Summary
Resolution types and contracts
packages/core/src/resolution/types.ts, packages/core/src/storage/graphRepository.ts, packages/core/src/types.ts, packages/core/src/index.ts
UnresolvedCallSite and PreciseResolution types enable multi-pass edge resolution; GraphRepository interface adds three methods to list unresolved edges with source location, map language-server results to symbol IDs, and persist precise edges; Indexer interface adds resolvePreciseEdges with options and summary types.
Precise resolution storage functions
packages/core/src/storage/preciseResolution.ts
getUnresolvedCallSites queries non-precise CALLS edges and derives 0-based source locations from symbol ranges; findSymbolIdAt maps file+line to stored symbol ID via exact range match (returns null for ambiguous/no-match); persistPreciseEdges updates edges with target_id and {"resolution":"precise","confidence":1.0} metadata in single transaction.
Storage integration tests
packages/core/src/storage/preciseResolution.test.ts
In-memory DuckDB validates round-trip: seeds unresolved CALLS edge, verifies getUnresolvedCallSites includes source location, findSymbolIdAt resolves file+line to symbol ID, persistPreciseEdges sets target_id and precise tier, and heuristic pass-2 workflow preserves edge for later upgrade.
Repository adapters
packages/core/src/storage/fakeGraphRepository.ts, packages/core/src/storage/adapters/duckdbGraphRepository.ts
FakeGraphRepository adds in-memory CALLS edge model, seedCallSite helper, and implements precise methods for contract tests; DuckDbGraphRepository delegates three methods to preciseResolution functions via held DuckDB connection.
Indexer precise resolution
packages/core/src/index.ts
resolvePreciseEdges(workspaceRoot, resolver, options) fetches unresolved call sites from repository, resolves each via provided PreciseLocationResolver, maps resolved file+line candidates to symbol IDs via repository lookup, persists upgraded edges, reports progress/cancellation, and returns total/upgraded/cancelled counts.
Indexer tests
packages/core/src/index.test.ts
Validates successful upgrade with progress tracking and reduced work on repeat run, no-op when resolver returns empty, and cancellation before processing.
VS Code command
packages/extension/src/commands/resolvePreciseEdges.ts
createResolvePreciseEdgesCommand validates workspace folder, runs indexer.resolvePreciseEdges under cancellable progress UI with incremental reporting, logs results, calls optional onResolved callback on upgrades, shows appropriate info message.
Extension integration
packages/extension/package.json, packages/extension/src/extension.ts
Registers dextree.resolvePreciseEdges command; wires command factory with logger, getIndexer, and refreshViewsAfterIndex callback during activation.
Command and mock tests
packages/extension/src/commands/resolvePreciseEdges.test.ts, packages/extension/src/commands/indexFile.test.ts, packages/extension/src/commands/indexWorkspace.test.ts, packages/extension/src/extension.test.ts, packages/extension/src/watcher/workspaceWatcher.test.ts
Complete command test suite covering upgrade reporting, empty unresolved case, and cancellation messaging; mock indexer factories updated to stub resolvePreciseEdges method.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes


Possibly related issues


Possibly related PRs

  • dgtalbug/dextree#163: Introduces PreciseLocationResolver and related types from packages/core/src/resolution/types.ts that this PR extends for precise edge upgrading.

Suggested labels

area:core, area:extension, type:feat


🌳 A second pass of LSP-backed wisdom,
Where heuristic edges gain precise vision,
Each CALLS edge climbs from hunch to truth,
As language servers whisper proof—
Dextree grows more certain with each upgrade.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 40.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed Title clearly summarizes the main change: implementing persistent precise LSP-based call resolution (pass-2), stacked on #165. Specific and directly maps to changeset.
Description check ✅ Passed Description comprehensively covers the feature scope, architectural decisions, safety guarantees, test coverage, and dependencies. Directly related to all files modified.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Comment @coderabbitai help to get the list of available commands and usage tips.

Base automatically changed from feature/graph-repository to main June 7, 2026 12:51
@codecov-commenter

codecov-commenter commented Jun 7, 2026

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

❌ Patch coverage is 68.51852% with 17 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
packages/core/src/storage/fakeGraphRepository.ts 11.76% 15 Missing ⚠️
packages/core/src/index.ts 95.23% 0 Missing and 1 partial ⚠️
packages/core/src/storage/preciseResolution.ts 92.30% 0 Missing and 1 partial ⚠️

📢 Thoughts on this report? Let us know!

@dgtalbug
dgtalbug marked this pull request as ready for review June 7, 2026 12:51
dgtalbug added 2 commits June 7, 2026 18:22
Adds the core half of persisted precise call resolution (the unbuilt 'pass-2 LSP
fills them in' the edge schema references). New GraphRepository methods:
- getUnresolvedCallSites(workspaceRoot): CALLS edges not yet precise, each with
  the source symbol's NodeLocation (file + start line/col) for the LSP query
- findSymbolIdAt(filePath, line): map an LSP result location back to a stored
  target symbol id (file + start line; null on no/ambiguous match)
- persistPreciseEdges(resolutions): set target_id + stamp
  metadata.resolution='precise' (confidence 1.0) in one transaction

Implemented in storage/preciseResolution.ts (behind the adapter) + the in-memory
FakeGraphRepository (with a seedCallSite test helper). The existing
stampResolutionTier already guards <> 'precise', so precise edges are never
downgraded — proven by test. Real-DuckDB tests cover list/match/persist,
idempotency, and no-downgrade. core 279/279.

Refs: #166
Wires the pass-2 precise resolution end to end. New Indexer.resolvePreciseEdges
(workspaceRoot, resolver, {onProgress, isCancelled}) orchestrates the core loop:
work-list (getUnresolvedCallSites) → host resolver per site → match each result
back to a target (findSymbolIdAt) → batch persist (persistPreciseEdges). The host
owns only the LSP resolver (RULE-ARCH-005); all DB access stays behind the
repository.

Extension: createResolvePreciseEdgesCommand runs the pass under a cancellable
withProgress notification (reports N/M sites · K upgraded) and refreshes the graph
on finish. Registered as dextree.resolvePreciseEdges / 'Resolve Precise Edges
(LSP)' in activate + package.json.

Behavior-preserving + opt-in: nothing runs unless invoked; idempotent (precise
edges drop out of the work-list); graceful (cold/empty resolver leaves edges
heuristic); cancellable (persists partial). Integration tests against a real
in-memory DB cover upgrade, idempotency, cold resolver, and cancellation; command
tests cover the UI shell. Indexer mocks across extension tests gained the method.

core 282 / exporters 161 / extension 643.

Refs: #166
@dgtalbug
dgtalbug force-pushed the feature/precise-resolution branch from c138bc1 to 069d605 Compare June 7, 2026 12:53
@dgtalbug
dgtalbug merged commit 5b4ac15 into main Jun 7, 2026
12 of 13 checks passed
@dgtalbug
dgtalbug deleted the feature/precise-resolution branch June 7, 2026 12:57
@github-actions github-actions Bot locked as resolved and limited conversation to collaborators Jun 22, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Persist precise (LSP) call resolution into the index — pass-2

2 participants