Bug: _rebuild_code (post-commit/post-checkout hook path) silently discards the semantic graph
Summary
graphify.watch._rebuild_code() — the function the installed post-commit/post-checkout git hooks call — builds a graph from only the currently-detected code files via AST extraction, then unconditionally overwrites graphify-out/graph.json with it. If a full /graphify run has already populated the graph with semantic doc/paper/image nodes (from the LLM extraction path), a subsequent code-only hook rebuild destroys all of that, silently, with no warning.
This makes the hook actively dangerous on any repo that mixes code and docs (e.g. a markdown-heavy knowledge base with a handful of scripts): every commit that touches a code file wipes the semantic graph back down to code-AST-only.
Reproduction
- Run a full
/graphify pass on a mixed corpus (docs + a few code files) — confirm graph.json has both code and semantic (document-type) nodes.
- Install the git hooks:
graphify hook install.
- Commit a change to any code file (
.py, .ts, etc. — anything in CODE_EXTENSIONS).
- Inspect
graphify-out/graph.json — the semantic nodes are gone. Only the AST-extracted code nodes remain.
Root cause
In graphify/watch.py::_rebuild_code:
G = build_from_json(result) # built from code_files ONLY
communities = cluster(G)
...
to_json(G, communities, str(out / "graph.json")) # unconditional overwrite
There's no attempt to load the existing graph.json and merge; G is built from scratch every time.
Suggested fix
Load the existing graph (if present) and merge the freshly-extracted code subgraph into it, rather than replacing it wholesale:
G = build_from_json(result)
existing_path = watch_path / "graphify-out" / "graph.json"
if existing_path.exists():
try:
from networkx.readwrite import json_graph
existing_data = json.loads(existing_path.read_text())
G_existing = json_graph.node_link_graph(existing_data, edges="links")
G_existing.update(G)
G = G_existing
except Exception as merge_exc:
print(f"[graphify watch] Could not merge into existing graph.json ({merge_exc}); writing code-only graph instead.")
communities = cluster(G)
I've applied and tested this patch locally (verified: a 337-node graph with semantic content survives a simulated hook rebuild with the patch, vs. collapsing to 82 nodes without it). Happy to open a PR with this change if useful — let me know.
Secondary note (not a bug, just a heads-up)
The post-commit hook template in hooks.py only triggers on real code extensions, which is correct. Some users may have manually added .md/.mdx to their local hook's trigger list (I found this in the wild) hoping it would "refresh" the doc graph too — worth noting in docs that this doesn't do anything useful today, since _rebuild_code only ever reads code files regardless of what triggered it. Doc/paper/image extraction requires the LLM-driven /graphify --update path, which the hook correctly doesn't attempt.
Bug:
_rebuild_code(post-commit/post-checkout hook path) silently discards the semantic graphSummary
graphify.watch._rebuild_code()— the function the installedpost-commit/post-checkoutgit hooks call — builds a graph from only the currently-detected code files via AST extraction, then unconditionally overwritesgraphify-out/graph.jsonwith it. If a full/graphifyrun has already populated the graph with semantic doc/paper/image nodes (from the LLM extraction path), a subsequent code-only hook rebuild destroys all of that, silently, with no warning.This makes the hook actively dangerous on any repo that mixes code and docs (e.g. a markdown-heavy knowledge base with a handful of scripts): every commit that touches a code file wipes the semantic graph back down to code-AST-only.
Reproduction
/graphifypass on a mixed corpus (docs + a few code files) — confirmgraph.jsonhas both code and semantic (document-type) nodes.graphify hook install..py,.ts, etc. — anything inCODE_EXTENSIONS).graphify-out/graph.json— the semantic nodes are gone. Only the AST-extracted code nodes remain.Root cause
In
graphify/watch.py::_rebuild_code:There's no attempt to load the existing
graph.jsonand merge;Gis built from scratch every time.Suggested fix
Load the existing graph (if present) and merge the freshly-extracted code subgraph into it, rather than replacing it wholesale:
I've applied and tested this patch locally (verified: a 337-node graph with semantic content survives a simulated hook rebuild with the patch, vs. collapsing to 82 nodes without it). Happy to open a PR with this change if useful — let me know.
Secondary note (not a bug, just a heads-up)
The
post-commithook template inhooks.pyonly triggers on real code extensions, which is correct. Some users may have manually added.md/.mdxto their local hook's trigger list (I found this in the wild) hoping it would "refresh" the doc graph too — worth noting in docs that this doesn't do anything useful today, since_rebuild_codeonly ever reads code files regardless of what triggered it. Doc/paper/image extraction requires the LLM-driven/graphify --updatepath, which the hook correctly doesn't attempt.