From 911205238b4aa798513cf355b67e7077e379d94a Mon Sep 17 00:00:00 2001 From: Jakob Heuser Date: Mon, 26 Oct 2026 08:33:50 -0700 Subject: [PATCH 1/3] docs(worktrees): fetch with an explicit refspec and check the base before reading anything A stale tree carries stale conventions, so the rebase-first rule could not reach the worktree that needed it. Spell out the refspec in the manual steps, branch from origin/main rather than local main, and give the worktrees skill the same fetch-and-count check. --- .agents/skills/worktrees-pnpm/SKILL.md | 15 ++++++++++++++- AGENTS.md | 6 ++++-- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/.agents/skills/worktrees-pnpm/SKILL.md b/.agents/skills/worktrees-pnpm/SKILL.md index 41a98b05..7dfa0ad5 100644 --- a/.agents/skills/worktrees-pnpm/SKILL.md +++ b/.agents/skills/worktrees-pnpm/SKILL.md @@ -14,12 +14,25 @@ problem in this repo. **`git worktree add` is not finished until `pnpm install` has run inside the new worktree.** ```bash -git fetch origin main +git fetch origin +refs/heads/main:refs/remotes/origin/main # explicit refspec; see the hook for why git worktree add -b .agents/.worktrees/ origin/main # or an existing , without -b cd .agents/.worktrees/ pnpm install # ← not optional ``` +**Then confirm the base is current, whoever made the worktree.** The `WorktreeCreate` hook fetches +before branching, but a worktree made by hand or by another tool is cut from whatever its maker +had locally. Before reading code or installing: + +```bash +git fetch origin +refs/heads/main:refs/remotes/origin/main +git rev-list --count HEAD..origin/main # anything above 0: git rebase origin/main +``` + +Branch from `origin/main`, never the local `main`, which is only as fresh as the main checkout's +last pull. A stale tree also carries stale conventions, including this one, so the check cannot +wait until you have read the docs it would correct. + Worktrees go in **`.agents/.worktrees/`**, gitignored except for a tracked `.gitkeep`, so the directory exists in every clone. This is the location every agent harness shares, not a Claude Code one, so a worktree made by hand, by Claude Code, or by another tool lands in the same place. In Claude Code, the `WorktreeCreate` / `WorktreeRemove` hooks configured in `.claude/settings.json` (scripts in this skill's `scripts/` directory) do the placement, which would otherwise nest worktrees at `.claude/worktrees/`. A harness without those hooks follows the manual steps above, and the result is identical. `ls .agents/.worktrees/` answers "what worktrees do I have?" at a glance. **Two older locations are still ignored, and new worktrees never go there.** `worktrees/` at the repo root was the previous standard, and `.claude/worktrees/` is Claude Code's default. Both keep their ignore entries so that worktrees already living there do not surface as untracked files or get linted, until their work is done. diff --git a/AGENTS.md b/AGENTS.md index dec62000..f3be279f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -91,14 +91,16 @@ Both flags can be passed in one call. See also **Stacked PRs → Other gotchas** **Use the hooks and skills that are available, and do not depend on them.** In Claude Code, the `WorktreeCreate`/`WorktreeRemove` hooks in `.claude/settings.json` do the placement, and `.agents/skills/` (which `.claude/skills` links to) holds the skills, `worktrees-pnpm` among them. Another harness may have neither. If no hook places the worktree for you, do it by hand. The result must be the same: a fresh branch cut from `origin`'s default branch as just fetched, never from the main checkout's `HEAD`: ```bash -git fetch origin main +git fetch origin +refs/heads/main:refs/remotes/origin/main git worktree add -b .agents/.worktrees/ origin/main cd .agents/.worktrees/ && pnpm install ``` +The refspec is spelled out because a bare `git fetch origin main` only updates `origin/main` when `remote.origin.fetch` covers it, which a single-branch clone of another branch does not. Branch from `origin/main`, never the local `main`: the local branch is whatever the main checkout was last pulled to. + The three rules that cause the most damage when missed: -- **A worktree starts from the latest `origin/main`, checked before any work begins.** Run `git fetch origin`, then `git rev-list --count HEAD..origin/main`. Anything above `0` means the worktree is behind: rebase onto `origin/main` (`git rebase origin/main`) before reading code, making changes, or running `pnpm install` (a rebase can change the lockfile), unless the work was explicitly asked to start from another branch or commit. The `WorktreeCreate` hook already branches from a freshly fetched `origin/main`, but a worktree made any other way (by hand, by another tool, or by a session board) is cut from whatever the main checkout was last pulled to. One started 68 commits behind `main`, and nothing flagged it: stale code reads exactly like current code. +- **A worktree starts from the latest `origin/main`, checked before any work begins.** Run `git fetch origin`, then `git rev-list --count HEAD..origin/main`. Anything above `0` means the worktree is behind: rebase onto `origin/main` (`git rebase origin/main`) before reading code, making changes, or running `pnpm install` (a rebase can change the lockfile), unless the work was explicitly asked to start from another branch or commit. The `WorktreeCreate` hook already branches from a freshly fetched `origin/main`, but a worktree made any other way (by hand, by another tool, or by a session board) is cut from whatever the main checkout was last pulled to. One started 68 commits behind `main`, and nothing flagged it: stale code reads exactly like current code. Another started 156 behind and never saw this rule at all, because the rule landed after its base: **a stale tree carries stale conventions**, so nothing in the repo can warn a checkout that is already behind. That is why the fix belongs in whatever creates the worktree (the hook, a session board, your own hands), and why this check comes before reading anything else here. - **A worktree gets its own empty `node_modules`.** `git worktree add` is not finished until `pnpm install` has run inside it. Without that, `git commit` fails in `lint-staged` (no `prettier`/`eslint`), and every `pnpm` script fails. A missing `prettier` here once cost an agent an hour of dead-end workarounds. There is no `pnpm worktree` command; `git worktree` is the tool. - **NEVER point an agent at the main repo path** (e.g. `/Users//code/taskless/skills`). It will `cd` there and run git commands and edits in the **main** checkout, defeating isolation. It can create and check out a branch in your working tree, silently switching your session off its own branch. Tell the agent to work in **its assigned worktree** (`$PWD`) and pass only relative paths plus GitHub identifiers (`owner/repo`). From 164c21b14eb4775826ae2966e9a0ef484be0020e Mon Sep 17 00:00:00 2001 From: Jakob Heuser Date: Mon, 26 Oct 2026 09:35:09 -0700 Subject: [PATCH 2/3] docs(agents): use the explicit main refspec in the freshness check too --- AGENTS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AGENTS.md b/AGENTS.md index f3be279f..eb048c42 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -100,7 +100,7 @@ The refspec is spelled out because a bare `git fetch origin main` only updates ` The three rules that cause the most damage when missed: -- **A worktree starts from the latest `origin/main`, checked before any work begins.** Run `git fetch origin`, then `git rev-list --count HEAD..origin/main`. Anything above `0` means the worktree is behind: rebase onto `origin/main` (`git rebase origin/main`) before reading code, making changes, or running `pnpm install` (a rebase can change the lockfile), unless the work was explicitly asked to start from another branch or commit. The `WorktreeCreate` hook already branches from a freshly fetched `origin/main`, but a worktree made any other way (by hand, by another tool, or by a session board) is cut from whatever the main checkout was last pulled to. One started 68 commits behind `main`, and nothing flagged it: stale code reads exactly like current code. Another started 156 behind and never saw this rule at all, because the rule landed after its base: **a stale tree carries stale conventions**, so nothing in the repo can warn a checkout that is already behind. That is why the fix belongs in whatever creates the worktree (the hook, a session board, your own hands), and why this check comes before reading anything else here. +- **A worktree starts from the latest `origin/main`, checked before any work begins.** Run `git fetch origin +refs/heads/main:refs/remotes/origin/main` (the explicit refspec, for the reason above), then `git rev-list --count HEAD..origin/main`. Anything above `0` means the worktree is behind: rebase onto `origin/main` (`git rebase origin/main`) before reading code, making changes, or running `pnpm install` (a rebase can change the lockfile), unless the work was explicitly asked to start from another branch or commit. The `WorktreeCreate` hook already branches from a freshly fetched `origin/main`, but a worktree made any other way (by hand, by another tool, or by a session board) is cut from whatever the main checkout was last pulled to. One started 68 commits behind `main`, and nothing flagged it: stale code reads exactly like current code. Another started 156 behind and never saw this rule at all, because the rule landed after its base: **a stale tree carries stale conventions**, so nothing in the repo can warn a checkout that is already behind. That is why the fix belongs in whatever creates the worktree (the hook, a session board, your own hands), and why this check comes before reading anything else here. - **A worktree gets its own empty `node_modules`.** `git worktree add` is not finished until `pnpm install` has run inside it. Without that, `git commit` fails in `lint-staged` (no `prettier`/`eslint`), and every `pnpm` script fails. A missing `prettier` here once cost an agent an hour of dead-end workarounds. There is no `pnpm worktree` command; `git worktree` is the tool. - **NEVER point an agent at the main repo path** (e.g. `/Users//code/taskless/skills`). It will `cd` there and run git commands and edits in the **main** checkout, defeating isolation. It can create and check out a branch in your working tree, silently switching your session off its own branch. Tell the agent to work in **its assigned worktree** (`$PWD`) and pass only relative paths plus GitHub identifiers (`owner/repo`). From 5727ad6b2df032fd1df29b27e298cbc6bd312a29 Mon Sep 17 00:00:00 2001 From: Jakob Heuser Date: Mon, 26 Oct 2026 09:40:50 -0700 Subject: [PATCH 3/3] docs(worktrees-pnpm): check the base before installing, in the same block --- .agents/skills/worktrees-pnpm/SKILL.md | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/.agents/skills/worktrees-pnpm/SKILL.md b/.agents/skills/worktrees-pnpm/SKILL.md index 7dfa0ad5..f0931b05 100644 --- a/.agents/skills/worktrees-pnpm/SKILL.md +++ b/.agents/skills/worktrees-pnpm/SKILL.md @@ -17,18 +17,15 @@ problem in this repo. git fetch origin +refs/heads/main:refs/remotes/origin/main # explicit refspec; see the hook for why git worktree add -b .agents/.worktrees/ origin/main # or an existing , without -b cd .agents/.worktrees/ -pnpm install # ← not optional -``` - -**Then confirm the base is current, whoever made the worktree.** The `WorktreeCreate` hook fetches -before branching, but a worktree made by hand or by another tool is cut from whatever its maker -had locally. Before reading code or installing: - -```bash -git fetch origin +refs/heads/main:refs/remotes/origin/main git rev-list --count HEAD..origin/main # anything above 0: git rebase origin/main +pnpm install # ← not optional, and after the rebase, which can change the lockfile ``` +**The base check applies whoever made the worktree.** The `WorktreeCreate` hook fetches before +branching, but a worktree made by hand or by another tool is cut from whatever its maker had +locally. Stepping into one you did not create, run the fetch and the `rev-list` check above first, +before reading code or installing. + Branch from `origin/main`, never the local `main`, which is only as fresh as the main checkout's last pull. A stale tree also carries stale conventions, including this one, so the check cannot wait until you have read the docs it would correct.