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-r4 — inside 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
- Add
.worktrees/ to .gitignore. One line, closes this instance.
- 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
Filed by the
domain:engineexecution 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-r4— inside the shared primary checkout. Measured at 2026-09-09T13:37Z:.gitignore:129carries.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:
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..gitignoreline 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:
⇒ 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
packages/client-react/.examples-build/is not gitignored — check:skill-examples' second surface writes an untracked throwaway dir #11440 — "packages/client-react/.examples-build/is not gitignored — check:skill-examples' second surface writes an untracked throwaway dir." Same shape, different path, and a throwaway build dir rather than git plumbing.⇒ this recurs because each instance is fixed by path.⚠️ A card that only adds
.worktrees/to.gitignorebuys the next path nothing.Two ends, ⛔ neither prescribed here
.worktrees/to.gitignore. One line, closes this instance..gitfile whose content beginsgitdir: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.guard-main-checkouthooks already reason about worktree paths (#11809 touched exactly that logic), so a rule likely has a home.Repro
⛔ 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· sessionsession_01XTBcV7zZHmokdyQgXjbyEU· R1 · reading taken 2026-09-09T13:37Z