Skip to content

Incremental rebuild drops cross-file references edges between Markdown docs; GRAPHIFY_FORCE does not help in the hook (follow-up to #1018) #2211

Closed

Description

@Jolex56

Summary

An incremental rebuild re-extracts a changed Markdown file but does not re-establish that file's cross-file references edges to unchanged neighbour documents. The links are still literally present in the text — only the graph loses them. graphify update . --force restores all of them, so the extraction itself is fine; the incremental merge is what drops them.

Because the post-commit hook rebuilds incrementally after every commit that touches a graph-tracked file — including .md files — the doc half of the graph erodes a little with every documentation commit, silently.

Measurement (graphifyy 0.9.26)

A commit editing four Markdown files (CLAUDE.md, docs/FEATURE-INVENTORY.md, docs/features/overlay.md, docs/features/configuration.md) in a 44-file repo:

before: 408 nodes / 693 edges
after: 409 nodes / 676 edges <- 18 edges lost, 1 node gained

All 18 lost edges are references edges whose source is one of the four edited files, and whose target is an unchanged document:

CLAUDE.md --references--> diagnostics.md, input.md, output.md,
pythonsidecar.md, recognition.md, setup.md (6)
FEATURE-INVENTORY.md --references--> diagnostics.md, input.md, output.md,
recognition.md, setup.md, history.md (6)
overlay.md --references--> diagnostics.md, output.md, pythonsidecar.md,
recognition.md, setup.md (5)
configuration.md --references--> output.md (1)

No code node and no edge outside those four source files was affected.

The links were never removed from the text. Counted with grep in the post-rebuild working tree:

CLAUDE.md -> docs/features/setup.md 10 occurrences
CLAUDE.md -> docs/features/diagnostics.md 2 occurrences
FEATURE-INVENTORY.md -> features/overlay.md 28 occurrences
FEATURE-INVENTORY.md -> features/recognition.md 16 occurrences
(etc. — every dropped target still linked)

Running graphify update . --force immediately afterwards, with no further file changes:

409 nodes / 695 edges -> all 18 edges restored (plus 2 new ones for content added in the same commit)

No cost.json was written, so this is the plain AST path — the edges are structurally extractable, they are simply not re-linked on the incremental path.

GRAPHIFY_FORCE=1 does not mitigate this in the hook

Setting GRAPHIFY_FORCE=1 as a persistent environment variable had no effect (verified: variable present in the committing shell, edges still dropped 695 -> 687 on the next doc commit). The installed post-commit script always passes an explicit changed-file list:

sh
CHANGED=$(git diff --name-only HEAD~1 HEAD 2>/dev/null || git diff --name-only HEAD 2>/dev/null)
export GRAPHIFY_CHANGED="$CHANGED"
python
_force = os.environ.get('GRAPHIFY_FORCE', '').lower() in ('1', 'true', 'yes')
_rebuild_code(_root, changed_paths=changed, force=_force)

Since changed_paths is always supplied, force only lifts the overwrite/fail-closed guard — it does not widen the re-extraction scope the way the CLI's graphify update . --force (no changed_paths) does. So there is currently no way to get the hook to produce a correct doc graph.

Relation to #1018

#1018 (closed) asked for the hook to "skip when no files matching CODE_EXTENSIONS changed (docs-only commits → manual refresh, as documented)". In 0.9.26 that is only partially implemented: the hook does skip when nothing graph-tracked changed — a docs-only commit under a .graphifyignored path correctly logged

1 file(s) changed → No tracked code files in change set - skipping rebuild

— but the filter keys on graph-tracked files, not on code files. A commit touching only tracked .md files therefore does rebuild, contrary to the documented intent, and that rebuild is exactly what degrades the graph. Closing #1018's point 2 properly would incidentally fix this.

Possibly also related: #857 (semantic references edges never re-inferred for files edited between passes).

Impact
Silent, cumulative loss of doc→doc navigation edges — the part of the graph that makes documentation traversable. Nothing warns; node counts even go up while edges go down.
For repos that track graphify-out/ in git, every doc commit additionally requires a manual --force plus a second repair commit, or the degraded graph gets committed.
Practical workaround we ended up with: disable the hook entirely via GRAPHIFY_SKIP_HOOK=1 and run graphify update . --force manually before each commit. That works, but it means the auto-rebuild feature is unusable for any repo with a meaningful amount of cross-linked documentation.
Suggested fixes (in order of preference)
On the incremental path, re-resolve a re-extracted file's outgoing cross-file references against the existing graph, instead of only linking to files that were re-extracted in the same run.
Implement #1018 point 2 as specified: skip the hook rebuild when no CODE_EXTENSIONS file changed.
Make GRAPHIFY_FORCE=1 widen the hook's scope (drop changed_paths), so the documented force-escape hatch also works from the hook.
Environment
graphifyy 0.9.26 (PyPI, installed via uv tool install graphifyy)
Windows 11, Python 3.12.10
44 tracked files (C#/XAML/Python code plus 12 Markdown documents), graphify-out/ tracked in git
.graphifyignore present (full copy of .gitignore patterns plus docs/reviews/)

Activity

  1. safishamsi commented on Jul 27, 2026

    @safishamsi
    Member

    Fixed in v0.9.28. Markdown reference links now stamp their resolved target, so an incremental rebuild canonicalizes the target to the root-relative node instead of dropping the md->md references edge. https://github.com/Graphify-Labs/graphify/releases/tag/v0.9.28

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions