Carry a project's agent instructions into each worktree - #13
Merged
Merged
Conversation
`git worktree add` checks out tracked files only, so a gitignored CLAUDE.md stays behind — as this repository's own does. An isolated session started with no instructions at all and behaved differently from one run in the project directory, for a reason nothing on screen explained. linkProjectFiles symlinks CLAUDE.md, AGENTS.md and .claude into each new worktree. A link and not a copy, so the project keeps one copy of each and an edit made in any session is the edit every sibling reads. A copy would be one more thing to drift, which is the problem this is fixing. **Only a name the project ignores is linked.** The target is an absolute path on this machine, so a link git can see is a machine path one `git add -A` away from a public commit, and the thing running in that worktree is an agent. A tracked CLAUDE.md needs nothing: git carries it. Being ignored is also what keeps the worktree removable, and that was measured rather than assumed. `git worktree remove` refuses a tree holding an untracked symlink (exit 128) and accepts one holding an ignored symlink (exit 0). newSession unwinds the worktree when linking fails, so a link git could see would strand the very tree the unwind is for. TestIgnoredLinksDoNotBlockRemoval keeps that measured. What this does not do is merge the agent's own memory. Claude Code partitions transcripts and memory by absolute directory — ui.claudeSlug already mirrors the rule — so each worktree is a separate project to it. Giving sessions a shared directory is giving up the isolation that is the point of one. internal/coord is what crosses sessions. Each test was checked by stubbing linkProjectFiles to return nil. The four that assert linking fail; the two that assert nothing was linked pass, as they should.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Multiple worktrees in one project never shared instructions.
git worktree addchecks out tracked files only, so a gitignoredCLAUDE.mdstays behind — as this repository's own does. Every isolated session here started with no project instructions at all.linkProjectFilessymlinksCLAUDE.md,AGENTS.mdand.claudeinto each new worktree. A link and not a copy, so the project keeps one copy of each and an edit made in any session is the edit every sibling reads.The part worth reviewing
Only a name the project ignores is linked. The link target is an absolute path on this machine, so a link git can see is a machine path one
git add -Aaway from a public commit — and the thing running in that worktree is an agent.gitx.Ignoresis the gate.That rule also does a second job, and this one was measured rather than reasoned about:
git worktree removefatal: ... contains modified or untracked files(128)newSessionunwinds the worktree when linking fails. A link git could see would strand the very tree the unwind exists for.TestIgnoredLinksDoNotBlockRemovalpins it.What this does not do
It does not merge the agent's own memory. Claude Code partitions transcripts and memory by absolute directory —
ui.claudeSlugalready mirrors that rule — so each worktree is a separate project to it. Giving sessions a shared directory is giving up the isolation that is the point of one, so the split stands.internal/coordis what crosses sessions: notes, messages and claims are keyed by session rather than by path.Limits, stated rather than hidden
.claudethat is partly tracked arrives as a real directory holding the tracked half, and the ignored files inside it still do not travel.CLAUDE.mdthat is untracked and not ignored is left behind. That is deliberate — see the rule above — but it is a real case.newSessionhas no test coverage, before or after this change. The new step and the unwind's precondition are each tested in isolation; the wiring between them is not.Verification
Every test was checked by stubbing
linkProjectFilestoreturn nil. The four that assert linking fail. The two that assert nothing was linked pass, which is correct for a guard.