From f65f92cf7626d7d19b03bfd19fa805c5b6a02dca Mon Sep 17 00:00:00 2001 From: Tibsfox Date: Mon, 6 Apr 2026 20:07:42 -0700 Subject: [PATCH] fix(gsd): import all-done milestones as complete during DB migration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit migrateHierarchyToDb imported milestones with all-done roadmap slices as "active" when SUMMARY.md was missing. This let plan-milestone overwrite already-completed work. Now checks parsed roadmap slices — if all are done, imports as "complete" even without SUMMARY.md. Closes #3390 Closes #3379 Co-Authored-By: Claude Opus 4.6 (1M context) --- src/resources/extensions/gsd/md-importer.ts | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/resources/extensions/gsd/md-importer.ts b/src/resources/extensions/gsd/md-importer.ts index dfea9ad7c..b1054b5a7 100644 --- a/src/resources/extensions/gsd/md-importer.ts +++ b/src/resources/extensions/gsd/md-importer.ts @@ -530,11 +530,6 @@ export function migrateHierarchyToDb(basePath: string): { // Ghost milestone: no CONTEXT, ROADMAP, or SUMMARY → skip if (!hasRoadmap && !hasContext && !hasSummary) continue; - // Determine milestone status - let milestoneStatus = 'active'; - if (hasSummary) milestoneStatus = 'complete'; - else if (hasParked) milestoneStatus = 'parked'; - // Determine milestone title from roadmap H1 or CONTEXT heading let milestoneTitle = ''; let roadmapContent: string | null = null; @@ -544,6 +539,16 @@ export function migrateHierarchyToDb(basePath: string): { roadmap = parseRoadmap(roadmapContent); milestoneTitle = roadmap.title; } + + // Determine milestone status + let milestoneStatus = 'active'; + if (hasSummary) milestoneStatus = 'complete'; + else if (hasParked) milestoneStatus = 'parked'; + // Import milestones with all-done roadmap slices as complete (#3390, #3379) + // even when SUMMARY.md is missing — the roadmap checkboxes are authoritative. + else if (roadmap && roadmap.slices.length > 0 && roadmap.slices.every(s => s.done)) { + milestoneStatus = 'complete'; + } if (!milestoneTitle && hasContext) { const contextContent = readFileSync(contextPath!, 'utf-8'); const h1Match = contextContent.match(/^#\s+(.+)/m);