codegraph_node / codegraph_explore read CURRENT bytes but slice them at
INDEXED line ranges; after an un-synced edit that slice can be a DIFFERENT
symbol's code served under the requested name — isError: false, introduced
by the 'verbatim … do not Read' guarantee. The watcher-based pending (#403)
and degraded (#876) banners cannot cover a project reached via projectPath:
cross-project instances have no watcher, by construction.
Freshness is now verified at the point of emission from data the index
already stores: one stat per rendered file (size + floored mtime, the sync
fast path's own test), sha256 content-hash compare only on stat mismatch
(so a touch/identical rewrite never false-positives), memoized briefly per
handler. On drift:
- codegraph_node: small files ship WHOLE and CURRENT (Read-parity, still
no Read needed); large ones omit the body with an explicit notice
steering to the tool's file-read mode or Read. Location/signature stay,
flagged as possibly shifted.
- codegraph_explore: the whole-file render (already correct by
construction) is kept and flagged; adaptive/skeleton/cluster slicing is
disabled for drifted files — a too-big drifted file is omitted with a
notice instead. The verbatim/do-not-Read header gains a per-file
exception, and a trailing note flags shifted line references (flow,
blast radius, symbol lists).
The guarantee itself is preserved: everything actually rendered is still
byte-accurate — drifted files ship whole or not at all, never as a
possibly-wrong slice. A re-sync of the target project restores normal
output (covered by test).
Adds __setLoadCodeGraphForTests (same seam pattern as __setFsWatchForTests)
so in-process tests can exercise a genuine cross-project open, which
vitest's transform cannot service through the lazy require.
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>