Skip to content

#611: Worktree agents cannot query the graphify graph — port the .githooks/ bootstrap from claude-coordinator - #612

Merged
JDonaghy merged 4 commits into
developfrom
issue-611-worktree-agents-cannot-query-the-graphif
Jul 31, 2026
Merged

#611: Worktree agents cannot query the graphify graph — port the .githooks/ bootstrap from claude-coordinator#612
JDonaghy merged 4 commits into
developfrom
issue-611-worktree-agents-cannot-query-the-graphif

Conversation

@JDonaghy

Copy link
Copy Markdown
Owner

Closes #611

Automated PR opened by coordinator for review of issue #611.

JDonaghy and others added 4 commits July 31, 2026 13:59
Agents working in a git worktree here are graph-blind: graphify-out/ is
gitignored (only its .gitignore is tracked), so `git worktree add`
materialises an empty graphify-out/, and `graphify query` resolves
graph.json strictly relative to cwd with no upward walk or override.

Port the versioned post-checkout/post-commit/post-merge hooks that
claude-coordinator arrived at in PRs #1613/#1614: on `git worktree add`,
post-checkout symlinks the new worktree's graphify-out at the base
checkout's graph and never rebuilds there; post-commit/post-merge shim
past the fact that core.hooksPath replaces .git/hooks wholesale, chaining
to the machine-local graphify hook outside linked worktrees. Logic is
unchanged from upstream; only the comments naming claude-coordinator-specific
tooling were adapted.

Adds an integration test that drives the hooks through real git in a temp
repo, covering: worktree add creates the symlink, no dangling symlink when
the base has no graph yet, a worktree-local graph is never clobbered, the
hooks are committed as mode 100755, and (anti-vacuity) that post-commit/
post-merge actually run inside a linked worktree rather than silently
no-opping.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- hooks_are_committed_as_executable now reads git ls-files against
  CARGO_MANIFEST_DIR's real index instead of a scratch repo that was
  force-chmod'd before asserting on it, so it can actually catch a hook
  losing its executable bit in a real commit.
- post_commit_and_post_merge_shims_* now installs fake machine-local
  hooks and asserts they do/don't fire, distinguishing "skipped because
  linked worktree" from "chained to a hook that was never installed".
- Gate the whole integration-test module on #![cfg(unix)] since it
  drives real sh hooks, symlinks, and POSIX modes.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Ports the corrected .githooks/post-checkout from claude-coordinator
(issue #1617, commit c4d1f35) rather than re-deriving it.

The version originally ported here did `rm -rf graphify-out && ln -sfn
$base/graphify-out graphify-out`, replacing the whole directory with a
machine-local, absolute-path symlink. `git worktree add` had already
checked out the tracked `graphify-out/.gitignore` -- the only tracked
file in that directory, and what makes it self-ignoring -- so the
`rm -rf` deleted a tracked file out from under git. Every fresh worktree
then showed a deleted `.gitignore` plus an untracked symlink, and the
worktree-rescue sweep committed both onto unrelated worker branches.
That is not hypothetical: it shipped in claude-coordinator a72f922 and
polluted worker worktrees on all three machines.

Fix: keep `graphify-out/` a real directory and symlink each entry of the
base graph (graph.json, manifest.json, cache/, ...) *into* it, skipping
.gitignore. The tracked .gitignore survives, its `*` / `!.gitignore`
rule keeps the linked entries invisible to git for free, and a fresh
linked worktree is `git status` clean. The existing guards are kept: only
bootstrap when the base has a real graph.json, never create a dangling
link, never clobber a real graph built in the worktree (the "already
bootstrapped" probe moves from `-L graphify-out` to
`graphify-out/graph.json`, so re-firing the hook is idempotent).

Tests encoded the old contract and are rewritten for the new shape, plus
the assertion whose absence let this ship:

- worktree_add_leaves_git_status_empty -- the acceptance bar. Verified
  non-vacuous: against the old hook it fails with exactly the incident
  signature (` D graphify-out/.gitignore` / `?? graphify-out`).
- worktree_add_links_base_graph_contents_into_worktree -- graphify-out/
  stays a real dir; graph.json / manifest.json / cache/ are links that
  resolve into the base checkout. Also greps the hook's progress
  message, kept in sync with the hook.
- worktree_add_preserves_the_tracked_gitignore -- the tracked file is
  still a real, unmodified, tracked file. Guarded against the vacuity
  that made it pass under the old hook (it was resolving through the
  directory symlink to the base's copy).
- worktree_remove_leaves_the_base_graph_intact -- `git worktree remove`
  must not recurse through the `cache/` directory symlink into the base.
- no_symlink_when_base_has_no_graph / real_graph_in_worktree_is_never_
  clobbered -- updated to the per-entry shape, both now also assert a
  clean `git status`.

core.hooksPath remains unset fleet-wide (the live mitigation applied
when this shipped); re-enabling it on each machine is an operator step
after this merges, not part of this change.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
CI's first step is `cargo fmt -- --check`, which this branch predates —
it was cut before #613 enabled CI, so the new integration test was never
format-checked. Mechanical rustfmt output only, no logic change.
@JDonaghy
JDonaghy force-pushed the issue-611-worktree-agents-cannot-query-the-graphif branch from 1e3ab80 to d22482c Compare July 31, 2026 19:20
@JDonaghy
JDonaghy merged commit 3d15648 into develop Jul 31, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Worktree agents cannot query the graphify graph — port the .githooks/ bootstrap from claude-coordinator

1 participant