diff --git a/src/resources/extensions/gsd/auto-start.ts b/src/resources/extensions/gsd/auto-start.ts index 139724433..3136a409c 100644 --- a/src/resources/extensions/gsd/auto-start.ts +++ b/src/resources/extensions/gsd/auto-start.ts @@ -315,6 +315,32 @@ export async function bootstrapAutoSession( } } + // Survivor branch exists but milestone still needs discussion (#1726): + // The worktree/branch was created but the milestone only has CONTEXT-DRAFT.md. + // Route to the interactive discussion handler instead of falling through to + // auto-mode, which would immediately stop with "needs discussion". + if (hasSurvivorBranch && state.phase === "needs-discussion") { + const { showSmartEntry } = await import("./guided-flow.js"); + await showSmartEntry(ctx, pi, base, { step: requestedStepMode }); + + invalidateAllCaches(); + const postState = await deriveState(base); + if ( + postState.activeMilestone && + postState.phase !== "needs-discussion" + ) { + state = postState; + // Discussion succeeded — clear survivor flag so normal flow continues + hasSurvivorBranch = false; + } else { + ctx.ui.notify( + "Discussion completed but milestone draft was not promoted. Run /gsd to try again.", + "warning", + ); + return releaseLockAndReturn(); + } + } + if (!hasSurvivorBranch) { // No active work — start a new milestone via discuss flow if (!state.activeMilestone || state.phase === "complete") { diff --git a/src/resources/extensions/gsd/tests/auto-start-needs-discussion.test.ts b/src/resources/extensions/gsd/tests/auto-start-needs-discussion.test.ts index f8a395259..7f5bc2a59 100644 --- a/src/resources/extensions/gsd/tests/auto-start-needs-discussion.test.ts +++ b/src/resources/extensions/gsd/tests/auto-start-needs-discussion.test.ts @@ -200,6 +200,37 @@ async function main(): Promise { } } + // ─── 7. Survivor branch + needs-discussion routes to showSmartEntry (#1726) ─ + console.log("\n=== 7. Survivor branch + needs-discussion routes to showSmartEntry ==="); + { + const source = readAutoStartSource(); + + // When hasSurvivorBranch is true AND phase is needs-discussion, the code + // must route to showSmartEntry instead of falling through to auto-mode. + const survivorNeedsDiscussion = source.match( + /if\s*\(hasSurvivorBranch\s*&&\s*state\.phase\s*===\s*"needs-discussion"\)\s*\{[^}]*showSmartEntry/s, + ); + assertTrue(!!survivorNeedsDiscussion, + "hasSurvivorBranch && needs-discussion must route to showSmartEntry"); + + // Verify the handler checks if the discussion succeeded + const handlerBlock = source.match( + /if\s*\(hasSurvivorBranch\s*&&\s*state\.phase\s*===\s*"needs-discussion"\)\s*\{([\s\S]*?)\n \}/, + ); + assertTrue(!!handlerBlock, + "found survivor + needs-discussion handler block"); + if (handlerBlock) { + assertTrue( + handlerBlock[1].includes('postState.phase !== "needs-discussion"'), + "handler must check if phase advanced after discussion", + ); + assertTrue( + handlerBlock[1].includes("releaseLockAndReturn"), + "handler must abort if discussion didn't promote draft", + ); + } + } + report(); }