From df76eea7645a70cc9035cb030a54fa8559dc7cdd Mon Sep 17 00:00:00 2001 From: Jeremy McSpadden Date: Thu, 19 Mar 2026 22:13:25 -0500 Subject: [PATCH] fix(#1526): auto-mode worktree commits land on main instead of milestone branch (#1534) GitServiceImpl.getMainBranch() was designed to detect manual /worktree worktrees (worktree/ branches) but incorrectly applied the same logic to auto-mode worktrees (milestone/ branches). When no worktree/ branch existed, it fell back to the current branch, which in certain contexts could be main, causing slice commits to land on main instead of the milestone branch. Fix: Detect if currently on a milestone/* branch first (auto-mode case) and return it, before checking for worktree/* branches (manual worktree case). - Modify getMainBranch() to detect milestone branches first - Add test verifying getMainBranch() returns correct branch in auto-worktree - All tests pass, build succeeds Fixes #1526 --- src/resources/extensions/gsd/git-service.ts | 13 ++++++++++++- .../gsd/tests/auto-worktree.test.ts | 19 +++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/src/resources/extensions/gsd/git-service.ts b/src/resources/extensions/gsd/git-service.ts index dc46190e9..7db9b0e70 100644 --- a/src/resources/extensions/gsd/git-service.ts +++ b/src/resources/extensions/gsd/git-service.ts @@ -479,9 +479,20 @@ export class GitServiceImpl { const wtName = detectWorktreeName(this.basePath); if (wtName) { + // Auto-mode worktrees use milestone/ branches (wtName = milestone ID) + const milestoneBranch = `milestone/${wtName}`; + const currentBranch = nativeGetCurrentBranch(this.basePath); + + // If we're on a milestone/ branch, use it (auto-mode case) + if (currentBranch.startsWith("milestone/")) { + return currentBranch; + } + + // Otherwise check for manual worktree branch (worktree/) const wtBranch = `worktree/${wtName}`; if (nativeBranchExists(this.basePath, wtBranch)) return wtBranch; - return nativeGetCurrentBranch(this.basePath); + + return currentBranch; } // Repo-level default detection: origin/HEAD → main → master → current branch. diff --git a/src/resources/extensions/gsd/tests/auto-worktree.test.ts b/src/resources/extensions/gsd/tests/auto-worktree.test.ts index 5ddd3c0f1..cb21d4f2b 100644 --- a/src/resources/extensions/gsd/tests/auto-worktree.test.ts +++ b/src/resources/extensions/gsd/tests/auto-worktree.test.ts @@ -153,6 +153,25 @@ async function main(): Promise { // After teardown, originalBase should be null assertEq(getAutoWorktreeOriginalBase(), null, "no split-brain: originalBase cleared"); + // ─── #1526: getMainBranch returns milestone branch in auto-worktree ── + console.log("\n=== #1526: getMainBranch() returns milestone/ in auto-worktree ==="); + { + const { GitServiceImpl } = await import("../git-service.ts"); + + // Create worktree + const wtPath = createAutoWorktree(tempDir, "M005"); + // Don't set main_branch pref so getMainBranch falls through to worktree detection + const gitService = new GitServiceImpl(wtPath); + gitService.setMilestoneId("M005"); + + // Verify getMainBranch returns the milestone branch + const mainBranch = gitService.getMainBranch(); + assertEq(mainBranch, "milestone/M005", "getMainBranch returns milestone/ in auto-worktree"); + + // Cleanup + teardownAutoWorktree(tempDir, "M005"); + } + // ─── #778: reconcile plan checkboxes on re-attach ───────────────── console.log("\n=== #778: reconcile plan checkboxes on re-attach ==="); {