Description
Graphify version: 0.4.11 (pipx via miniforge), macOS 14, Python 3.12
Symptom
After a /graphify <path> (full rebuild), every subsequent /graphify --update reports a manifest that contains only the files seen in the prior --update run — not the cumulative corpus. The next --update then re-flags every other file in the corpus as "new", even though their content (and the existing graph.json) is unchanged.
Concrete reproducer in my corpus (a mixed wiki+code+raw archive, ~1028 files):
- Full rebuild →
manifest.jsoncorrectly contains all ~1028 files (Step 9 saves it from.graphify_detect.json). - Touch ~20 wiki entities; run
/graphify --update. Behaves correctly for this run. - After the run,
manifest.jsonnow contains 24 entries (only the files touched in step 2's input, since--update's.graphify_detect.jsonis populated withnew_filesonly). - Touch nothing; run
/graphify --updateagain.detect_incrementalreports 1008 new files — every file in the corpus except the 24 from step 2.
The content-hash semantic cache rescues this from being catastrophic — it hits on ~890 of the 1008 files and only ~118 are re-extracted. But:
- The "new files" report is misleading and triggers re-extraction of any uncached doc.
- The deletion-detection logic in the merge step has the wrong "manifest universe" to compare against, so it would miss true deletions.
code_onlyfast-path doesn't fire when 99 stale code files appear innew_files.- It's the #538 bug, but symmetric: that one fixed manifest persistence on the full-rebuild path; the
--updatepath now persists too little.
Root cause
Two related defects in skill.md (installed/shipped Claude Code skill):
1. --update flow never populates .graphify_detect.json
The ## For --update section only writes .graphify_incremental.json, but Steps 3A–9 (which the section instructs the agent to "run as normal") all read from .graphify_detect.json. The skill works at all only because each agent ends up writing this file ad-hoc — and the natural ad-hoc shape is {'files': new_files}, which leads to defect 2.
2. Step 9 save_manifest(detect['files']) shrinks the manifest
# Step 9, current
detect = json.loads(Path('graphify-out/.graphify_detect.json').read_text())
save_manifest(detect['files'])save_manifest (graphify/detect.py) iterates the provided file list and writes {path: mtime} to manifest.json, overwriting the previous manifest. If detect['files'] is the --update subset (changed files only), every unchanged file's mtime is dropped from the manifest. Next detect_incremental sees stored_mtime is None for all of them → flags as new.
This is the same family as #538 (manifest not saved on full rebuild), but on the opposite path: here the manifest is saved on --update, but with the wrong (subset) data.
Suggested fix
Minimal, two-touch patch to graphify/skill.md:
(a) In the ## For --update section, after the detect_incremental block, add an explicit .graphify_detect.json population step that carries both keys:
$(cat graphify-out/.graphify_python) -c "
import json
from pathlib import Path
r = json.loads(Path('graphify-out/.graphify_incremental.json').read_text())
Path('graphify-out/.graphify_detect.json').write_text(json.dumps({
'files': r.get('new_files', {}), # drives processing in Steps 3A-3C
'all_files': r.get('files', {}), # full corpus, used by Step 9 save_manifest
'total_files': r.get('new_total', 0),
'total_words': r.get('total_words', 0),
'skipped_sensitive': r.get('skipped_sensitive', []),
'needs_graph': True,
}))
"(b) Change Step 9's save_manifest(detect['files']) to prefer all_files when present:
# In --update mode, 'all_files' carries the full corpus; 'files' is the changed
# subset. Full-rebuild mode populates only 'files', so the fallback handles that.
save_manifest(detect.get('all_files') or detect['files'])These changes are additive — full-rebuild flow is unaffected because it never writes all_files, and the fallback or detect['files'] preserves its existing semantics.
A PR is being prepared.
Observations / related
- Issue #826 (cache key path mismatch) is in the same operational area but distinct — the content-hash cache was masking some of this for me on subsequent runs.
- The defect mostly hides on code-only corpora because AST extraction is cheap and
code_onlywould normally trigger the no-LLM fast-path. On mixed wiki/doc corpora it's much more visible: hundreds of doc files get flagged as "new" and the semantic cache becomes the only thing preventing a full re-extraction bill. - I've already applied this patch locally and verified:
manifest.jsonis restored to 1028 entries, and a subsequentdetect_incrementalreportsnew_total: 0against an unchanged corpus.
Happy to revise the PR shape (e.g., move the file-population logic into detect.py instead of the skill markdown if that's preferred upstream).
Activity
Adriftnote commentedon May 12, 2026
Quick clarification after I checked the v7/0.7.14 source (my installed version was 0.4.11 from pipx/miniforge, hence the slightly out-of-date framing above):
Defect 2 (
save_manifest(detect['files'])shrinking the manifest) — actually fixed in v0.7.14 by the new merge block at lines 797-841 ofgraphify/skill.md, which now callssave_manifest(incremental['files'])directly.incremental['files']is the full-corpus list from the internaldetect()call insidedetect_incremental, so the manifest is restored correctly. Step 9'ssave_manifest(detect['files'])(line 675) still exists for the full-rebuild path, but--updateno longer reaches Step 9 (the flow exits after Steps 4–8 per line 844), so it's a no-op for this bug.Defect 1 (
.graphify_detect.jsonnot populated in --update flow) — still present in v7/0.7.14. Step 3A (line 226), Step 3B0 (line 262), Step 5 timing block (lines 247-250), and a couple of report stages all read.graphify_detect.jsonunconditionally. The--updatesection only writes.graphify_incremental.json. In practice this either:.graphify_detect.json, orPR #838 targets v7 with a minimal patch for Defect 1 only. Closing-the-loop on Defect 2 for older versions is implicit (anyone on v0.7.14+ is fine; older users should
pip install -U graphifyy).safishamsi commentedon May 13, 2026
Fixed in commit
822abd6.Two-part fix in
skill.md:After
detect_incrementalin the--updatesection, added an explicit step that writes.graphify_detect.jsonwith bothfiles(the delta, for Steps 3A/3B) andall_files(the full corpus). Previously this file was populated ad-hoc with only the delta subset, so Steps 3A–6 operated on the wrong universe.Step 9's
save_manifest(detect['files'])changed tosave_manifest(detect.get('all_files') or detect['files'])— defense-in-depth so the full corpus manifest is preserved even if an agent runs Step 9 during an--updaterun.fix --update manifest shrink and align file_type enum (#837, #840)
fix --update manifest shrink and align file_type enum (Graphify-Labs#837
fix --update manifest shrink and align file_type enum (Graphify-Labs#837
fix(skillgen): drop a leftover partial list when --update starts, not…