fix(cli): resolve the graph from the primary checkout in linked worktrees (#2008)#2064
fix(cli): resolve the graph from the primary checkout in linked worktrees (#2008)#2064HerenderKumar wants to merge 1 commit into
Conversation
…rees (Graphify-Labs#2008) Read commands (query/explain/path/affected) resolve their default graph to <GRAPHIFY_OUT>/graph.json relative to CWD. Run from a linked git worktree — increasingly the default for AI-agent task isolation — that path does not exist, so the command failed hard with "graph file not found" and callers had to wrap every invocation in a `git rev-parse --git-common-dir` shim. When the local graph is absent and CWD is inside a linked worktree (git-dir != git-common-dir, the same absolute compare the commit/checkout hooks already use), fall back to the primary checkout's graph. Read-only by construction: writers resolve through graphify.paths, an explicit --graph still wins, an absolute GRAPHIFY_OUT short-circuits, and an absent primary graph keeps the original "run extract" error. The git probe runs only on the cold path (no local graph), leaving the common case untouched.
| return str(local) | ||
|
|
||
|
|
||
| def _worktree_primary_graph() -> str | None: |
There was a problem hiding this comment.
Nice helper function. Would it be worth adding a short docstring explaining why graph.json is the default output path?
There was a problem hiding this comment.
Thanks !!! Actually both new functions already carry docstrings, If I mention specifically _default_graph_path (lines 85–93) and _worktree_primary_graph (lines 104–113). _default_graph_path explains why <GRAPHIFY_OUT>/graph.json is the default and how the worktree fallback kicks in, and _worktree_primary_graph documents the git-dir vs. git-common-dir check and every case where it returns None. Btw I am happy to extend further if any part seems less explanatory.
| are run from a linked git worktree that has no ``graphify-out/`` of its own. | ||
|
|
||
| Agent workflows (Claude Code etc.) increasingly run every task in its own | ||
| worktree, where ``graphify explain``/``query``/``path``/``affected`` used to fail |
There was a problem hiding this comment.
Using Path here improves readability. Is there any scenario where _GRAPHIFY_OUT could be missing or invalid? If so, should that be handled explicitly?
There was a problem hiding this comment.
Thanks !!! Agreed on Path. On _GRAPHIFY_OUT — it isn't a value that gets passed in while the program runs. It's a fixed setting loaded once at the top of the file (line 14), so it's always a valid string (defaults to graphify-out). If it were ever wrong, the program wouldn't even start — it'd fail right at import, not inside this function. The only tricky case is when it's set to an absolute path, and that's already handled at line 114 (the os.path.isabs check). So there's nothing extra to handle here. That said, I'm happy to add a check if you'd still like one just to be safe.
Summary
Fixes #2008. The read commands (
query/explain/path/affected) resolve their default graph to<GRAPHIFY_OUT>/graph.jsonrelative to CWD. Run from a linked git worktree — increasingly the default for AI-agent task isolation (Claude Code etc.) — that path does not exist, so the command fails hard withGraph file not foundand callers have to wrap every invocation in agit rev-parse --git-common-dirshim.Fix
cli.py::_default_graph_path()— used only by the read commands; writers resolve throughgraphify.paths— gains a worktree fallback: when the local graph is absent and CWD is inside a linked worktree (--git-dir!=--git-common-dir, the same absolute compare the commit/post-checkout hooks already use for #1809), resolve to the primary checkout's graph.Read-only and surgical by construction:
extract/build) never touch this helper — a write is never redirected;--graphstill overrides;GRAPHIFY_OUTshort-circuits (already shared across worktrees);git rev-parseprobes run only on the cold path (no local graph), so the common case is untouched.Testing
New
tests/test_worktree_graph_path.py(realgit worktree, no grammar deps):origin/v8);tests/test_explain_cli.py+tests/test_affected_cli.pypass unchanged.