Skip to content

fix(graph): check grounding against a graph stale only by changed source - #241

Merged
theyashasvipandey merged 2 commits into
mainfrom
fix/228-check-drift-without-refresh
Sep 24, 2026
Merged

theyashasvipandey merged 2 commits into
mainfrom
fix/228-check-drift-without-refresh

Conversation

@theyashasvipandey

Copy link
Copy Markdown
Collaborator

Closes #228.

Problem

After any source edit the graph becomes stale, and mex check skipped every grounding check until a full mex graph refresh (75–175 s on the eval subjects, #209). The score did not move for the very edit grounding exists to catch: Hono scored 8 before and after a grounded-body edit.

What changes

When the only reason the graph is not fresh is changed source files (and the complete changed-path list is known), check grounds anyway:

Grounded node's file Result
Unchanged since the snapshot Checked against the snapshot, as before
Edited, tree-sitter language (Python, Go, …) Node re-extracted from the edited file by its Tier-1 id with the same extractFile + body-hash normalization a refresh uses → real GROUNDING_DRIFT or clean
Edited TypeScript/JavaScript GROUNDING_UNVERIFIED — compiler spans come from a whole program, so exact equality can't be proven per file
Deleted, or node not located exactly GROUNDING_UNVERIFIED
  • GROUNDING_UNVERIFIED is a new warning, counted in the score, telling the user to run mex graph refresh.
  • A stale snapshot never reconciles: nothing is reported GROUNDING_GONE, AMBIGUOUS or moved.
  • Config, semantic-input, branch, grammar, schema and parse-health staleness skip grounding exactly as before. The freshness warning stays.
  • Edited files are read through the existing contained, size-capped status reader. No raw reads are added, and engine-impl.ts, maintenance.ts and extraction/** are untouched.

Also fixed: closing a read-only grounding runtime now releases the descriptor that binds graph.db, not only the SQLite reader. On Windows the open handle made the next in-process refresh fail with "The live graph changed before candidate publication". This was pre-existing on main, and this change would have spread it to stale checks.

Tests

  • test/graph-grounding-source-drift.test.ts (new):
    • A differential test compares every grounded node's verdict before refresh with its verdict after a real rebuild. It covers a body change, a comment above plus a whitespace-only body edit, a rename, a deleted file, and a function inserted above, in Python and TypeScript, plus a drift that already existed before the edit. Every verdict must be identical or UNVERIFIED. On main it fails: the body edit reads clean before the refresh and DRIFT after.
    • Deletion and body-edit detection before refresh, with the score moving.
    • Descriptor release (fails on main).
    • A config change still skips grounding. This one passes both ways; it pins the old behaviour.
  • test/graph-migration.test.ts asserted the old "stale ⇒ no grounding issues" rule; it now expects UNVERIFIED for the edited TypeScript grounding and anchor.
  • Full suite on Windows:
    • Same test count on both sides: 35 failures with this change vs 33 on main.
    • The 7 tests that failed only with this change pass when rerun in isolation; the remaining failures are the known local Windows baseline.
    • Two main failures now pass because of the descriptor fix: builds fingerprints, detects body drift, re-grounds… and a successful no-op agent session preserves drift….

Real reproduction (copies of the eval subjects)

Counts are grounds_to entries detected out of the total for that symbol.

Edit Before refresh (main) Before refresh (this PR) After graph refresh
Hono: change compose() body 0 / 2 1 / 2 UNVERIFIED 1 / 2 DRIFT*
Hono: delete testClient 0 / 2 2 / 2 UNVERIFIED 2 / 2 GONE
mex: change extractGroundings body 0 / 1 1 / 1 UNVERIFIED 1 / 1 DRIFT
mex: delete createRepositoryGraphPort 0 / 1 1 / 1 UNVERIFIED 1 / 1 GONE

* The missed one is #226 (root grounds_to skipped).

  • Whole-scaffold differential (every grounding and inline anchor, verdict before refresh vs after):
    • Hono: 52 sites, 0 contradictions (main: 5).
    • mex: 24 sites, 0 contradictions (main: 11, including all 8 grounding issues that existed before the edit, which main loses while stale).
  • Score: Hono goes 8 → 0 before the refresh. mex was already at 0.
  • check wall time on a stale Hono: about 12.4 s on main vs 13.1 s with this change, about what a check on a fresh graph already costs.

Accepted cost

  • Every grounding in an edited TypeScript/JavaScript file reads UNVERIFIED until the next refresh.
  • A grounding that was already GONE or AMBIGUOUS also reads UNVERIFIED while the graph is stale.
  • A possible follow-up is exact per-file compiler re-extraction for TypeScript. It measured about 2.4 s per check on mex, so it is not included here.

When the only reason the graph is stale is changed source files, mex check
now grounds instead of skipping: unchanged files are checked against the
snapshot, nodes in edited tree-sitter files are re-extracted and hashed
exactly as a refresh would, and everything else (deleted files, edited
TypeScript/JavaScript, nodes not located exactly) is reported as the new
GROUNDING_UNVERIFIED warning. Nothing is reported gone or moved from a
stale snapshot; every other non-fresh reason still skips grounding.

Also release the graph.db descriptor when a read-only grounding runtime
closes; on Windows the open handle broke the next in-process refresh.

Closes #228
@theyashasvipandey
theyashasvipandey merged commit 60655db into main Sep 24, 2026
9 checks passed
@theyashasvipandey
theyashasvipandey deleted the fix/228-check-drift-without-refresh branch September 24, 2026 16:55
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.

check reports no grounding drift after a source edit until a full graph refresh

1 participant