Skip to content

fix(branch ensure-feature): treat sibling-worktree checkout as success (#211) - #479

Merged
PolyphonyRequiem merged 1 commit into
mainfrom
fix/issue-211-ensure-feature-sibling-worktree
May 19, 2026
Merged

fix(branch ensure-feature): treat sibling-worktree checkout as success (#211)#479
PolyphonyRequiem merged 1 commit into
mainfrom
fix/issue-211-ensure-feature-sibling-worktree

Conversation

@PolyphonyRequiem

Copy link
Copy Markdown
Owner

Problem

Under the parallel-fleet apex convention the apex spawns sibling
worktrees per item (polyphony-item-3043, polyphony-item-9001,
…) all backed by the same shared bare repo. When polyphony branch ensure-feature runs from one worktree and the target feature branch
is already checked out in a sibling, git checkout <branch> refuses
with exit 128:

fatal: 'feature/3043' is already used by worktree at 'C:/.../polyphony-item-3043'

GitClient.CheckoutAsync throws ExternalToolException, the
outer catch in EnsureFeature wraps it as CacheError, and
the workflow tips over — even though the verb's sole job (make sure
the branch exists) is already satisfied.

Fix

Per #211 Option 1 (idempotent success):

  • Narrow try/catch around the CheckoutAsync call inside the
    localExisted branch.
  • Match ExternalToolException.Stderr against
    is already used by worktree at '<path>' via a partial regex.
  • On match: treat existence as satisfied, surface the sibling worktree
    path on a new WorktreePath envelope field, set
    Action = "exists_in_other_worktree", and keep the existing push
    step intact for !remoteExisted (git push operates on refs
    and is unaffected by which worktree owns the checkout).
  • Unrelated checkout failures still propagate to the outer catch and
    exit CacheError unchanged.

Envelope shape

BranchEnsureFeatureResult.Action now includes
exists_in_other_worktree. WorktreePath is the new nullable
field, populated only on that path. Consumers that ignore it keep
working — fully additive.

Tests

New BranchCommandsEnsureFeatureTests (7 tests, all green):

  • Local + remote both exist → checked_out, no push
  • Local in sibling worktree, remote exists → exists_in_other_worktree + WorktreePath, no push
  • Local in sibling worktree, remote absent → still pushes
  • Local exists, generic checkout failure (e.g. dirty tree) → CacheError (regression guard)
  • Remote-only → fetch + tracking checkout
  • Neither exists → create + push
  • Missing --branchRoutingFailure

Validation

  • dotnet test tests/Polyphony.Tests — 3836 passed, 0 failed
  • Invoke-Pester tests/lint-jinja-resolver.Tests.ps1 — 30 passed
  • tests/lint/fixtures/verb-output-schemas.json — surgical insert
    of the new worktree_path field per the
    hand-curated-fixture convention

Closes #211

#211)

Under the parallel-fleet apex convention a feature branch may be checked
out in a sibling worktree (e.g. polyphony-item-3043) when `branch
ensure-feature` is invoked from another worktree. The verb then ran
`git checkout <branch>` unconditionally, which git refuses with
exit 128 and stderr `fatal: '<branch>' is already used by worktree at
'<path>'`. `CheckoutAsync` threw `ExternalToolException`, the
outer catch wrapped it as `CacheError`, and the workflow exploded
even though the only thing the verb cares about — the branch's
existence — was already satisfied.

This commit narrows the catch around the `CheckoutAsync` call and
recognises that specific stderr via a partial regex. On match we treat
the branch as present (existence satisfied), surface the sibling
worktree path on a new `WorktreePath` envelope field, set
`Action=exists_in_other_worktree`, and keep the existing push step
intact for the `!remoteExisted` case (push works regardless of
which worktree owns the checkout). Unrelated checkout failures still
propagate to the outer catch and emit `CacheError` unchanged.

The new field is additive — `WorktreePath` is nullable, only set
on the new action — so consumers that ignore it keep working.

Tests cover all four state combinations plus the three AB#211 paths
(sibling-worktree happy path, sibling-worktree + remote-absent push,
generic checkout failure still errors).

Closes #211

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@PolyphonyRequiem
PolyphonyRequiem merged commit 1405226 into main May 19, 2026
1 check passed
@PolyphonyRequiem
PolyphonyRequiem deleted the fix/issue-211-ensure-feature-sibling-worktree branch May 19, 2026 21:00
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.

polyphony branch ensure-feature fails when branch is checked out in sibling worktree

1 participant