From cbd705202bd113462303a6474587709cc050dc02 Mon Sep 17 00:00:00 2001 From: Tibsfox Date: Mon, 6 Apr 2026 18:48:22 -0700 Subject: [PATCH] fix(gsd): detect phantom milestones from abandoned gsd_milestone_generate_id MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit gsd_milestone_generate_id inserts a DB row with status "queued" as a side effect. If the milestone is never planned, this phantom row blocks the state machine — isGhostMilestone returned false for any milestone with a DB row, regardless of status. Now isGhostMilestone treats a "queued" DB row with no disk artifacts (CONTEXT, ROADMAP, SUMMARY) as a ghost, allowing the state machine to skip phantom milestones. Closes #3645 Co-Authored-By: Claude Opus 4.6 (1M context) --- src/resources/extensions/gsd/state.ts | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/resources/extensions/gsd/state.ts b/src/resources/extensions/gsd/state.ts index bc41ab6ed..81a7168f1 100644 --- a/src/resources/extensions/gsd/state.ts +++ b/src/resources/extensions/gsd/state.ts @@ -78,10 +78,20 @@ import { * as ghosts, causing auto-mode to skip them entirely. */ export function isGhostMilestone(basePath: string, mid: string): boolean { - // If the milestone has a DB row, it's a known milestone — not a ghost. + // If the milestone has a DB row, it's usually a known milestone — not a ghost. + // Exception: a "queued" row with no disk artifacts is a phantom from + // gsd_milestone_generate_id that was never planned (#3645). if (isDbAvailable()) { const dbRow = getMilestone(mid); - if (dbRow) return false; + if (dbRow) { + if (dbRow.status === 'queued') { + const hasContent = resolveMilestoneFile(basePath, mid, "CONTEXT") + || resolveMilestoneFile(basePath, mid, "ROADMAP") + || resolveMilestoneFile(basePath, mid, "SUMMARY"); + return !hasContent; + } + return false; + } } // If a worktree exists for this milestone, it was legitimately created.