diff --git a/src/resources/extensions/gsd/auto-worktree.ts b/src/resources/extensions/gsd/auto-worktree.ts index e16998c1d..d03500ead 100644 --- a/src/resources/extensions/gsd/auto-worktree.ts +++ b/src/resources/extensions/gsd/auto-worktree.ts @@ -182,7 +182,7 @@ export function syncGsdStateToWorktree( const mainMilestones = readdirSync(mainMilestonesDir, { withFileTypes: true, }) - .filter((d) => d.isDirectory() && /^M\d{3}/.test(d.name)) + .filter((d) => d.isDirectory()) .map((d) => d.name); for (const mid of mainMilestones) { @@ -339,7 +339,7 @@ export function syncWorktreeStateBack( try { const wtMilestones = readdirSync(wtMilestonesDir, { withFileTypes: true }) - .filter((d) => d.isDirectory() && /^M\d{3}/.test(d.name)) + .filter((d) => d.isDirectory()) .map((d) => d.name); for (const mid of wtMilestones) { diff --git a/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts b/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts index 56fdb4f9b..9c5552a2c 100644 --- a/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +++ b/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts @@ -19,6 +19,8 @@ * - syncWorktreeStateBack syncs root-level .gsd/ files (REQUIREMENTS, PROJECT, etc.) * - syncWorktreeStateBack syncs ALL milestone directories, not just the current one * - syncWorktreeStateBack handles next-milestone artifacts created during completion + * - syncGsdStateToWorktree syncs non-standard milestone dir names (#1547) + * - syncWorktreeStateBack syncs non-standard milestone dir names (#1547) */ import { mkdtempSync, mkdirSync, writeFileSync, rmSync, existsSync, readFileSync } from 'node:fs'; @@ -517,6 +519,78 @@ async function main(): Promise { } } + // ─── 14. syncGsdStateToWorktree syncs non-standard milestone dir names (#1547) ── + console.log('\n=== 14. syncGsdStateToWorktree syncs non-standard milestone dir names (#1547) ==='); + { + const mainBase = createBase('main'); + const wtBase = createBase('wt'); + + try { + // Main has milestone dirs with non-standard names + const customDir = join(mainBase, '.gsd', 'milestones', 'sprint-alpha'); + mkdirSync(customDir, { recursive: true }); + writeFileSync(join(customDir, 'CONTEXT.md'), '# Sprint Alpha Context'); + + const suffixDir = join(mainBase, '.gsd', 'milestones', 'M001-abc123'); + mkdirSync(suffixDir, { recursive: true }); + writeFileSync(join(suffixDir, 'M001-abc123-CONTEXT.md'), '# M001 Context'); + + assertTrue(!existsSync(join(wtBase, '.gsd', 'milestones', 'sprint-alpha')), 'sprint-alpha missing before sync'); + assertTrue(!existsSync(join(wtBase, '.gsd', 'milestones', 'M001-abc123')), 'M001-abc123 missing before sync'); + + const result = syncGsdStateToWorktree(mainBase, wtBase); + + assertTrue( + existsSync(join(wtBase, '.gsd', 'milestones', 'sprint-alpha', 'CONTEXT.md')), + '#1547: non-standard milestone dir "sprint-alpha" synced to worktree', + ); + assertTrue( + existsSync(join(wtBase, '.gsd', 'milestones', 'M001-abc123', 'M001-abc123-CONTEXT.md')), + '#1547: suffixed milestone dir "M001-abc123" synced to worktree', + ); + assertTrue(result.synced.length > 0, 'sync reported files'); + } finally { + cleanup(mainBase); + cleanup(wtBase); + } + } + + // ─── 15. syncWorktreeStateBack syncs non-standard milestone dir names (#1547) ── + console.log('\n=== 15. syncWorktreeStateBack syncs non-standard milestone dir names (#1547) ==='); + { + const mainBase = mkdtempSync(join(tmpdir(), 'gsd-wt-back-custom-main-')); + const wtBase = mkdtempSync(join(tmpdir(), 'gsd-wt-back-custom-wt-')); + + try { + mkdirSync(join(mainBase, '.gsd', 'milestones'), { recursive: true }); + mkdirSync(join(wtBase, '.gsd', 'milestones'), { recursive: true }); + + // Worktree has a non-standard milestone dir + const wtCustomDir = join(wtBase, '.gsd', 'milestones', 'sprint-beta'); + mkdirSync(wtCustomDir, { recursive: true }); + writeFileSync(join(wtCustomDir, 'SUMMARY.md'), '# Sprint Beta Summary'); + + assertTrue( + !existsSync(join(mainBase, '.gsd', 'milestones', 'sprint-beta')), + 'sprint-beta missing in main before sync', + ); + + const { synced } = syncWorktreeStateBack(mainBase, wtBase, 'M001'); + + assertTrue( + existsSync(join(mainBase, '.gsd', 'milestones', 'sprint-beta', 'SUMMARY.md')), + '#1547: non-standard milestone dir "sprint-beta" synced back to main', + ); + assertTrue( + synced.some((p) => p.includes('sprint-beta')), + '#1547: sprint-beta appears in synced list', + ); + } finally { + rmSync(mainBase, { recursive: true, force: true }); + rmSync(wtBase, { recursive: true, force: true }); + } + } + report(); }