Fall back to the main checkout's graph from a git worktree (#2008) - #3635
ayushcodes10 wants to merge 3 commits into
Conversation
A worktree shares history with the main checkout but has no graphify out directory of its own, so a bare query, explain, path, affected, or god nodes run failed with "graph file not found" even though the graph exists one worktree away. AI agent workflows that run every task in its own worktree hit this constantly, and previously needed a manual cd wrapper carried in project rules. The default graph path now checks the usual location relative to the current directory first, and only when that is absent, resolves git's shared common directory and looks for the graph in its parent, the main checkout's root. This is read only and degrades silently to the ordinary path on any failure: not a git repository, git missing from PATH, or no graph anywhere. A worktree that has its own graph built directly in it still uses that one, not the fallback. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Builds real git repos and worktrees under tmp_path rather than mocking subprocess, since the point is git's own common directory resolution behaving as expected. Covers the worktree fallback firing, a main checkout with no graph anywhere returning the plain default, a worktree with no graph anywhere doing the same, a directory that is not a git repository at all, and a worktree that has its own graph preferring that one over the fallback. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Graphify reviewed this change.
Worth a look — the grounded gate found no coupling regressions or blocking issues, but 2 advisory finding(s) below merit a look before merge.
Formal verification. 1 change(s) tested, no difference found (not proven).
Graphify review — findings
Resolves the default graph path to the main checkout's graphify-out/graph.json when a read-only command runs inside a git worktree that has no graphify-out/ of its own, so query/explain/path/etc. no longer fail with "graph file not found". _worktree_graph_fallback shells out to git rev-parse --git-common-dir and looks in its parent, firing only when the local candidate is absent and returning None (leaving the plain error to surface) on any git failure or when no graph exists anywhere.
Worth a look
- git-common-dir parent is not the main checkout root for a worktree —
graphify/cli.py:111· Escalate · medium- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
- Worktree fallback assumes the common git dir is inside the main checkout —
graphify/cli.py:111· Escalate · medium- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
Analysis details — impact, health, verification
Impact & health
Graphify review
Impact — 495 functions depend on the 276 functions this change touches.
Health — this change adds coupling hotspots:
- new:
dispatch_command()— 2 callers, 125 callees - new:
_stale_graph_sources()— 7 callers, 6 callees - new:
_run_hook_guard()— 4 callers, 8 callees - new:
test_poisoned_manifest_is_healed()— 0 callers, 6 callees
Verification — 495 functions in the blast radius were not formally verified this run (proofs are advisory here).
Gate & verification
graphify gate
PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.
Advisory (not blocking):
- verification_scope: 433 function(s) in the blast radius were not formally verified this run
Test selection
Test selection
287 of 287 test file(s) selected (100%) via static blast radius.
Escalated to a full run for safety — the selection is not trustworthy on its own (see below). CI should run the whole suite.
tests/test_affected_cli.py— impact, full-run-safetytests/test_affected_member_seed.py— full-run-safetytests/test_agents_platform.py— impact, full-run-safetytests/test_analyze.py— full-run-safetytests/test_anthropic_custom_endpoint.py— full-run-safetytests/test_antigravity_install.py— full-run-safetytests/test_apm_fallback_version.py— full-run-safetytests/test_architecture_doc.py— full-run-safetytests/test_astro_extraction.py— full-run-safetytests/test_astro_import_ids.py— full-run-safetytests/test_atomic_canvas_export.py— full-run-safetytests/test_atomic_version_stamp.py— full-run-safetytests/test_atomic_writes.py— full-run-safetytests/test_backend_env_isolation.py— full-run-safetytests/test_backend_extras.py— full-run-safetytests/test_benchmark.py— full-run-safetytests/test_benchmark_raw_graph.py— full-run-safetytests/test_build.py— full-run-safetytests/test_build_merge_dedup_scope.py— full-run-safetytests/test_build_merge_hyperedges_and_prune.py— full-run-safetytests/test_build_merge_shrink_guard.py— full-run-safetytests/test_builtin_global_type_refs.py— full-run-safetytests/test_cache.py— full-run-safetytests/test_callflow_html.py— full-run-safetytests/test_cargo_introspect.py— full-run-safetytests/test_carried_hyperedge_remap.py— full-run-safetytests/test_case_sensitive_resolution.py— full-run-safetytests/test_charmap_encoding.py— full-run-safetytests/test_chunking.py— full-run-safetytests/test_cjs_module_extension.py— full-run-safetytests/test_claude_cli_backend.py— full-run-safetytests/test_claude_md.py— full-run-safetytests/test_cli_broken_pipe.py— full-run-safetytests/test_cli_export.py— full-run-safetytests/test_cli_help.py— full-run-safetytests/test_cluster.py— full-run-safetytests/test_codebuddy.py— impact, full-run-safetytests/test_community_hub_labels.py— full-run-safetytests/test_community_labels_skill.py— full-run-safetytests/test_confidence.py— full-run-safetytests/test_corrupt_graph_json.py— full-run-safetytests/test_cpp_nested_and_cli.py— full-run-safetytests/test_cpp_objc_cross_file_calls.py— full-run-safetytests/test_cpp_preprocess.py— full-run-safetytests/test_cross_extension_reexport_self_cycle.py— full-run-safetytests/test_cross_language_call_resolution.py— full-run-safetytests/test_cross_repo_external_call_guards.py— full-run-safetytests/test_cross_repo_member_calls.py— full-run-safetytests/test_cross_repo_shared_types.py— full-run-safetytests/test_csharp_call_site_generic_args.py— full-run-safety- … and 237 more
non-code file(s) changed (
CHANGELOG.md) → running the full suite for safety (a code graph can't see config/fixture/data deps)
changed code file(s) with no mapped test (
CHANGELOG.md) — a coverage gap or a missing link — running the full suite rather than only the selected tests
Selection is safe under the controlled-regression assumption; always-run tests + a periodic full run are the backstops. Advisory — it never changes the check verdict.
Formal verification
No difference found (not proven): No behavior difference found in \_default\_graph\_path (not a proof).
The verifier ran both versions of \_default\_graph\_path on many inputs and saw identical behavior every time. Strong evidence the change is safe, but evidence, not a proof.
Guarantee: Empirical: differential testing (both versions run on many generated inputs). A divergence on an untested input remains possible, so this is 'no counterexample found', not 'proven equivalent'.
Note: An input the sampler did not try could still differ.
· 4 more finding(s) on lines outside this diff (see the check run).
|
Checked both flagged findings against the code. For the ordinary case (a linked worktree off a normal checkout), The one real edge case is a checkout set up with a separate git dir ( |
Summary
Fixes #2008. The graph (`graphify-out/`) lives in the checkout that built it, but a bare `graphify explain`/`query`/`path`/`affected`/`god-nodes` run from inside a git worktree failed with "graph file not found," since the worktree has no `graphify-out/` of its own. AI-agent workflows that run every task in its own worktree hit this constantly, and previously needed a manual `cd` wrapper carried in project rules that breaks when the cwd disappears mid-session.
Fix
`_default_graph_path()` (the shared default every read-only graph command falls back to when `--graph` isn't given) now:
This is read-only and degrades silently to the ordinary (non-existent) default path on any failure: not a git repository, git missing from `PATH`, or no graph anywhere — so the existing "graph file not found" error still surfaces normally in those cases, just with one more location checked first. A worktree that has its own graph built directly in it (not the common case, but possible) still uses that one, not the fallback.
Test plan
🤖 Generated with Claude Code