fix(cache): re-anchor CWD-relative source_file on write so warm hits keep canonical symbol ids - #2632
Conversation
There was a problem hiding this comment.
Graphify reviewed this change.
Worth a look — the grounded gate found no coupling regressions or blocking issues, but 1 advisory finding(s) below merit a look before merge.
Formal verification. No changes could be formally verified in this run.
Graphify review — findings
This PR modifies _relativize_source_files_in in graphify/cache.py to handle CWD-relative source_file values by re-anchoring them to root-relative form when the CWD differs from the inferred root, rather than passing all relative paths through unchanged. The change targets a scenario (#2630) where relative inputs handed to extract() from a directory above the root produced cached path stamps that disagreed with how they were later read back. It also adds a new test (test_warm_hit_with_relative_inputs_from_above_the_root) exercising this case with an Astro fixture, verifying that warm cache hits reproduce cold-extraction node ids and that the .astro file is served from cache rather than re-extracted. The surface area is the single relativization helper plus its supporting test file; the many listed symbols appear to reflect module-level re-indexing rather than direct edits to each.
Worth a look
- cwd_form == root_resolved / sp comparison is always False for relative sp —
graphify/cache.py:571· Escalate · medium- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
Analysis details — impact, health, verification
Impact & health
Graphify review
Impact — 1177 functions depend on the 205 functions this change touches.
Health — grade A; 10 existing hotspot(s) in the area this change touches (pre-existing, not introduced here):
extract()— 433 callers, 41 callees (high)_rebuild_code()— 94 callers, 51 callees (high)detect()— 86 callers, 13 callees (high)save_semantic_cache()— 50 callers, 9 callees (high)extract_corpus_parallel()— 26 callers, 10 callees (high)dispatch_command()— 2 callers, 117 callees (high)file_hash()— 39 callers, 6 callees (high)load_cached()— 39 callers, 5 callees (high)- …and 2 more
Verification — 1177 functions in the blast radius were not formally verified this run (proofs are advisory here).
Gate & verification
graphify gate
PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.
Advisory (not blocking):
- verification_scope: 749 function(s) in the blast radius were not formally verified this run
Formal verification
Could not verify: Could not verify \_relativize\_source\_files\_in.
The verifier did not have enough to check \_relativize\_source\_files\_in, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: parameter `root` is annotated `Path` — outside the synthesizable primitive/collection set
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Fixed in v0.9.41 ( |
|
Shipped in v0.9.41 ( |
Fixes #2630.
Not an astro defect
extract_astro()'s ID recipe is already correct — a cold run produces exactly whatthe issue asks for:
Both reported symptoms only appear on a warm AST cache hit, and the cause is a
round-trip asymmetry in
graphify/cache.py:source_filewith the path stringextract()was handed, so arelative input yields a CWD-relative stamp (
src/pages/index.astro)._relativize_source_files_inskipped relative values on write ("already-relativefields pass through unchanged"), but
_absolutize_source_files_inreads them back asroot-relative (
root / sp).<root>/src/pages/index.astro— a path that names no file.Every
source_file-gated remap inextract()then misses. The one that matters hereis the file-stem prefix pass, which looks up
Path(source_file).resolve()inprefix_remap(extract.py:5756); with the lookup broken, the symbol keeps theraw-input-path stem that a cold run rewrites. Hence
src_pages_index_posts(case 1), andfor absolute inputs the on-disk path survives into the persisted id (case 2's leak class).
Astro only looks special
.astrois cached;.js/.jsx/.mjs/.cjs/.ts/.tsx/.mts/.cts/.vue/.svelteare all in_JS_CACHE_BYPASS_SUFFIXESand never round-trip through the cache. That is the entirereason the report's
content.tsnode stayed correct while its.astrosibling did not.The defect is language-agnostic — Python reproduces it identically:
Fix
One place, upstream of every extractor: the write side re-anchors a CWD-relative
source_fileto root-relative, so_absolutize_source_files_inbecomes a true inverse.Guarded to fail closed — it rewrites only when the CWD-relative reading names a real file
and differs from the root-relative reading. A fragment that already stores
root-relative (a semantic subagent's, per
_normalize_source_file_value) and a ghost pathare both left exactly as before.
What is deliberately not changed
extract_astro()— nothing to fix, and the issue's suggested post-passassert sym_id.startswith(file_stem + "_")is redundant once the shared gate is correct.An
assertwould also turn a stale cache entry into a crash.cache_root—extract(sub, cache_root=X)afterextract(super, cache_root=X)still replays a stale rel path, because the$graphify-root$marker (AST cache replays node ids minted under the original root after a corpus move/clone #2257) re-anchors only the root portion and assumes a file'spath within the root is unchanged. Separate design limit, and not reachable from this
issue's repro (default
cache_rootfollowsroot).Verification
src_pages_index_postspages_index_postsindex_posts/ leaked slug when warmed cross-rootindex_postssrc_lib_content_get_postslib_content_get_postsNew test
tests/test_cache.py::test_warm_hit_with_relative_inputs_from_above_the_rootasserts warm ids == cold ids and that every symbol sits under its file node's stem. It
carries the same warmth probe as the #2257 test, so a silent re-extraction cannot make it
pass vacuously. Confirmed failing without the fix, passing with it.
Full suite: identical failure set before and after (42 pre-existing, Windows/env). One of
those is
test_astro_import_ids.py::test_astro_relative_inputs_keep_canonical_ids, whichfails on
source_fileseparators — the other defect in the same repro project, out ofscope here.