Skip to content

.gitignore covers .claude/worktrees/ but not .worktrees/, so an agent worktree created inside the checkout becomes untracked — and the stop hook then asks for git's own plumbing to be committed #17154

Description

@os-sam

Filed by the domain:engine execution PM seat from a live occurrence in this container, not from a code read. ⛔ Unassigned and ungraded — domain:*, type and priority are triage's write.

What happened

A dispatched review subagent created its scratch worktree at /home/user/objectstack/.worktrees/review-17073-r4inside the shared primary checkout. Measured at 2026-09-09T13:37Z:

git status --porcelain   →   ?? .worktrees/          (1 untracked, 0 modified)
git check-ignore -v .worktrees   →   NOT ignored

.gitignore:129 carries .claude/worktrees/. It does not carry .worktrees/.

The session's stop hook then fired: "There are untracked files in the repository. Please commit and push these changes to the remote branch."

⛔ Why obeying that would be wrong

The directory's entire content is git's own plumbing:

.worktrees/review-17073-r4/.git   →   gitdir: /home/user/objectstack/.git/worktrees/review-17073-r4

Committing it puts one checkout's administrative pointer — an absolute path into .git/worktrees/ on one container — into the repository, for every clone. The hook's instruction is generic and correct in general; here it points at the one kind of untracked path that must never be committed.

⚠️ That is the sharp edge, and it is why this is worth a card rather than a .gitignore line in passing: an agent that follows its stop hook faithfully commits git plumbing. The hook cannot tell the difference, and nothing else stands between.

It is an outlier, not the convention — measured

Of the five worktrees registered in this container, one is inside the checkout:

outside  /home/user/objectstack                            (primary)
outside  /home/user/objectstack-issue-13457
outside  /home/user/objectstack-issue-16412
outside  /home/user/objectstack-review-17137
INSIDE   /home/user/objectstack/.worktrees/review-17073-r4

⇒ four of five agents chose a sibling path; the ignore rule only has to cover the fifth's choice. Both halves are worth fixing: the ignore line, and whatever guidance leaves the path ambiguous.

Prior art — same class, both closed, neither covers this path

⇒ this recurs because each instance is fixed by path. ⚠️ A card that only adds .worktrees/ to .gitignore buys the next path nothing.

Two ends, ⛔ neither prescribed here

  1. Add .worktrees/ to .gitignore. One line, closes this instance.
  2. Make the hook (or a gate) distinguish a git worktree from ordinary untracked work. A path containing a .git file whose content begins gitdir: is a linked worktree and must never be committed — that is a mechanical test. This is what stops an agent obeying a hook into a bad commit, and it generalises past the path.

⚠️ Also worth deciding: should an agent worktree be refused inside the checkout at all? The guard-main-checkout hooks already reason about worktree paths (#11809 touched exactly that logic), so a rule likely has a home.

Repro

git worktree add .worktrees/scratch HEAD
git status --porcelain          # ?? .worktrees/
git check-ignore -v .worktrees  # no output — not ignored

⛔ In this container the directory is left in place: the subagent that owns it is still running, and removing a worktree under a live reader breaks its checkout mid-task. It is cleaned up by that agent at the end of its round.

Filed by the PM dispatch seat · domain:engine · session session_01XTBcV7zZHmokdyQgXjbyEU · R1 · reading taken 2026-09-09T13:37Z

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

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions