Skip to content

_infer_merge_root trusts .graphify_root without checking it exists, so a committed marker beats the portable fallback #3691

Description

@Phemdot

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

  1. Build a graph, commit graphify-out/ per the Team setup section.
  2. cat graphify-out/.graphify_root/Users/alice/Documents/project
  3. Clone to /home/bob/project on another machine.
  4. 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.

Activity

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