Skip to content

graph, mcp: carry re-parsed call edges as provisional and list reads that reindex - #1230

Draft
zzet wants to merge 3 commits into
mainfrom
perf/projected-reads
Draft

zzet wants to merge 3 commits into
mainfrom
perf/projected-reads

Conversation

@zzet

@zzet zzet commented Oct 31, 2026

Copy link
Copy Markdown
Owner

Draft, partial. Groundwork for reads that go through pending edits instead of reindexing (#1014). No handler changes behaviour yet: the wiring waits for the pending-edit projection (#1011).

Changes

  • Provisional call edges (internal/graph/overlay.go, internal/graph/edge.go). An overlay that covers a file hides every stored edge recorded there, and the file's re-parse holds its calls as unresolved references until relations are rebuilt. Reading an edited file through an overlay would therefore drop the edited function's callees and remove it from the callers of everything it calls, although none of those calls changed.
    • CarryBaseEdges returns copies of the stored call edges whose source still calls the same target name in the re-parse. Each copy sits at the new call's line and carries the new Edge.Provisional flag.
    • Repeated calls pair up in line order. A call the edit removed is not carried.
    • Only calls are carried. Go import placeholders carry the import path, which a re-parse resolves by path.
    • Provisional is never persisted and stays out of the edge identity, like NameOnly. The wire-contract fingerprint for graph.Edge and the delta writer's field count record it as an in-memory field.
  • IsTracked (internal/indexer/tracked.go). A read that finds a file drifted from the index has to tell a tracked file from one an editor or git created after indexing, and a tracked file missing on disk from an untracked one. The existing helpers fold an untracked path into "fresh" and stat the file to do it. IsTracked answers from the recorded mtimes alone.
  • Read-path guard (internal/mcp/read_path_guard_test.go, report mode). It builds a reference graph of the package's non-test files, follows each read tool's registered handler, and reports the shortest chain to reindexFile, IncrementalReindexRepo or IncrementalReindexPaths. A read tool is one the daemon does not mark effectful, the same table that drives the MCP readOnlyHint. Today it reports five handlers, all through ensureFresh: get_cfg, get_editing_context, get_file_summary, get_symbol and get_symbol_source. A fixture shows it catches a read handler that reindexes through a helper. It becomes a failing gate once read handlers stop reindexing.

Status

Tests

  • Under -race: TestIsTracked, TestCarryBaseEdgesKeepsEditedCallerRelations, TestReadHandlersNeverReachReindex, and the existing TestOverlay* tests in graph, indexer and mcp pass. go vet passes, and golangci-lint 2.14.0 on graph, indexer and mcp reports 0 issues.
  • That run caught the delta writer's field-count check (Edge has 17 fields; deltaEdgeContentEqual accounts for 16). The fix is in the carry commit, and the TestDelta*, TestOverlay*, TestEdge* and carry tests then pass under -race.
  • A review found the graph.Edge wire-contract fingerprint stale. TestWireContractFingerprint/graph.Edge fails without the fix. At this head it passes, along with the graph field-count and carry tests, and golangci-lint on ./cmd/gortex/... reports 0 issues.
  • Not run locally: the full package race suites. CI runs them.

The branch is based on 017db84 and merges cleanly with main at 7096db7.

Refs #1014

zzet added 3 commits October 31, 2026 21:41
An overlay that covers a file hides every stored edge recorded there,
and the file's re-parse holds its calls as unresolved references until
relations are rebuilt. Reading an edited file through an overlay would
therefore drop the edited function's callees and remove it from the
callers of everything it calls, although none of those calls changed.

CarryBaseEdges returns copies of the stored call edges whose source
still calls the same target name in the re-parse, at the new call's
line and marked with the new Edge.Provisional flag. Repeated calls
pair up in line order, and a call the edit removed is not carried.
Provisional is never persisted and stays out of the edge identity,
like NameOnly, so the wire-contract fingerprint and the delta writer's
field count record it as an in-memory field.

Refs #1014
A read that finds a file drifted from the index has to tell a tracked
file apart from one an editor or git created after indexing, and a
tracked file missing on disk apart from an untracked one. The existing
helpers fold an untracked path into "fresh" and stat the file to do
it.

IsTracked answers from the recorded mtimes alone.

Refs #1014
Five read handlers refresh a drifted file by reindexing it
synchronously, with no deadline, repeating the watcher's work for the
same file and waiting behind the edit pipeline. Before reads go
through pending edits instead, list every read tool whose handler can
reach reindexFile, IncrementalReindexRepo or IncrementalReindexPaths.

The guard builds a reference graph of the package's non-test files,
follows each read tool's registered handler through it and reports
the shortest chain to a reindex. It only reports for now and becomes
a gate once read handlers stop reindexing; a fixture shows it catches
a read handler that reindexes through a helper.

Refs #1014
@zzet zzet added this to the v0.64.12 milestone Oct 31, 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.

1 participant