Fix semantic node preservation on incremental rebuild and detach git hooks

watch.py: filter preserved nodes by ID membership in new AST output instead
of file_type — INFERRED/AMBIGUOUS nodes on code files also carry file_type=code
and were being wrongly dropped, triggering the to_json safety check refusal.

hooks.py: detach post-commit and post-checkout rebuilds with nohup + disown
so git commit returns immediately instead of blocking for the full rebuild
duration. Rebuild log written to ~/.cache/graphify-rebuild.log.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Safi
2026-05-02 08:53:17 +01:00
co-authored by Claude Sonnet 4.6
parent be83a8cc55
commit c8969edd3c
2 changed files with 28 additions and 13 deletions
+15 -5
View File
@@ -61,7 +61,13 @@ fi
""" + _PYTHON_DETECT + """
export GRAPHIFY_CHANGED="$CHANGED"
$GRAPHIFY_PYTHON -c "
# Run rebuild detached so git commit returns immediately.
# Full repo rebuilds can take hours; blocking the post-commit hook stalls the shell.
_GRAPHIFY_LOG="${HOME}/.cache/graphify-rebuild.log"
mkdir -p "$(dirname "$_GRAPHIFY_LOG")"
echo "[graphify hook] launching background rebuild (log: $_GRAPHIFY_LOG)"
nohup $GRAPHIFY_PYTHON -c "
import os, sys
from pathlib import Path
@@ -79,7 +85,8 @@ try:
except Exception as exc:
print(f'[graphify hook] Rebuild failed: {exc}')
sys.exit(1)
"
" > "$_GRAPHIFY_LOG" 2>&1 < /dev/null &
disown 2>/dev/null || true
# graphify-hook-end
"""
@@ -111,8 +118,10 @@ GIT_DIR=$(git rev-parse --git-dir 2>/dev/null)
[ -f "$GIT_DIR/CHERRY_PICK_HEAD" ] && exit 0
""" + _PYTHON_DETECT + """
echo "[graphify] Branch switched - rebuilding knowledge graph (code files)..."
$GRAPHIFY_PYTHON -c "
_GRAPHIFY_LOG="${HOME}/.cache/graphify-rebuild.log"
mkdir -p "$(dirname "$_GRAPHIFY_LOG")"
echo "[graphify] Branch switched - launching background rebuild (log: $_GRAPHIFY_LOG)"
nohup $GRAPHIFY_PYTHON -c "
from graphify.watch import _rebuild_code
from pathlib import Path
import sys
@@ -121,7 +130,8 @@ try:
except Exception as exc:
print(f'[graphify] Rebuild failed: {exc}')
sys.exit(1)
"
" > "$_GRAPHIFY_LOG" 2>&1 < /dev/null &
disown 2>/dev/null || true
# graphify-checkout-hook-end
"""
+13 -8
View File
@@ -60,20 +60,25 @@ def _rebuild_code(watch_path: Path, *, follow_symlinks: bool = False) -> bool:
result = extract(code_files, cache_root=watch_root)
# Preserve semantic nodes/edges from a previous full run.
# AST-only rebuild replaces code nodes; doc/paper/image nodes are kept.
# AST-only rebuild replaces nodes for changed files; everything else is kept.
# Filter by node ID membership in the new AST output, not by file_type —
# INFERRED/AMBIGUOUS nodes extracted from code files also carry file_type="code"
# and would be wrongly dropped by a file_type-based filter.
out = watch_path / "graphify-out"
existing_graph = out / "graph.json"
if existing_graph.exists():
try:
existing = json.loads(existing_graph.read_text(encoding="utf-8"))
code_ids = {n["id"] for n in existing.get("nodes", []) if n.get("file_type") == "code"}
sem_nodes = [n for n in existing.get("nodes", []) if n.get("file_type") != "code"]
sem_edges = [e for e in existing.get("links", existing.get("edges", []))
if e.get("confidence") in ("INFERRED", "AMBIGUOUS")
or (e.get("source") not in code_ids and e.get("target") not in code_ids)]
new_ast_ids = {n["id"] for n in result["nodes"]}
preserved_nodes = [n for n in existing.get("nodes", []) if n["id"] not in new_ast_ids]
all_ids = new_ast_ids | {n["id"] for n in preserved_nodes}
preserved_edges = [
e for e in existing.get("links", existing.get("edges", []))
if e.get("source") in all_ids and e.get("target") in all_ids
]
result = {
"nodes": result["nodes"] + sem_nodes,
"edges": result["edges"] + sem_edges,
"nodes": result["nodes"] + preserved_nodes,
"edges": result["edges"] + preserved_edges,
"hyperedges": existing.get("hyperedges", []),
"input_tokens": 0,
"output_tokens": 0,