Skip to content

fix: guard mutating commands across worktrees#101

Merged
dubscode merged 1 commit into
mainfrom
feature/dub-61-worktree-aware-mutating-commands-extend-tier-0-sync-to-all
May 25, 2026
Merged

fix: guard mutating commands across worktrees#101
dubscode merged 1 commit into
mainfrom
feature/dub-61-worktree-aware-mutating-commands-extend-tier-0-sync-to-all

Conversation

@dubscode

Copy link
Copy Markdown
Contributor

TL;DR

Added a shared worktree mutation guard and wired it through split, absorb, squash, fold, pop, rename, move, reorder, unlink, and submit.

Why

Mutating a branch checked out in another worktree can fail mid-command and leave users in awkward recovery states.

DUB-8 introduced checkout detection for sync/restack/post-merge; DUB-61 extends that protection to the remaining mutating surfaces.

Before

  • Several Tier 3 commands had no preflight for sibling-worktree checkouts.
  • Existing worktree refusals used per-command wording, making recovery behavior less consistent.

After

  • A shared guard refuses unsafe branch mutations before undo, journal, state, push, or PR side effects.
  • Recovery hints include the exact other worktree path and the command to rerun from there.

File-by-file

packages/cli/src/lib/worktree-guards.ts

new +49 / -0

Adds the shared guard that finds the first conflicting branch checkout and throws a DubError with path-specific recovery hints.

await assertBranchesNotCheckedOutElsewhere(cwd, branches, 'dub submit');

packages/cli/src/commands/*.ts and packages/cli/src/lib/fold.ts

mod +72 / -69

Wires the guard into Tier 3 mutation targets and submit's resolved push scope before side effects.

packages/cli/src/commands/worktree-guards.test.ts

new +171 / -0

Adds real-git-worktree regression tests for every DUB-61 command and the recovery-path hint.

packages/cli/test/helpers.ts

mod +19 / -0

Adds a reusable helper for creating and cleaning up sibling worktrees in integration tests.

Where to focus review

  1. Mutation Ordering - packages/cli/src/commands: Each guard should run after the branch targets are known but before undo entries, cleanup journals, state writes, pushes, or PR mutations.
  2. Submit Scope - packages/cli/src/commands/submit.ts: Submit should check exactly the resolved branches that would be pushed and should not block dry-run planning.
  3. Recovery Hint - packages/cli/src/lib/worktree-guards.ts: The user needs the exact other worktree path to recover safely.

Test plan

  • integration: DUB-61 command worktree guard suite - packages/cli/src/commands/worktree-guards.test.ts covers split, absorb, squash, fold, pop, rename, move, reorder, unlink, submit, and recovery hints with real sibling worktrees.
  • unit: Existing fold parent-worktree assertion - packages/cli/test/commands/fold-tree.test.ts updated to assert the shared refusal wording.

Quality gates

  • Checks: pnpm checks - passed (Biome checked 318 files with no fixes applied.)
  • Typecheck: pnpm typecheck - passed (Turbo typecheck succeeded for docs, dubstack, and dubstack-retarget-action.)
  • Tests: pnpm test - passed (124 test files and 1354 tests passed.)

Self-QA

See QA fallback evidence.

CLI self-QA for worktree-aware mutation refusal.

  • Every Tier 3 command refuses when its target branch is checked out in another worktree.
  • Submit refuses before pushing a branch in scope that is checked out elsewhere.
  • Recovery hints include the sibling worktree path.

Acceptance criteria

  • All Tier 3 mutating commands check worktree - Guard wired into split, absorb, squash, fold, pop, rename, move, reorder, and unlink with regression coverage.
  • Submit checks worktree for each branch in scope - submit checks plan.branches before push/PR work when dryRun is false.
  • Refusal includes worktree path in the recovery hint - Shared DubError recovery includes The other worktree is '<path>'.; regression asserts this.
  • Tests for each command - worktree-guards.test.ts has one refusal scenario per named command plus a recovery-hint scenario.

Adversarial review

Iterations: 1

Remaining critical/major: 0/0

Remaining minor/nitpick: 0/0

  • One move no-op strictness concern was found and fixed before final gates.

Dependencies

  • DUB-8: Done in Linear before implementation started.
  • External dependencies: No external dependencies detected.

Rollout

Ship as a CLI safety improvement; no migration or docs update required because command syntax is unchanged.

  • Before mutation - Preflight target branch checkouts: Commands resolve the branch targets and call the shared guard before side effects.
  • On conflict - Refuse with recovery: The CLI reports the branch and exact sibling worktree path, then suggests rerunning there or switching that worktree off the branch.

Commit

fix: guard mutating commands across worktrees

Completes DUB-61

Copilot AI review requested due to automatic review settings May 25, 2026 04:42
@vercel

vercel Bot commented May 25, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
dubstack Ready Ready Preview, Comment May 25, 2026 4:42am

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds a shared “worktree mutation” preflight guard to prevent Tier 3 commands (and submit) from mutating branches that are checked out in another git worktree, avoiding mid-command git failures and messy recovery states.

Changes:

  • Introduces assertBranchesNotCheckedOutElsewhere() / findWorktreeCheckoutConflict() and standardizes the user-facing refusal + recovery hints.
  • Wires the shared guard into mutating commands (split/absorb/squash/fold/pop/rename/move/reorder/unlink) and into submit’s resolved push scope (when not dry-run).
  • Adds real git-worktree integration coverage for each guarded command plus recovery-path assertions, and updates relevant mocks/fixtures.

Reviewed changes

Copilot reviewed 19 out of 19 changed files in this pull request and generated no comments.

Show a summary per file
File Description
packages/cli/test/helpers.ts Adds withBranchWorktree() helper to create/remove sibling worktrees for integration tests.
packages/cli/test/commands/fold-tree.test.ts Updates fold test expectations to match the new shared refusal wording.
packages/cli/src/lib/worktree-guards.ts Adds the shared worktree conflict detection + standardized DubError refusal and recovery hints.
packages/cli/src/lib/fold.ts Replaces per-command worktree checks with the shared guard for fold targets.
packages/cli/src/commands/worktree-guards.test.ts New integration suite validating the guard across all DUB-61 commands using real worktrees.
packages/cli/src/commands/unlink.ts Adds guard before unlink mutation side effects.
packages/cli/src/commands/unlink.test.ts Extends git mocks to include listWorktreeCheckouts to support the guard path.
packages/cli/src/commands/submit.ts Adds guard on plan.branches when dryRun is false, before submit side effects.
packages/cli/src/commands/submit.test.ts Extends git mocks to include listWorktreeCheckouts to support the guard path.
packages/cli/src/commands/squash.ts Adds guard for the current branch before squash mutation work.
packages/cli/src/commands/split.ts Adds guard for the source branch before split mutation work.
packages/cli/src/commands/reorder.ts Replaces bespoke reorder worktree logic with the shared guard.
packages/cli/src/commands/rename.ts Replaces bespoke rename worktree logic with the shared guard.
packages/cli/src/commands/pop.ts Adds guard for the current branch before pop mutation work.
packages/cli/src/commands/move.ts Adds guard for all branches potentially mutated by a move (after no-op early return).
packages/cli/src/commands/move.test.ts Extends git mocks to include listWorktreeCheckouts to support the guard path.
packages/cli/src/commands/absorb.ts Adds guard for non-stack modes and stack-mode cross-fixup branches before mutation/journaling.
.reports/dub-61-qa.md Adds QA fallback evidence documenting the safety change and test/gate results.
.reports/dub-61-adversarial-review.md Adds adversarial review artifact documenting findings and verification.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@dubscode dubscode merged commit f61202a into main May 25, 2026
13 checks passed
@dubscode dubscode deleted the feature/dub-61-worktree-aware-mutating-commands-extend-tier-0-sync-to-all branch May 25, 2026 04:51
@github-actions

Copy link
Copy Markdown

🎉 This PR is included in version 1.10.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants