Repository navigation
graphify claude install (v0.8.41): 26 validated bugs in the generated Claude Code skill (interpreter, --directed, shrink-guard, cleanup ordering) #1392
Description
Bug bundle: graphify claude install skill (v0.8.41) — 26 validated issues in the generated Claude Code skill
The auto-generated Claude Code skill produced by graphify claude install (pinned .graphify_version = 0.8.41) ships several reproducible bugs in SKILL.md and the references/*.md runbooks. We vendored this snapshot into a monorepo and an automated reviewer (plus manual checks) surfaced the issues below. Each was verified against the installed graphify v0.8.41 Python source (build.py, export.py, etc.), not just read off the markdown.
Reporting upstream because the skill is regenerated by graphify claude install, so any downstream edit to the vendored copy is overwritten on the next install/upgrade — the fix has to live in the generator.
Happy to open individual issues or send a PR against the skill template if that's easier; grouping here so the pattern is visible.
A. Interpreter resolution — generated commands call system python3, which can't import graphify under uv tool install
- SKILL.md interpreter bootstrap —
uv tool run graphifyy pythonselects no package and falls through to systempython3. The skill should resolve and persist the tool's interpreter (e.g. read the shebang of the installedgraphifybin) — we use$(cat graphify-out/.graphify_python). - references/add-watch.md
--watch— runs barepython3 -m graphify.watch; not importable underuv tool install. Must use the resolved interpreter. - references/exports.md
--mcp—python3 -m graphify.serveand the generated Claude Desktop configcommandboth point at system python; not importable. Must use the resolved interpreter / its absolute path.
B. Environment variables set but never exported — child Python reads os.environ and never sees the override
- references/transcribe.md —
GRAPHIFY_WHISPER_MODEL=baseset as an unexported shell var; transcriber silently uses the default model. - references/transcribe.md — Step 1 sets
WHISPER_PROMPTbut the transcriber readsGRAPHIFY_WHISPER_PROMPT; domain hint silently ignored (name mismatch + missingexport).
C. --directed not propagated — directed builds silently produce undirected graphs
- SKILL.md Steps 4/5 —
build_from_json(extraction)omitsdirected=, so--directedbuilds undirected. - references/update.md merge — the update-only merge calls
build_merge(...)withoutdirected=;build_merge(..., directed: bool = False)(build.py:448) then builds undirected viabuild(all_chunks, directed=directed). On--directed --update, reciprocal A↔B edges collapse to one when the merged graph is written back throughG.edges(), and the later directed rebuild cannot recover the dropped edge.
D. Output path uses scan-root, but later steps read cwd graphify-out/ (or vice-versa)
- SKILL.md (CHUNK_PATH) — chunk output path is derived from
.graphify_root(the scanned dir) but Step B3 merges from cwdgraphify-out/; a non-cwd scan (graphify <subdir>) writes chunks where the merge never looks → "no nodes". - references/add-watch.md ingest —
/graphify add <url>ingests into cwdPath('./raw'), but the following--updatescans$(cat .graphify_root); a non-cwd build saves the file outside the scanned root → "no new files". Ingest intoroot/'raw'.
E. File-list / category scoping
- SKILL.md Step B0 —
all_filesflattens everydetect['files']category, includingcode, into the semantic-extraction list.codeis already covered structurally by the AST pass, so LLM subagents re-read every source file — a repo-root run with even one README becomes unexpectedly expensive. Scope to content categories only ({document, paper, image};video→documentvia transcription). - references/update.md / transcribe.md — on
--update, the non-code branch jumps straight to extraction without running transcription, so changed.mp4/.mp3stay infiles.videoand feed unreadable media paths into semantic extraction (and a video-only corpus hits the code-only fast path → empty graph). Route media through transcription, then rewrite.graphify_detect.jsonto move transcripts intofiles.documentand dropfiles.video. - SKILL.md ~230 (stale semantic cache) —
.graphify_cached.jsonis only (re)written whencheck_semantic_cachereturns hits (if cached_nodes or ...:). If a prior run took the all-cached shortcut and skipped Part B cleanup, the file is left behind; on a later run with zero cache hits but some uncached files the conditional is false, the stale file survives, and Step B3 merges its unrelated nodes/edges into the new graph. Always rewrite (empty) or delete the cache file before the check.
F. Cleanup deletes intermediate files that later steps still read
- SKILL.md / code-only path — code-only runs skip Part C but the merge reads
.graphify_semantic.json/.graphify_extract.json→FileNotFoundError. Write an empty semantic file and run Part C so the merge input exists. - references/update.md
--cluster-only— instructs "run Steps 5–9 as normal", but a prior build's Step 9 deletes.graphify_{extract,detect,analysis}.jsonthat Step 5 reads →FileNotFoundError. Thegraphify cluster-onlyCLI is self-contained (re-clusters, regenerates outputs) — rely on it, don't re-run Steps 5–9. - references/update.md update tail — "run Steps 4–9 as normal" then "after Step 4 show the diff", but Step 9 cleanup deletes
.graphify_extract.jsonwhich the diff block reads → every successful update fails at the diff. Order must be Steps 4–8 → diff (+rm .graphify_old.json) → Step 9 last.
G. graph.json overwrite / shrink-guard safety (#479)
- references/update.md prune —
prunewas built fromdeleted + changed.build_mergealready drops every re-extracted source_file from the base before merging (re-extracted files replace their prior contribution), then appliesprune_sourcesto the final merged graph — so passingchangedfiles inprunedeletes the freshly re-extracted nodes, leavinggraph.jsonmissing that file.pruneshould be deleted-files-only. - SKILL.md Step 4 — the
to_json(...)return value was ignored; it returnsFalseand writes nothing when the new graph is smaller than the existinggraph.json(#479shrink-guard, export.py). A deletion--updatethen saved the manifest/report over a stalegraph.json. Capture the return; abort loudly on refusal. - SKILL.md Step 4 (zero-node guard) — the empty-extraction guard ran after
GRAPH_REPORT.md,to_json(force=...), and.graphify_analysis.jsonwere written, so an empty extraction on--update(withforce=True) overwrote a goodgraph.jsonbefore aborting. Move theG.number_of_nodes() == 0check to immediately afterbuild_from_json(...), before any write. - SKILL.md
is_update—forcewas decided by the mere presence of.graphify_old.json. An update that aborts after creating the backup but before cleanup leaves the file behind, so a later plain rebuild reads it, passesforce=True, and bypasses the shrink-guard. Decideforcefrom an explicit substitutedIS_UPDATEflag instead (fail-safe: guard stays on if unset). - SKILL.md ~438 (report before export) —
GRAPH_REPORT.mdis written before theto_jsonshrink-guard runs. On a non---updaterebuild that produces fewer nodes,to_jsonreturnsFalseand aborts without replacinggraph.json, but the report has already been overwritten — leaving the report describing a graph that queries (still on the oldgraph.json) never see. Write the report only after a successful export (or stage it in a temp file).
H. Misc
- references/extraction-spec.md — schema example uses a forbidden filename-only node ID (
session_validatetoken), which the spec itself warns creates ghost/duplicate nodes. Use the immediate-parent+filename form (auth_session_validatetoken). - references/query.md fallback traversal — token filter
len(t) > 3drops valid 3-letter vocabulary tokens (api,jwt,ios); the vocab threshold is>= 3. Align tolen(t) >= 3. - references/transcribe.md — the transcription block redirects all Python stdout to
.graphify_transcripts.jsonvia shell> ...json, buttranscribe_all/download_audioprint progress to stdout → polluted JSON → the nextjson.loadsfails. Write the file from Python (Path.write_text(json.dumps(...))), send status to stderr.
Environment: graphifyy v0.8.41 (PyPI), installed via uv tool install graphifyy; skill generated by graphify claude install --platform claude. All line references are to the generated v0.8.41 skill files.
Activity
Fix crash/data-loss bugs in the generated skill runbooks (#1392)
Fix remaining #1392 skill bugs: --directed propagation, content-only …
safishamsi commentedon Jun 19, 2026
Update: the remaining non-crash bugs are now fixed on
v8(382b669), following the crash/data-loss tier (30fe8ba).This batch (Claude-path skill fragments):
build_from_json/build_mergenow takedirected=IS_DIRECTEDin Step 4, the Step 5 rebuild, and the--updatemerge + diff. Prose tells the agent to substituteIS_DIRECTEDexactly likeINPUT_PATH. A--directed(and--directed --update) run no longer silently rebuilds undirected and collapses reciprocal edges.document/paper/image; code is already covered structurally by the AST pass, so subagents stop re-reading every source file..graphify_cached.jsonis deleted on a cache miss, so Part C never merges a stale cache from a prior run.--updatenow transcribes changed video files and moves transcripts into documents before the semantic pipeline.graspologicdependency pulls ingensim<=3.9which fails to build on Python 3.12+ #5/feat: add Factory Droid platform support #23 transcribe — writes viawrite_text(no shell redirect), usesGRAPHIFY_WHISPER_MODEL/GRAPHIFY_WHISPER_PROMPT, status to stderr.add-watchandexportsuse$(cat graphify-out/.graphify_python)explicitly; MCP Desktop config documents the absolute interpreter path.Triage of the 23 numbered items: 17 real (now all fixed), 2 already fixed via #1366 (#15/#16), 4 not-a-bug (#1/#9/#17/#19 — covered by the existing "replace
python3with the resolved interpreter" instruction).Note on Aider/Devin: those monoliths are pinned by a roundtrip invariant and were intentionally excluded here. Their own Step 1 already instructs replacing
python3with the resolved interpreter, so the interpreter items don't apply; the other items there are tracked as a separate follow-up that bumps the roundtrip reference. Will ship in the next release.safishamsi commentedon Jun 19, 2026
All actionable items are fixed and released in 0.8.44 (live on PyPI). Upgrade with
uv tool install --upgrade graphifyy(orpip install -U graphifyy), thengraphify installto refresh the skill.Final triage of the 23 numbered items: 17 real (all fixed), 2 already fixed, 4 not-a-bug.
Crash/data-loss tier (commit 30fe8ba):
.graphify_semantic.jsonbefore Part C (no moreFileNotFoundError)--cluster-onlyuses the self-containedgraphify cluster-onlyCLI instead of re-running steps that read deleted intermediatesto_jsonpersisted the graph (respects the Prevent partial-chunk overwrite in --update: pre-build node-count assertion + build_merge() helper #479 shrink-guard)Remaining correctness tier (commit 382b669):
--directedpropagated intobuild_from_json(build + rebuild) andbuild_merge(--updatemerge + diff), so reciprocal edges are no longer collapseddocument/paper/image(code covered by the AST pass).graphify_cached.jsondeleted on cache miss so stale caches are never merged--updatetranscribes changed video into documents before the semantic passwrite_text, honoursGRAPHIFY_WHISPER_MODEL/GRAPHIFY_WHISPER_PROMPT, status to stderradd-watch/exportsuse the resolved interpreter explicitly; MCP Desktop config documents the absolute interpreter pathAlready fixed before this batch (via #1366, shipped 0.8.42): #15, #16.
Not-a-bug (the skill's Step 1 already instructs replacing
python3with the resolved interpreter$(cat graphify-out/.graphify_python), which covers these): #1, #9, #17, #19.Each fix was smoke-tested locally against the real library/CLI (directed reciprocal-edge preservation, the
to_jsonshrink-guard refusing to clobber a larger graph, content-only file scoping, cache unlink-on-miss, andcluster-onlyregenerating the report with all intermediates deleted), in addition to the full unit suite (2210 passed).Note: the Aider and Devin monolith skills are pinned by a roundtrip invariant and were intentionally excluded from the runbook edits; their own Step 1 already resolves the interpreter, so the interpreter items don't apply there. Any monolith-specific follow-up will bump the roundtrip reference separately.
Thanks for the unusually thorough report. Closing as resolved; please reopen if anything in 0.8.44 still misbehaves.
safishamsi commentedon Jun 19, 2026
Follow-up: the Aider and Devin monolith skills now carry these fixes too (commit applied on v8, will ship in the next release).
Those two are hand-maintained single-file skills, frozen against a pinned pristine-v8 blob by a round-trip guard, so they were excluded from the first pass. I evolved the guard from a positional, line-count-exact check into a multiset diff that classifies every added/removed line against documented sanctioned change-classes - so the multi-line fixes can land while any unsanctioned drift still fails the test.
Both monoliths now: thread
directed=IS_DIRECTEDthrough everybuild_from_jsoncall (a--directedrun no longer collapses reciprocal edges), scope semantic extraction to document/paper/image, unlink a stale.graphify_cached.jsonon a cache miss, and run Step 4's zero-node guard before any write with the report/analysis gated onto_jsonpersisting the graph.Verified by extracting the rendered monolith's Step 4 block and running it: an empty extraction now fires the guard before any write and leaves a good existing graph and report untouched. That closes the last gap noted in the previous comment (the monolith exclusion).
Apply the #1392 runbook fixes to the Aider and Devin monolith skills
Merge origin/v8: keep Solidity in Unreleased, adopt 0.8.44 (Graphify-…
Write a portable manifest.json from the skill runbooks (#1417)
fenghaitao commentedon Jun 24, 2026
On E.10: SKILL.md Step B0 — all_files flattens every detect['files'] category, including code, into the semantic-extraction list. code is already covered structurally by the AST pass, so LLM subagents re-read every source file — a repo-root run with even one README becomes unexpectedly expensive. Scope to content categories only ({document, paper, image}; video → document via transcription).
This should bring a regression. We have the cross doc<->code edges before this change, and then after this, those edges are gone. When we give the code and doc together and ask LLM to extract, we will create entities from the code, and those entities could be merged to the AST if their ids are identifcal, after that, the edges from the doc will be pointed to the AST node.
Should we give the code index.md and then update the prompt so that we could get those edges back?
And after this change, are the code session in graphify/skills/claude/references/extraction-spec.md dead?
Fix crash/data-loss bugs in the generated skill runbooks (Graphify-La…
Fix remaining Graphify-Labs#1392 skill bugs: --directed propagation, …
Apply the Graphify-Labs#1392 runbook fixes to the Aider and Devin mon…
Write a portable manifest.json from the skill runbooks (Graphify-Labs…
Sanction the no cluster label fix comment for the monolith guard
fix(skill): harden pipeline subcommands after review (Graphify-Labs#197
fix(skill): harden pipeline subcommands after review (Graphify-Labs#197
fix(skill): harden pipeline subcommands after review (Graphify-Labs#197
Sanction the no cluster label fix comment for the monolith guard
fix(skill): harden pipeline subcommands after review (Graphify-Labs#197
fix(skill): harden pipeline subcommands after review (Graphify-Labs#197