Skip to content

fix(cache): re-anchor CWD-relative source_file on write so warm hits keep canonical symbol ids - #2632

Closed
rajarshidattapy wants to merge 1 commit into
Graphify-Labs:v8from
rajarshidattapy:fix/astro-frontmatter-symbol-id-stem
Closed

fix(cache): re-anchor CWD-relative source_file on write so warm hits keep canonical symbol ids#2632
rajarshidattapy wants to merge 1 commit into
Graphify-Labs:v8from
rajarshidattapy:fix/astro-frontmatter-symbol-id-stem

Conversation

@rajarshidattapy

Copy link
Copy Markdown
Contributor

Fixes #2630.

Not an astro defect

extract_astro()'s ID recipe is already correct — a cold run produces exactly what
the issue asks for:

pages_index          # file node
pages_index_posts    # frontmatter variable -- correctly prefixed

Both reported symptoms only appear on a warm AST cache hit, and the cause is a
round-trip asymmetry in graphify/cache.py:

  • Extractors stamp source_file with the path string extract() was handed, so a
    relative input yields a CWD-relative stamp (src/pages/index.astro).
  • _relativize_source_files_in skipped relative values on write ("already-relative
    fields pass through unchanged"), but _absolutize_source_files_in reads them back as
    root-relative (root / sp).
  • When CWD is not the inferred root the two disagree, and a warm hit resurrects
    <root>/src/pages/index.astro — a path that names no file.

Every source_file-gated remap in extract() then misses. The one that matters here
is the file-stem prefix pass, which looks up Path(source_file).resolve() in
prefix_remap (extract.py:5756); with the lookup broken, the symbol keeps the
raw-input-path stem that a cold run rewrites. Hence src_pages_index_posts (case 1), and
for absolute inputs the on-disk path survives into the persisted id (case 2's leak class).

Astro only looks special

.astro is cached; .js/.jsx/.mjs/.cjs/.ts/.tsx/.mts/.cts/.vue/.svelte are all in
_JS_CACHE_BYPASS_SUFFIXES and never round-trip through the cache. That is the entire
reason the report's content.ts node stayed correct while its .astro sibling did not.
The defect is language-agnostic — Python reproduces it identically:

run 1 (cold)   lib_content_get_posts
run 2 (warm)   src_lib_content_get_posts

Fix

One place, upstream of every extractor: the write side re-anchors a CWD-relative
source_file to root-relative, so _absolutize_source_files_in becomes 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 path
are both left exactly as before.

What is deliberately not changed

  • extract_astro() — nothing to fix, and the issue's suggested post-pass
    assert sym_id.startswith(file_stem + "_") is redundant once the shared gate is correct.
    An assert would also turn a stale cache entry into a crash.
  • Cross-root shared cache_rootextract(sub, cache_root=X) after
    extract(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's
    path within the root is unchanged. Separate design limit, and not reachable from this
    issue's repro (default cache_root follows root).

Verification

scenario before after
case 1 warm (relative inputs, CWD above root) src_pages_index_posts pages_index_posts
case 2 (absolute single-file input) index_posts / leaked slug when warmed cross-root index_posts
python equivalent, warm src_lib_content_get_posts lib_content_get_posts

New test tests/test_cache.py::test_warm_hit_with_relative_inputs_from_above_the_root
asserts 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, which
fails on source_file separators — the other defect in the same repro project, out of
scope here.

@graphify-labs graphify-labs Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 spgraphify/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

safishamsi added a commit that referenced this pull request Aug 12, 2026
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@safishamsi

Copy link
Copy Markdown
Collaborator

Fixed in v0.9.41 (graphifyy==0.9.41 on PyPI). A warm cache hit no longer re-anchors a CWD-relative source_file to a ghost path when the working directory differs from the graph root; cold and warm node ids match. Thanks @rajarshidattapy.

@safishamsi safishamsi closed this Aug 12, 2026
@safishamsi

Copy link
Copy Markdown
Collaborator

Shipped in v0.9.41 (graphifyy==0.9.41 on PyPI). Credited in the release notes. Thanks @rajarshidattapy!

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.

Astro frontmatter variable nodes do not share their file node's ID stem, and can embed the absolute on-disk path in the node ID

2 participants