Skip to content

bug: absolute paths in manifest.json, .graphify_root, and cache/ast/*.json break git-shared graphify-out/ across machines #777

Description

@ikeniborn

Problem

Committing graphify-out/ (or $GRAPHIFY_OUT) to git is the documented way to share incremental cache + graph artifacts across a team / CI. Today three artifacts contain absolute paths and break on clone to a different filesystem location:

  1. manifest.json — keys are absolute (/home/alice/proj/foo.py). detect_incremental cache miss → full rebuild on every other machine. Same observation as Question — manifest.json absolute paths + missing graphify-out/.gitignore: intended or oversight? #722 (filed there as a "question").
  2. .graphify_root — absolute project path. graphify update with no args fails (or scans the wrong tree) after clone.
  3. cache/ast/*.jsonsource_file field on cached nodes/edges is absolute. Not directly fatal but pollutes diffs and leaks user paths into commits. Not covered by Question — manifest.json absolute paths + missing graphify-out/.gitignore: intended or oversight? #722.

graph.json is already correctly relativized via watch._relativize_source_files (since 0.5.0) — proves the intent and the pattern.

Filing as a separate bug (rather than commenting on #722) because (a) #722 is question-shaped and limited to manifest + .gitignore, (b) cache/ast/*.json is a distinct artifact with the same root cause, (c) this draft includes a concrete fix + PR offer. Linking #722 to consolidate triage.

Reproduce

mkdir /tmp/p && cd /tmp/p && git init -q && echo "def f(): pass" > a.py
graphify update .

# 1) absolute keys in manifest
python3 -c 'import json; print(list(json.load(open("graphify-out/manifest.json")).keys())[:1])'
# ['/tmp/p/a.py']  ← absolute

# 2) absolute project root
cat graphify-out/.graphify_root
# /tmp/p  ← absolute

# 3) absolute source_file in cache/ast
python3 -c 'import json,glob; f=glob.glob("graphify-out/cache/ast/*.json")[0]; d=json.load(open(f)); print({n.get("source_file") for n in d.get("nodes",[])})'
# {'/tmp/p/a.py'}  ← absolute

Move /tmp/p to /tmp/q (or clone the committed graphify-out/ on another host): incremental detection misses, root resolution breaks, diffs churn on every rebuild.

Proposed fix

Three small changes mirroring the existing _relativize_source_files, each backwards compatible (relpath fallback to abspath when target escapes root):

  1. graphify/detect.py::save_manifest — relativize keys against root before serialization. Skip entries where os.path.relpath(...).startswith("..") (paths outside the root) and write them absolute, matching _relativize_source_files.
  2. graphify/watch.py::_rebuild_code — write the user-provided watch_path (often .) to .graphify_root instead of the resolved absolute watch_root.
  3. graphify/cache.py::save_cached — walk result["nodes"] + result["edges"], relativize source_file fields against root using the same helper, before JSON dump.

Symmetric loaders (load_manifest, load_cached, __main__ root resolution) re-anchor relative paths against CWD / --root — matches the pattern that already works for graph.json.

Workaround (for context)

Two-way normalize-paths.py PreToolUse/PostToolUse hook flips manifest, root, and cache/ast paths abs↔rel around every graphify invocation. Plus a small runtime shim that patches watch.py to pass manifest_path explicitly. See lib/graphify/install.sh::_patch_graphify_watch and .nvm-isolated/.claude-isolated/hooks/normalize-paths.py in iclaude. Brittle — would rather drop both once upstream lands.

PR offer

Happy to submit a single PR with the three changes + tests (test_save_manifest_relativizes_keys, test_save_cached_relativizes_source_file, test_graphify_root_preserves_relative) after triage / scope confirmation.

Related: #722.

Environment

  • graphifyy 0.7.7
  • Python 3.12 (uv-managed)
  • Project: iclaude (GRAPHIFY_OUT=.graphify committed to git)

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