mirror of
https://github.com/safishamsi/graphify.git
synced 2026-10-15 16:39:43 +00:00
Correct recovered groups own source_file before writing them
A recovered group's write KEY pointed at the right file, but every node, edge, and hyperedge inside it still carried the original unresolvable source_file string. load_cached rejects an entry on read if any item's source_file does not match the path it was loaded under, so the previous commit alone wrote a group that reported as saved but could never actually be read back, silently reproducing the exact loss this fix exists for. This corrects every item's source_file to the recovered path's portable form before the group is written, only when recovery actually redirected the key, so an unrecovered group stays byte identical to before. Includes the regression test proving the full round trip: a group recovered by basename now survives a subsequent load_cached call. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017qfdzgbA5KedGEjD1AayNh
This commit is contained in:
1 parent
73f5a66370
commit
4b91dcd462
2 files changed
+48
No files matched your search
@@ -1592,6 +1592,24 @@ def save_semantic_cache(
|
||||
for fpath, result in by_file.items():
|
||||
cache_path, p = _recover_group_path(fpath)
|
||||
if p.is_file():
|
||||
if cache_path != source_path(fpath):
|
||||
# #2973: recovery only redirected the WRITE KEY. Each item in
|
||||
# this group still carries the original unresolvable
|
||||
# source_file string, and _semantic_entry_matches_path
|
||||
# rejects an entry on read if any item's source_file doesn't
|
||||
# match the path it was loaded under -- so leaving the old
|
||||
# value in place would write a "successful" entry that can
|
||||
# never actually be read back, silently reproducing the same
|
||||
# loss this recovery exists to fix.
|
||||
corrected = _normalize_value(str(cache_path))
|
||||
result = {
|
||||
**result,
|
||||
"nodes": [{**n, "source_file": corrected} for n in result["nodes"]],
|
||||
"edges": [{**e, "source_file": corrected} for e in result["edges"]],
|
||||
"hyperedges": [
|
||||
{**h, "source_file": corrected} for h in result["hyperedges"]
|
||||
],
|
||||
}
|
||||
if allowed_paths is not None and cache_path not in allowed_paths:
|
||||
warnings.warn(
|
||||
"semantic cache skipped out-of-scope source_file "
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
"""#2973 — save_semantic_cache must recover a group whose reported
|
||||
source_file never resolves to a real file, by an unambiguous basename match
|
||||
against the already dispatched allowlist, instead of silently dropping it.
|
||||
|
||||
A weak or local backend's adaptive-retry split path (llm.py bisecting a
|
||||
chunk that overflowed context and retrying) sometimes re-prompts with a
|
||||
reduced file subset and loses track of which of the original chunk's files
|
||||
a given node came from, so its source_file drifts to something that never
|
||||
resolves at all. Without recovery this silently discarded the group's nodes
|
||||
and edges from the cache on every incremental run.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import pytest
|
||||
|
||||
from graphify.cache import load_cached, save_semantic_cache
|
||||
|
||||
|
||||
def test_malformed_but_basename_unique_path_recovers(tmp_path):
|
||||
real = tmp_path / "sub" / "weird_named_file.py"
|
||||
real.parent.mkdir(parents=True)
|
||||
real.write_text("def f(): pass\n")
|
||||
|
||||
nodes = [{"id": "n1", "label": "f", "source_file": "lost_dir/weird_named_file.py"}]
|
||||
saved = save_semantic_cache(nodes, [], root=tmp_path, allowed_source_files=[real])
|
||||
assert saved == 1
|
||||
|
||||
cached = load_cached(real, root=tmp_path, kind="semantic")
|
||||
assert cached is not None
|
||||
assert {n["id"] for n in cached["nodes"]} == {"n1"}
|
||||
Reference in new issue
Block a user