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

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions