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 f50d9df7b..57ebe3740 100644 --- a/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +++ b/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts @@ -221,7 +221,8 @@ describe('worktree-sync-milestones', async () => { try { // Build worktree milestone structure with slice-level and task-level files - const wtSliceDir = join(wtBase, '.gsd', 'milestones', 'M001', 'slices', 'S01'); + // Use M002 as the milestone to sync, M001 as the "current" being merged (skipped) + const wtSliceDir = join(wtBase, '.gsd', 'milestones', 'M002', 'slices', 'S01'); const wtTasksDir = join(wtSliceDir, 'tasks'); mkdirSync(wtTasksDir, { recursive: true }); writeFileSync(join(wtSliceDir, 'S01-SUMMARY.md'), '# S01 Summary'); @@ -229,11 +230,12 @@ describe('worktree-sync-milestones', async () => { writeFileSync(join(wtTasksDir, 'T02-SUMMARY.md'), '# T02 Summary'); // Main project root starts with only the milestone directory (no slices yet) - mkdirSync(join(mainBase, '.gsd', 'milestones', 'M001'), { recursive: true }); + mkdirSync(join(mainBase, '.gsd', 'milestones', 'M002'), { recursive: true }); + // Pass M001 as milestoneId (the one being merged/skipped), M002 should still sync const { synced } = syncWorktreeStateBack(mainBase, wtBase, 'M001'); - const mainSliceDir = join(mainBase, '.gsd', 'milestones', 'M001', 'slices', 'S01'); + const mainSliceDir = join(mainBase, '.gsd', 'milestones', 'M002', 'slices', 'S01'); const mainTasksDir = join(mainSliceDir, 'tasks'); assert.ok( @@ -341,16 +343,16 @@ describe('worktree-sync-milestones', async () => { 'M002 missing in main before sync', ); - // Sync with milestoneId = M001 (the current milestone) + // Sync with milestoneId = M001 (the current milestone being merged — skipped) const { synced } = syncWorktreeStateBack(mainBase, wtBase, 'M001'); - // M001 should be synced (current milestone — always synced) + // M001 should be SKIPPED (current milestone being merged — #3641) assert.ok( - existsSync(join(mainBase, '.gsd', 'milestones', 'M001', 'M001-SUMMARY.md')), - 'M001 SUMMARY synced to main', + !existsSync(join(mainBase, '.gsd', 'milestones', 'M001', 'M001-SUMMARY.md')), + 'M001 SUMMARY NOT synced (current milestone skipped to prevent merge conflicts)', ); - // M002 should ALSO be synced (next milestone — the fix) + // M002 should be synced (other milestone — not skipped) assert.ok( existsSync(join(mainBase, '.gsd', 'milestones', 'M002-abc123', 'M002-abc123-CONTEXT.md')), 'M002 CONTEXT synced to main (next-milestone fix)', @@ -407,20 +409,17 @@ describe('worktree-sync-milestones', async () => { writeFileSync(join(wtBase, '.gsd', 'REQUIREMENTS.md'), '# Requirements\n## R001-R089\n## R090 — SCIM\n## R091 — WebAuthn'); writeFileSync(join(wtBase, '.gsd', 'PROJECT.md'), '# Project\nMilestones: M001-M007'); - // Sync with milestoneId = M006 (the completing milestone) + // Sync with milestoneId = M006 (the completing milestone — skipped by sync) const { synced } = syncWorktreeStateBack(mainBase, wtBase, 'M006-589wvh'); - // Verify M006 artifacts synced + // M006 is the current milestone being merged — it should be SKIPPED (#3641) + // Its files are already in the milestone branch and would conflict with squash merge. assert.ok( - existsSync(join(mainBase, '.gsd', 'milestones', 'M006-589wvh', 'M006-589wvh-SUMMARY.md')), - 'M006 SUMMARY synced', - ); - assert.ok( - existsSync(join(mainBase, '.gsd', 'milestones', 'M006-589wvh', 'slices', 'S01', 'S01-SUMMARY.md')), - 'M006 S01 SUMMARY synced', + !existsSync(join(mainBase, '.gsd', 'milestones', 'M006-589wvh', 'M006-589wvh-SUMMARY.md')), + 'M006 SUMMARY NOT synced (current milestone skipped)', ); - // Verify M007 artifacts synced (the critical fix) + // Verify M007 artifacts synced (the critical fix — other milestones still sync) assert.ok( existsSync(join(mainBase, '.gsd', 'milestones', 'M007-wortc8', 'M007-wortc8-CONTEXT.md')), 'M007 CONTEXT synced to main (next-milestone)', diff --git a/src/resources/extensions/gsd/tests/worktree-sync-tasks.test.ts b/src/resources/extensions/gsd/tests/worktree-sync-tasks.test.ts index 43d57c59e..65717415c 100644 --- a/src/resources/extensions/gsd/tests/worktree-sync-tasks.test.ts +++ b/src/resources/extensions/gsd/tests/worktree-sync-tasks.test.ts @@ -47,7 +47,8 @@ function writeFile(dir: string, relativePath: string, content: string): void { test("syncWorktreeStateBack copies task summaries from tasks/ subdirectory (#1678)", () => { const mainBase = makeTempDir("main"); const wtBase = makeTempDir("wt"); - const mid = "M001"; + const currentMid = "M000"; // milestone being merged (skipped by sync) + const mid = "M001"; // other milestone that should be synced try { // Set up worktree with milestone, slice, and task files @@ -64,8 +65,8 @@ test("syncWorktreeStateBack copies task summaries from tasks/ subdirectory (#167 // Set up main with empty .gsd mkdirSync(join(mainBase, ".gsd"), { recursive: true }); - // Run sync - const result = syncWorktreeStateBack(mainBase, wtBase, mid); + // Run sync — currentMid is skipped, mid (M001) should be synced + const result = syncWorktreeStateBack(mainBase, wtBase, currentMid); // Verify milestone-level files synced assert.ok( @@ -126,7 +127,8 @@ test("syncWorktreeStateBack copies task summaries from tasks/ subdirectory (#167 test("syncWorktreeStateBack handles multiple slices with tasks (#1678)", () => { const mainBase = makeTempDir("main"); const wtBase = makeTempDir("wt"); - const mid = "M002"; + const currentMid = "M000"; // milestone being merged (skipped) + const mid = "M002"; // other milestone that should be synced try { // Set up two slices with tasks @@ -139,7 +141,7 @@ test("syncWorktreeStateBack handles multiple slices with tasks (#1678)", () => { mkdirSync(join(mainBase, ".gsd"), { recursive: true }); - const result = syncWorktreeStateBack(mainBase, wtBase, mid); + const result = syncWorktreeStateBack(mainBase, wtBase, currentMid); // All task summaries from both slices should be synced assert.ok(existsSync(join(mainBase, `.gsd/milestones/${mid}/slices/S01/tasks/T01-SUMMARY.md`))); @@ -160,7 +162,8 @@ test("syncWorktreeStateBack handles multiple slices with tasks (#1678)", () => { test("syncWorktreeStateBack handles slices without tasks/ directory", () => { const mainBase = makeTempDir("main"); const wtBase = makeTempDir("wt"); - const mid = "M003"; + const currentMid = "M000"; // milestone being merged (skipped) + const mid = "M003"; // other milestone that should be synced try { // Slice with no tasks/ subdirectory (legitimate case: pre-planning) @@ -168,7 +171,7 @@ test("syncWorktreeStateBack handles slices without tasks/ directory", () => { mkdirSync(join(mainBase, ".gsd"), { recursive: true }); - const result = syncWorktreeStateBack(mainBase, wtBase, mid); + const result = syncWorktreeStateBack(mainBase, wtBase, currentMid); // Should sync the slice file without errors assert.ok(existsSync(join(mainBase, `.gsd/milestones/${mid}/slices/S01/S01-RESEARCH.md`))); @@ -183,7 +186,8 @@ test("syncWorktreeStateBack handles slices without tasks/ directory", () => { test("syncWorktreeStateBack ignores non-md files in tasks/", () => { const mainBase = makeTempDir("main"); const wtBase = makeTempDir("wt"); - const mid = "M004"; + const currentMid = "M000"; // milestone being merged (skipped) + const mid = "M004"; // other milestone that should be synced try { writeFile(wtBase, `.gsd/milestones/${mid}/slices/S01/S01-PLAN.md`, "# Plan\n"); @@ -194,7 +198,7 @@ test("syncWorktreeStateBack ignores non-md files in tasks/", () => { mkdirSync(join(mainBase, ".gsd"), { recursive: true }); - const result = syncWorktreeStateBack(mainBase, wtBase, mid); + const result = syncWorktreeStateBack(mainBase, wtBase, currentMid); // Only .md files should be synced assert.ok(existsSync(join(mainBase, `.gsd/milestones/${mid}/slices/S01/tasks/T01-SUMMARY.md`)));