Summary
graphify-out/cache/stat-index.json is keyed by absolute path and never prunes entries for roots that are no longer in play. When the same corpus is reached through more than one absolute path — a second mount of the same share, a CI/snapshot clone under /tmp, a moved checkout — the index accumulates one full set of entries per root, and a run from a root that isn't the one that wrote the index gets zero cache hits: every file re-extracts, every run, forever.
Observed
A ~1,130-file repo, graphify 0.9.17, on macOS. Every graphify update . reported all files uncached:
AST extraction: 1131/1131 uncached files (100%) [16 workers]
The stat index had grown to 11,614 entries for ~1,130 files — four roots, three of them dead:
| root |
entries |
/System/Volumes/Data/mnt/dev/mm/<repo> |
3198 |
/mnt/j/dev/mm/<repo> (same share, second mount path) |
3062 |
/tmp/platform_snap.QSy9DT/repo (throwaway clone, gone) |
2677 |
/tmp/platform_snap2.kaYXPI/repo (throwaway clone, gone) |
2677 |
The index that happened to be committed was written from the /mnt/j root, so on the machine running from /System/Volumes/Data/... nothing matched.
Deleting stat-index.json restored caching immediately — the next run reported 3181 files cached/unchanged, 0 re-extracted. It regrows the same way.
Why this is worth fixing rather than documenting
- The failure is silent: output says "uncached", which reads as "these files changed", not "your cache is keyed to a path you aren't at". Nothing points at the index.
- The cost scales with corpus size — here it was a full re-extract of 1,130 files on every run, plus a full rewrite of a 40 MB
graph.json.
graphify-out/ is documented as commit-worthy, so a cache index written on one machine travels to machines with a different absolute root, where it is guaranteed to miss.
- Dead roots are never reclaimed. Two of the four here were
/tmp clones that had not existed for days.
manifest.json already stores relative keys, and the incremental reference explicitly calls this out for the manifest (#1417: "manifest keys stay relative to the scan root — portable across clones/machines, so --update keeps matching cached files instead of missing every one after a move"). The stat index appears to have missed that same treatment.
Suggested fix
- Key
stat-index.json relative to the scan root, as manifest.json already is.
- Failing that, drop entries whose root is not the current scan root when writing the index, so it stays bounded and self-heals instead of accumulating.
- Either way, warn when a run gets ~0% cache hits against a non-empty index — that would have made this self-diagnosing.
Related: #1417 (relative manifest keys), #2197 (absolute source_file poisoning the semantic cache) — same root cause in a different artifact.
Environment
graphify 0.9.17, macOS 25.5 (Darwin), Python 3.11 install via uv. Corpus reached through two mount paths of one network share plus transient /tmp snapshot clones.
Summary
graphify-out/cache/stat-index.jsonis keyed by absolute path and never prunes entries for roots that are no longer in play. When the same corpus is reached through more than one absolute path — a second mount of the same share, a CI/snapshot clone under/tmp, a moved checkout — the index accumulates one full set of entries per root, and a run from a root that isn't the one that wrote the index gets zero cache hits: every file re-extracts, every run, forever.Observed
A ~1,130-file repo, graphify 0.9.17, on macOS. Every
graphify update .reported all files uncached:The stat index had grown to 11,614 entries for ~1,130 files — four roots, three of them dead:
/System/Volumes/Data/mnt/dev/mm/<repo>/mnt/j/dev/mm/<repo>(same share, second mount path)/tmp/platform_snap.QSy9DT/repo(throwaway clone, gone)/tmp/platform_snap2.kaYXPI/repo(throwaway clone, gone)The index that happened to be committed was written from the
/mnt/jroot, so on the machine running from/System/Volumes/Data/...nothing matched.Deleting
stat-index.jsonrestored caching immediately — the next run reported3181 files cached/unchanged, 0 re-extracted. It regrows the same way.Why this is worth fixing rather than documenting
graph.json.graphify-out/is documented as commit-worthy, so a cache index written on one machine travels to machines with a different absolute root, where it is guaranteed to miss./tmpclones that had not existed for days.manifest.jsonalready stores relative keys, and the incremental reference explicitly calls this out for the manifest (#1417: "manifest keys stay relative to the scan root — portable across clones/machines, so--updatekeeps matching cached files instead of missing every one after a move"). The stat index appears to have missed that same treatment.Suggested fix
stat-index.jsonrelative to the scan root, asmanifest.jsonalready is.Related: #1417 (relative manifest keys), #2197 (absolute
source_filepoisoning the semantic cache) — same root cause in a different artifact.Environment
graphify 0.9.17, macOS 25.5 (Darwin), Python 3.11 install via uv. Corpus reached through two mount paths of one network share plus transient
/tmpsnapshot clones.