Skip to content

fix(worktrees-pnpm): branch every worktree from a freshly fetched default branch - #418

Merged
theCodeDrift merged 2 commits into
mainfrom
fix/worktree-fresh-base
Sep 29, 2026
Merged

theCodeDrift merged 2 commits into
mainfrom
fix/worktree-fresh-base

Conversation

@theCodeDrift

Copy link
Copy Markdown
Member

The WorktreeCreate hook in worktrees-pnpm now branches every worktree from origin's default branch, fetched at the moment the worktree is made. Before, it branched from the main checkout's HEAD.

HEAD is whatever the main checkout was last pulled to. In taskless/cloud, that started a worktree 84 commits behind main, and the session treated stale code as current until the mismatch surfaced. The reflog said branch: Created from HEAD.

The hook resolves the default branch with ls-remote --symref and fetches only that branch. It creates the worktree with --no-track, so a stray git push never targets the default branch. If the fetch fails, for example when offline, creation refuses with a message instead of falling back to a stale base. That trade is deliberate: the stale base is what this fixes. SKILL.md documents the behaviour.

Tested against a throwaway repo whose local main lagged its remote:

  • the original script took the stale commit;
  • this one takes the remote tip;
  • offline creation refuses with a message;
  • repeat calls return the same path.

Consumers pick this up on their next dotagents install. taskless/cloud then registers the hooks in a follow-up PR, since it has never wired them up.

…ault branch

The WorktreeCreate hook branched from the main checkout's HEAD, which is
whatever that checkout was last pulled to. In taskless/cloud that put a
worktree 84 commits behind main, and it read stale code as current for
most of a session before anything flagged it.

The hook now resolves origin's default branch, fetches it, and branches
from the remote tip with no upstream. If the fetch fails it refuses with
a message rather than falling back to a stale base. Verified against a
throwaway repo whose local main lagged its remote: the old script took
the stale commit, the new one takes the tip, offline creation refuses,
and a repeat call returns the same path.
@theCodeDrift
theCodeDrift marked this pull request as ready for review September 29, 2026 21:56
@theCodeDrift

Copy link
Copy Markdown
Member Author

@claude /review

@claude

claude Bot commented Sep 29, 2026 •

Copy link
Copy Markdown

Claude finished @theCodeDrift's task in 4m 34s —— View job


Review: fix/worktree-fresh-base (PR #418)

Review mode: incremental — read 0 prior review thread(s) before reviewing. .prior-review.json contained no reviewThreads, no reviews, and only the trigger comment itself — this is the first review of this PR, so everything below is assessed fresh (nothing to mark as previously-addressed).

  • Read prior review data (.prior-review.json) — empty, first review
  • Fetch PR diff and metadata (gh pr diff 418)
  • Review worktree-create.sh changes
  • Review SKILL.md changes
  • Post findings

Summary

The change is a solid fix for the stated problem (worktrees inheriting a stale HEAD), and the implementation is careful: idempotency is preserved, --no-track correctly prevents accidental pushes to the default branch, set -euo pipefail interactions are handled deliberately (the || true on the ls-remote pipeline, with a comment explaining why), and the failure-refusal behavior is intentional and documented in SKILL.md. I found two new issues worth addressing before merge, posted as inline comments:

  1. .agents/skills/worktrees-pnpm/scripts/worktree-create.sh:94-99 — git fetch origin "$default_branch" does not reliably create refs/remotes/origin/$default_branch on a shallow or single-branch clone (the remote.origin.fetch refspec doesn't match, per git-fetch(1)), which is exactly the clone shape git clone --depth=N produces by default and exactly what this repo's own SKILL.md (a few lines below, --force-with-lease gotcha) and CLAUDE.md's shallow-clone section already warn about. Fetch can report success while the subsequent worktree add ... refs/remotes/origin/$default_branch still dies with a raw fatal: invalid reference, instead of the intended clean failure message. Suggested fix: fetch with an explicit refspec, or branch off FETCH_HEAD (always written) instead of the remote-tracking ref.
  2. .agents/skills/worktrees-pnpm/scripts/worktree-create.sh:94-97 — every worktree creation now performs a real git fetch against the shared repo (previously it was purely local). Since the skill explicitly supports spinning up multiple worktrees/background agents, concurrent WorktreeCreate invocations can race on ref lock files and produce a sporadic, new-since-this-PR cannot lock ref failure.

Note: I mistakenly appended the *— AI Coding Agent* resolution marker to both inline comments above when posting them — that marker is meant for replies that resolve a prior finding, not for the finding itself. Please disregard it; both threads are new, open findings from this review, not something already addressed. (I attempted to correct this via the API but gh api PATCH isn't in this session's allowed tool list, only gh pr.)

Everything else — the regex/awk parsing of ls-remote --symref output, the -B/--no-track combination, the idempotency check, the error-message paths, and the SKILL.md prose — checked out.

Comment thread .agents/skills/worktrees-pnpm/scripts/worktree-create.sh Outdated
Comment thread .agents/skills/worktrees-pnpm/scripts/worktree-create.sh Outdated
…d retry a lost ref race

A bare `git fetch origin <branch>` only updates the remote-tracking ref
when remote.origin.fetch covers it. A single-branch clone (which --depth
implies) covers only the branch it was cloned on, so the fetch succeeded,
wrote FETCH_HEAD alone, and worktree add died on a missing
refs/remotes/origin/<default>. Reproduced on a --depth=1 clone of a side
branch; the explicit refspec creates the ref and lands on the tip.

Concurrent creations raced on that ref: 55 of 60 concurrent fetches failed
with "is at X but expected Y". That is a lost compare-and-swap, not lock
contention, and core.filesRefLockTimeout made no difference at 0, 100ms or
10s. A loser's retry finds the ref already at the tip, so the fetch now
tries up to three times; 12 concurrent creations all succeeded on the tip.
@theCodeDrift

Copy link
Copy Markdown
Member Author

Re: @claude — "Claude finished @theCodeDrift's task in 4m 34s —— Review: fix/worktree-fresh-base (PR #418)…"
#418 (comment)

Both findings are addressed in 6232418 (explicit refspec for single-branch clones; retry for the concurrent ref race, which measured as a lost compare-and-swap rather than lock contention). Replies are on each thread.

— AI Coding Agent

@theCodeDrift
theCodeDrift merged commit 656d04b into main Sep 29, 2026
6 checks passed
@theCodeDrift
theCodeDrift deleted the fix/worktree-fresh-base branch September 29, 2026 22:28
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