From 6de816f68cedf11b7ed53447dbf04e5145b0f50b Mon Sep 17 00:00:00 2001 From: Colin Johnson Date: Sun, 15 Mar 2026 21:30:28 -0400 Subject: [PATCH] fix(init): prefer current milestone phase-op targets (#1068) --- get-shit-done/bin/lib/init.cjs | 23 +++++++++++++++++ tests/init.test.cjs | 46 +++++++++++++++++++++++++++++++++- 2 files changed, 68 insertions(+), 1 deletion(-) diff --git a/get-shit-done/bin/lib/init.cjs b/get-shit-done/bin/lib/init.cjs index 5b40ba7cf..d29e533c8 100644 --- a/get-shit-done/bin/lib/init.cjs +++ b/get-shit-done/bin/lib/init.cjs @@ -365,6 +365,29 @@ function cmdInitPhaseOp(cwd, phase, raw) { const config = loadConfig(cwd); let phaseInfo = findPhaseInternal(cwd, phase); + // If the only disk match comes from an archived milestone, prefer the + // current milestone's ROADMAP entry so discuss-phase and similar flows + // don't attach to shipped work that reused the same phase number. + if (phaseInfo?.archived) { + const roadmapPhase = getRoadmapPhaseInternal(cwd, phase); + if (roadmapPhase?.found) { + const phaseName = roadmapPhase.phase_name; + phaseInfo = { + found: true, + directory: null, + phase_number: roadmapPhase.phase_number, + phase_name: phaseName, + phase_slug: phaseName ? phaseName.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '') : null, + plans: [], + summaries: [], + incomplete_plans: [], + has_research: false, + has_context: false, + has_verification: false, + }; + } + } + // Fallback to ROADMAP.md if no directory exists (e.g., Plans: TBD) if (!phaseInfo) { const roadmapPhase = getRoadmapPhaseInternal(cwd, phase); diff --git a/tests/init.test.cjs b/tests/init.test.cjs index 61a011eaa..66a645d7d 100644 --- a/tests/init.test.cjs +++ b/tests/init.test.cjs @@ -475,6 +475,51 @@ describe('cmdInitPhaseOp fallback', () => { assert.strictEqual(output.has_plans, false); }); + test('prefers current milestone roadmap entry over archived phase with same number', () => { + const archiveDir = path.join( + tmpDir, + '.planning', + 'milestones', + 'v1.2-phases', + '02-event-parser-and-queue-schema' + ); + fs.mkdirSync(archiveDir, { recursive: true }); + fs.writeFileSync(path.join(archiveDir, '02-CONTEXT.md'), '# Archived context'); + fs.writeFileSync(path.join(archiveDir, '02-01-PLAN.md'), '# Archived plan'); + fs.writeFileSync(path.join(archiveDir, '02-VERIFICATION.md'), '# Archived verification'); + + fs.writeFileSync( + path.join(tmpDir, '.planning', 'ROADMAP.md'), + `# Roadmap + +
+Shipped milestone v1.2 + +### Phase 2: Event Parser and Queue Schema +**Goal:** Archived milestone work +
+ +## Milestone v1.3 Current + +### Phase 2: Retry Orchestration +**Goal:** Current milestone work +**Plans:** TBD +` + ); + + const result = runGsdTools('init phase-op 2', tmpDir); + assert.ok(result.success, `Command failed: ${result.error}`); + + const output = JSON.parse(result.output); + assert.strictEqual(output.phase_found, true); + assert.strictEqual(output.phase_dir, null); + assert.strictEqual(output.phase_name, 'Retry Orchestration'); + assert.strictEqual(output.phase_slug, 'retry-orchestration'); + assert.strictEqual(output.has_context, false); + assert.strictEqual(output.has_plans, false); + assert.strictEqual(output.has_verification, false); + }); + test('neither directory nor roadmap entry returns not found', () => { fs.writeFileSync( path.join(tmpDir, '.planning', 'ROADMAP.md'), @@ -867,4 +912,3 @@ describe('cmdInitNewMilestone', () => { // ───────────────────────────────────────────────────────────────────────────── // roadmap analyze command // ───────────────────────────────────────────────────────────────────────────── -