Version: graphifyy 0.9.63 (verified still present on main as of 2026-09-20)
Platform: macOS 15.6 (darwin 24.6.0), installed via uv tool install graphifyy
Summary
build.py::_infer_merge_root prefers the recorded .graphify_root marker over its
grandparent fallback, and returns the recorded path without checking that it exists.
Since .graphify_root holds an absolute path and the README tells teams to commit
graphify-out/, the first clone on any other machine resolves the scan root to a
directory that isn't there — and the fallback that would have been correct is never
reached, because the marker branch returns first.
Code
parent = graph_path.parent
try:
marker = parent / ".graphify_root"
if marker.exists():
recorded = marker.read_text(encoding="utf-8-sig").strip()
if recorded:
return str(Path(recorded).resolve()) # <- no is_dir() check
except OSError:
pass
from .paths import GRAPHIFY_OUT
try:
if (
parent.name == Path(GRAPHIFY_OUT).name
or (parent / ".graphify_root").exists()
or (parent / "manifest.json").exists()
):
return str(parent.parent.resolve()) # correct on ANY checkout
The second branch derives the root from graph.json's grandparent and is correct on
every clone. It is unreachable whenever the marker is present and non-empty, which —
for any team following the README — is always.
Repro
- Build a graph, commit
graphify-out/ per the Team setup section.
cat graphify-out/.graphify_root → /Users/alice/Documents/project
- Clone to
/home/bob/project on another machine.
- Any
build_merge path that infers the root (e.g. graphify update without an explicit root) resolves /Users/alice/Documents/project, which does not exist on Bob's machine, instead of /home/bob/project.
Suggested fix
Validate before trusting, and fall through rather than returning a dead path:
if marker.exists():
recorded = marker.read_text(encoding="utf-8-sig").strip()
if recorded and Path(recorded).is_dir():
return str(Path(recorded).resolve())
# else fall through to the grandparent heuristic
Storing the marker relative to the out-dir would fix it more durably, at the cost of
migrating existing markers.
Relationship to existing issues
Not a duplicate of these, but adjacent — happy to fold it into any of them if you'd
rather:
Workaround
Gitignore graphify-out/.graphify_root and let the grandparent fallback do the work;
it resolves correctly on every machine. The rest of graphify-out/ commits fine —
manifest.json is genuinely portable, and a fresh clone takes the MD5 slow path in
_detect_incremental and matches, exactly as the README describes.
Version: graphifyy 0.9.63 (verified still present on
mainas of 2026-09-20)Platform: macOS 15.6 (darwin 24.6.0), installed via
uv tool install graphifyySummary
build.py::_infer_merge_rootprefers the recorded.graphify_rootmarker over itsgrandparent fallback, and returns the recorded path without checking that it exists.
Since
.graphify_rootholds an absolute path and the README tells teams to commitgraphify-out/, the first clone on any other machine resolves the scan root to adirectory that isn't there — and the fallback that would have been correct is never
reached, because the marker branch returns first.
Code
The second branch derives the root from
graph.json's grandparent and is correct onevery clone. It is unreachable whenever the marker is present and non-empty, which —
for any team following the README — is always.
Repro
graphify-out/per the Team setup section.cat graphify-out/.graphify_root→/Users/alice/Documents/project/home/bob/projecton another machine.build_mergepath that infers the root (e.g.graphify updatewithout an explicit root) resolves/Users/alice/Documents/project, which does not exist on Bob's machine, instead of/home/bob/project.Suggested fix
Validate before trusting, and fall through rather than returning a dead path:
Storing the marker relative to the out-dir would fix it more durably, at the cost of
migrating existing markers.
Relationship to existing issues
Not a duplicate of these, but adjacent — happy to fold it into any of them if you'd
rather:
docs: expand team-setup gitignore recommendations #1866 propose gitignoring
.graphify_rootamong other.graphify_*files. Thatfixes the symptom for teams who adopt the new guidance. This issue is about the
read side:
_infer_merge_rootshould not return an unvalidated path regardless ofwhether the marker is committed, and the docstring's own phrase "the committed
graphify-out/.graphify_rootmarker" suggests committing it is intended to work.graphify update <relative path>writes the raw argument into .graphify_root, so hook/watch scan-root recovery silently falls back to CWD (watch.py writers skip .resolve(), cli.py writers do not) #3375 covers the write side (graphify update <relative path>records the rawargument). Same file, opposite end of the lifecycle.
committed or moved output.
Workaround
Gitignore
graphify-out/.graphify_rootand let the grandparent fallback do the work;it resolves correctly on every machine. The rest of
graphify-out/commits fine —manifest.jsonis genuinely portable, and a fresh clone takes the MD5 slow path in_detect_incrementaland matches, exactly as the README describes.