Skip to content

fix(cache): preserve symlink path identities - #2868

Closed
oleksii-tumanov wants to merge 1 commit into
Graphify-Labs:v8from
oleksii-tumanov:fix/cache-symlink-walked-path
Closed

fix(cache): preserve symlink path identities#2868
oleksii-tumanov wants to merge 1 commit into
Graphify-Labs:v8from
oleksii-tumanov:fix/cache-symlink-walked-path

Conversation

@oleksii-tumanov

@oleksii-tumanov oleksii-tumanov commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

Summary

  • keep resolved paths for shared stat metadata while salting extraction entries with the walked in-root path
  • preserve walked identity across semantic cache writes, authorization, and partial-state checks while accepting equivalent resolved-root spellings
  • invalidate pre-fix AST cache keys and reject legacy semantic entries that belong to another alias

Fixes #2832

Result

Before this change, a cold extraction kept real.py and sub/link.py, but a warm run assigned all four nodes to the link. With this change, cold and warm runs retain both source paths, and the warm regression records zero extractor calls. Resolved policy paths under a symlinked scan root map to the matching walked file without authorizing a leaf alias.

Testing

  • uv run --frozen pytest -q tests/test_cache.py tests/test_stat_index_portability.py tests/test_word_count_cache.py tests/test_extract_cache_location.py tests/test_extract.py --tb=short (275 passed, 4 skipped)
  • uv run --frozen pytest tests/ -q --tb=short --ignore=tests/test_ollama_retry_cap.py (4578 passed, 73 skipped; optional openai dependency unavailable locally)
  • uv run --frozen ruff check graphify/cache.py tests/test_cache.py
  • uv run --frozen python -m tools.skillgen --check (134 artifacts matched)
  • graphify update .

@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

Keys the extraction-cache stat-index salt on the walked path relative to root instead of the resolved path, so symlink aliases of one target get distinct portable entries while out-of-root targets keep the resolved-path fallback. Threads the walked identity through save_semantic_cache (source_path, group_skipped, allowed/partial sets, and per-group save/load) and adds _semantic_entry_matches_path in load_cached to reject legacy semantic entries written under a colliding symlink alias, letting the next extraction self-heal.

Worth a look

  • Resolved allowed_source_files are no longer accepted for symlink-root semantic savesgraphify/cache.py:1406 · Escalate · medium
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review Execution auto-disposal is off for this run; enable it (with sandbox isolation) to have Graphify try to confirm or refute this automatically.
Analysis details — impact, health, verification

Impact & health

Graphify review

Impact — 1282 functions depend on the 221 functions this change touches.

Health — this change adds coupling hotspots:

  • new: extract() — 468 callers, 41 callees
  • new: _rebuild_code() — 98 callers, 51 callees
  • new: detect() — 107 callers, 15 callees
  • new: save_semantic_cache() — 53 callers, 11 callees
  • new: load_cached() — 40 callers, 7 callees
  • new: extract_corpus_parallel() — 26 callers, 10 callees
  • new: file_hash() — 43 callers, 6 callees
  • new: dispatch_command() — 2 callers, 117 callees
  • …and 17 more — each is listed as a finding

Verification — 1282 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: 802 function(s) in the blast radius were not formally verified this run

Formal verification

Could not verify: Could not verify file\_hash.

The verifier did not have enough to check file\_hash, 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 `path` is annotated `Path` — outside the synthesizable primitive/collection set

Could not verify: Could not verify load\_cached.

The verifier did not have enough to check load\_cached, 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 `path` is annotated `Path` — outside the synthesizable primitive/collection set

Could not verify: Could not verify save\_semantic\_cache.

The verifier did not have enough to check save\_semantic\_cache, 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

· 25 more finding(s) on lines outside this diff (see the check run).

@oleksii-tumanov
oleksii-tumanov force-pushed the fix/cache-symlink-walked-path branch from 4e00fcc to 0671165 Compare August 19, 2026 08:43

@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

Namespaces the AST cache directory by a new _AST_CACHE_SCHEMA (v{version}-s{schema}) so key-semantics changes evict stale entries independently of the package version. Reworks file_hash to salt on the walked path relative to root instead of the resolved path, so distinct symlink aliases no longer share an extraction entry, and threads walked-vs-resolved root handling through save_semantic_cache's source-path normalization. Adds _semantic_entry_matches_path and wires it into load_cached to reject legacy semantic entries whose source_file doesn't match the requested walked path, letting the next extraction self-heal.

Worth a look

  • save loop still gates on is_file() while load self-heal uses walked identity, causing symlink entries to be droppedgraphify/cache.py:1497 · Escalate · medium
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review Execution auto-disposal is off for this run; enable it (with sandbox isolation) to have Graphify try to confirm or refute this automatically.
Analysis details — impact, health, verification

Impact & health

Graphify review

Impact — 1288 functions depend on the 227 functions this change touches.

Health — this change adds coupling hotspots:

  • new: extract() — 468 callers, 41 callees
  • new: _rebuild_code() — 98 callers, 51 callees
  • new: detect() — 107 callers, 15 callees
  • new: save_semantic_cache() — 55 callers, 11 callees
  • new: load_cached() — 43 callers, 7 callees
  • new: file_hash() — 44 callers, 6 callees
  • new: extract_corpus_parallel() — 26 callers, 10 callees
  • new: dispatch_command() — 2 callers, 117 callees
  • …and 17 more — each is listed as a finding

Verification — 1288 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: 808 function(s) in the blast radius were not formally verified this run

Formal verification

Could not verify: Could not verify cache\_dir.

The verifier did not have enough to check cache\_dir, 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

Could not verify: Could not verify file\_hash.

The verifier did not have enough to check file\_hash, 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 `path` is annotated `Path` — outside the synthesizable primitive/collection set

Could not verify: Could not verify load\_cached.

The verifier did not have enough to check load\_cached, 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 `path` is annotated `Path` — outside the synthesizable primitive/collection set

Could not verify: Could not verify save\_semantic\_cache.

The verifier did not have enough to check save\_semantic\_cache, 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

· 25 more finding(s) on lines outside this diff (see the check run).

@safishamsi

Copy link
Copy Markdown
Collaborator

Shipped in v0.9.47 via authorship-preserving cherry-pick. Thanks @oleksii-tumanov! Release: https://github.com/Graphify-Labs/graphify/releases/tag/v0.9.47

@safishamsi safishamsi closed this Aug 19, 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.

Extraction cache keys on the resolved path, so a symlink displaces its target on rebuild

2 participants