From b28299bd609302a40477f048459c5d2213004d54 Mon Sep 17 00:00:00 2001 From: Lex Christopherson Date: Thu, 12 Mar 2026 08:55:40 -0600 Subject: [PATCH] fix: allow migration without ROADMAP.md (#93, #90) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ROADMAP.md was the only fatal requirement for .planning → .gsd migration, but the transformer already had a null-roadmap fallback that infers milestones from the phases/ directory. Downgrade to warning so partial v1 projects can migrate successfully. Closes #93 Closes #90 Co-Authored-By: Claude Opus 4.6 --- README.md | 2 +- src/resources/extensions/gsd/migrate/command.ts | 2 +- src/resources/extensions/gsd/migrate/validator.ts | 10 ++++------ .../extensions/gsd/tests/migrate-parser.test.ts | 10 +++++----- .../gsd/tests/migrate-validator-parsers.test.ts | 6 +++--- 5 files changed, 14 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index f94fea295..65d5c4681 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ GSD v2 solves all of these because it's not a prompt framework anymore — it's ### Migrating from v1 -> **Note:** A `ROADMAP.md` file is **required** for migration. If your project doesn't have one, you'll need to create it before running the migration command. +> **Note:** Migration works best with a `ROADMAP.md` file for milestone structure. Without one, milestones are inferred from the `phases/` directory. If you have projects with `.planning` directories from the original Get Shit Done, you can migrate them to GSD-2's `.gsd` format: diff --git a/src/resources/extensions/gsd/migrate/command.ts b/src/resources/extensions/gsd/migrate/command.ts index 58fec1f1d..e80731c14 100644 --- a/src/resources/extensions/gsd/migrate/command.ts +++ b/src/resources/extensions/gsd/migrate/command.ts @@ -99,7 +99,7 @@ export async function handleMigrate( `Directory not found: ${sourcePath}\n\n` + 'Migration converts a .planning/ directory (from older GSD versions) into .gsd/ format.\n' + 'If you are starting a new project, use /gsd:new-project instead.\n' + - 'If migrating, ensure the path contains a .planning/ directory with a ROADMAP.md file.', + 'If migrating, ensure the path contains a .planning/ directory.', "error", ); return; diff --git a/src/resources/extensions/gsd/migrate/validator.ts b/src/resources/extensions/gsd/migrate/validator.ts index 9325eea7f..2bbf44dfb 100644 --- a/src/resources/extensions/gsd/migrate/validator.ts +++ b/src/resources/extensions/gsd/migrate/validator.ts @@ -14,7 +14,7 @@ function issue(file: string, severity: ValidationSeverity, message: string): Val /** * Validate that a .planning directory has the minimum required structure. * Returns structured issues with severity levels: - * - fatal: directory doesn't exist or ROADMAP.md missing (migration cannot proceed) + * - fatal: directory doesn't exist (migration cannot proceed) * - warning: optional files missing (migration can proceed with reduced data) */ export async function validatePlanningDirectory(path: string): Promise { @@ -26,12 +26,10 @@ export async function validatePlanningDirectory(path: string): Promise i.severity === 'fatal' && i.file.includes('ROADMAP')), - 'no roadmap: fatal issue mentions ROADMAP' + result.issues.some(i => i.severity === 'warning' && i.file.includes('ROADMAP')), + 'no roadmap: warning issue mentions ROADMAP' ); } finally { cleanup(base); diff --git a/src/resources/extensions/gsd/tests/migrate-validator-parsers.test.ts b/src/resources/extensions/gsd/tests/migrate-validator-parsers.test.ts index 6e9804e8e..fa5b149bd 100644 --- a/src/resources/extensions/gsd/tests/migrate-validator-parsers.test.ts +++ b/src/resources/extensions/gsd/tests/migrate-validator-parsers.test.ts @@ -211,15 +211,15 @@ async function main(): Promise { } } - console.log('\n=== Validator: missing ROADMAP.md → fatal ==='); + console.log('\n=== Validator: missing ROADMAP.md → warning (not fatal) ==='); { const base = createFixtureBase(); try { const planning = createPlanningDir(base); writeFileSync(join(planning, 'PROJECT.md'), SAMPLE_PROJECT); const result = await validatePlanningDirectory(planning); - assertEq(result.valid, false, 'no roadmap: validation fails'); - assert(result.issues.some(i => i.severity === 'fatal' && i.file.includes('ROADMAP')), 'no roadmap: fatal issue mentions ROADMAP'); + assertEq(result.valid, true, 'no roadmap: validation still passes'); + assert(result.issues.some(i => i.severity === 'warning' && i.file.includes('ROADMAP')), 'no roadmap: warning issue mentions ROADMAP'); } finally { cleanup(base); }