Skip to content

Carry a project's agent instructions into each worktree - #13

Merged
tdwd merged 1 commit into
mainfrom
worktree-project-files
Sep 18, 2026
Merged

tdwd merged 1 commit into
mainfrom
worktree-project-files

Conversation

@tdwd

@tdwd tdwd commented Sep 18, 2026

Copy link
Copy Markdown
Contributor

Multiple worktrees in one project never shared instructions. git worktree add checks out tracked files only, so a gitignored CLAUDE.md stays behind — as this repository's own does. Every isolated session here started with no project instructions at all.

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.

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 -A away from a public commit — and the thing running in that worktree is an agent. gitx.Ignores is the gate.

That rule also does a second job, and this one was measured rather than reasoned about:

tree contains git worktree remove
untracked symlink fatal: ... contains modified or untracked files (128)
ignored symlink succeeds (0)

newSession unwinds the worktree when linking fails. A link git could see would strand the very tree the unwind exists for. TestIgnoredLinksDoNotBlockRemoval pins 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.claudeSlug already 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/coord is what crosses sessions: notes, messages and claims are keyed by session rather than by path.

Limits, stated rather than hidden

  • Top-level names only. A .claude that is partly tracked arrives as a real directory holding the tracked half, and the ignored files inside it still do not travel.
  • A CLAUDE.md that is untracked and not ignored is left behind. That is deliberate — see the rule above — but it is a real case.
  • newSession has 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 linkProjectFiles to return nil. The four that assert linking fail. The two that assert nothing was linked pass, which is correct for a guard.

`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.
@tdwd
tdwd merged commit 1655fda into main Sep 18, 2026
3 checks passed
@tdwd
tdwd deleted the worktree-project-files branch September 18, 2026 15:39
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.

1 participant