The --update runbook in graphify/skills/claude/references/update.md omits the kind= argument on both its detect_incremental() and save_manifest() calls, so both fall back to defaults that are wrong for an AST-only update. Verified on v8 HEAD (file is byte-identical to the copy shipped in graphifyy 0.9.19).
1. detect_incremental() defaults to kind="semantic" → re-extracts the whole corpus
Line 15:
result = detect_incremental(Path('INPUT_PATH'))
The signature defaults to kind="semantic", which reports a file as changed when its semantic_hash is missing. On a graph built without a semantic pass — e.g. installed without the LLM extra, or built purely from AST — no file has ever had a semantic_hash, so every file reports as changed.
On a 629-file corpus this reported new_total: 629 (629/629, 100%) when the real delta was 12 changed source files. Following the runbook literally would then chunk ~620 files into ~29 semantic subagents and spend LLM tokens re-extracting a corpus that had not changed.
The detect_incremental docstring already states the intended mode:
kind="ast": a file is "changed" when its ast_hash is missing or its content has changed. Use this for graphify update.
Passing kind="ast" gives the correct answer (12 files) at zero token cost.
2. save_manifest() defaults to kind="both" → falsely stamps semantic_hash
Line 145:
save_manifest(incremental['files'], root='INPUT_PATH')
This defaults to kind="both", which stamps both hashes. An AST-only update therefore writes a semantic_hash for files that were never semantically extracted. A later detect_incremental(kind="semantic") then considers them already done and skips them — so the semantic pass silently never runs on that corpus.
save_manifest's own docstring describes kind="ast" as the mode "written by graphify update (AST-only rebuild)", so the intent is clear; the runbook just doesn't pass it.
Interaction
The two defects partially mask each other: (2) suppresses future semantic extraction, while (1) forces a full semantic re-extraction whenever the manifest lacks those hashes. Net effect is that the token cost of an incremental update is unpredictable and can be ~50x the real delta.
Suggested fix
In the AST-only path of references/update.md, pass the mode explicitly:
result = detect_incremental(Path('INPUT_PATH'), kind='ast')
...
save_manifest(incremental['files'], root='INPUT_PATH', kind='ast')
The full-pipeline path (graphify extract after a real semantic pass) should keep kind="semantic" / "both" — the fix applies specifically to the code-only branch the runbook labels "Code-only changes detected - skipping semantic extraction (no LLM needed)", which is precisely the branch where a semantic-mode diff is wrong.
Same omission appears in the sibling copies under graphify/skills/*/references/update.md.
Environment
graphifyy 0.9.19 (installed via uv tool), file identical to v8 HEAD
- Python 3.11, Linux
The
--updaterunbook ingraphify/skills/claude/references/update.mdomits thekind=argument on both itsdetect_incremental()andsave_manifest()calls, so both fall back to defaults that are wrong for an AST-only update. Verified onv8HEAD (file is byte-identical to the copy shipped ingraphifyy0.9.19).1.
detect_incremental()defaults tokind="semantic"→ re-extracts the whole corpusLine 15:
The signature defaults to
kind="semantic", which reports a file as changed when itssemantic_hashis missing. On a graph built without a semantic pass — e.g. installed without the LLM extra, or built purely from AST — no file has ever had asemantic_hash, so every file reports as changed.On a 629-file corpus this reported
new_total: 629(629/629, 100%) when the real delta was 12 changed source files. Following the runbook literally would then chunk ~620 files into ~29 semantic subagents and spend LLM tokens re-extracting a corpus that had not changed.The
detect_incrementaldocstring already states the intended mode:Passing
kind="ast"gives the correct answer (12 files) at zero token cost.2.
save_manifest()defaults tokind="both"→ falsely stampssemantic_hashLine 145:
This defaults to
kind="both", which stamps both hashes. An AST-only update therefore writes asemantic_hashfor files that were never semantically extracted. A laterdetect_incremental(kind="semantic")then considers them already done and skips them — so the semantic pass silently never runs on that corpus.save_manifest's own docstring describeskind="ast"as the mode "written bygraphify update(AST-only rebuild)", so the intent is clear; the runbook just doesn't pass it.Interaction
The two defects partially mask each other: (2) suppresses future semantic extraction, while (1) forces a full semantic re-extraction whenever the manifest lacks those hashes. Net effect is that the token cost of an incremental update is unpredictable and can be ~50x the real delta.
Suggested fix
In the AST-only path of
references/update.md, pass the mode explicitly:The full-pipeline path (
graphify extractafter a real semantic pass) should keepkind="semantic"/"both"— the fix applies specifically to the code-only branch the runbook labels "Code-only changes detected - skipping semantic extraction (no LLM needed)", which is precisely the branch where a semantic-mode diff is wrong.Same omission appears in the sibling copies under
graphify/skills/*/references/update.md.Environment
graphifyy0.9.19 (installed viauv tool), file identical tov8HEAD