gsd-pi 2.78.1-dev.b0759e59b → 2.78.1-dev.e9d88a536

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (203) hide show
  1. package/README.md +8 -5
  2. package/dist/headless-recover.d.ts +23 -0
  3. package/dist/headless-recover.js +93 -0
  4. package/dist/headless.js +9 -0
  5. package/dist/help-text.js +1 -0
  6. package/dist/resources/.managed-resources-content-hash +1 -1
  7. package/dist/resources/extensions/browser-tools/tools/intent.js +8 -1
  8. package/dist/resources/extensions/gsd/auto-dispatch.js +4 -56
  9. package/dist/resources/extensions/gsd/auto-post-unit.js +7 -27
  10. package/dist/resources/extensions/gsd/auto-start.js +1 -8
  11. package/dist/resources/extensions/gsd/auto-worktree.js +59 -176
  12. package/dist/resources/extensions/gsd/auto.js +24 -6
  13. package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +9 -77
  14. package/dist/resources/extensions/gsd/commands-codebase.js +2 -2
  15. package/dist/resources/extensions/gsd/commands-handlers.js +5 -5
  16. package/dist/resources/extensions/gsd/commands-logs.js +2 -2
  17. package/dist/resources/extensions/gsd/commands-scan.js +2 -2
  18. package/dist/resources/extensions/gsd/commands-ship.js +2 -2
  19. package/dist/resources/extensions/gsd/commands-workflow-templates.js +5 -5
  20. package/dist/resources/extensions/gsd/db-writer.js +16 -85
  21. package/dist/resources/extensions/gsd/dispatch-guard.js +6 -10
  22. package/dist/resources/extensions/gsd/doctor-engine-checks.js +2 -2
  23. package/dist/resources/extensions/gsd/gsd-db.js +74 -8
  24. package/dist/resources/extensions/gsd/guided-flow.js +31 -8
  25. package/dist/resources/extensions/gsd/markdown-renderer.js +14 -51
  26. package/dist/resources/extensions/gsd/parallel-merge.js +14 -13
  27. package/dist/resources/extensions/gsd/parallel-monitor-overlay.js +5 -2
  28. package/dist/resources/extensions/gsd/paths.js +35 -1
  29. package/dist/resources/extensions/gsd/prompts/plan-milestone.md +6 -0
  30. package/dist/resources/extensions/gsd/queue-order.js +6 -1
  31. package/dist/resources/extensions/gsd/rethink.js +2 -2
  32. package/dist/resources/extensions/gsd/state.js +91 -372
  33. package/dist/resources/extensions/gsd/tools/complete-milestone.js +6 -5
  34. package/dist/resources/extensions/gsd/tools/complete-slice.js +7 -12
  35. package/dist/resources/extensions/gsd/tools/complete-task.js +19 -31
  36. package/dist/resources/extensions/gsd/tools/validate-milestone.js +7 -5
  37. package/dist/resources/extensions/gsd/workflow-manifest.js +2 -1
  38. package/dist/resources/extensions/gsd/workflow-mcp-auto-prep.js +3 -21
  39. package/dist/resources/extensions/gsd/workflow-reconcile.js +3 -3
  40. package/dist/resources/extensions/gsd/worktree-command.js +4 -3
  41. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  42. package/dist/web/standalone/.next/BUILD_ID +1 -1
  43. package/dist/web/standalone/.next/app-path-routes-manifest.json +12 -12
  44. package/dist/web/standalone/.next/build-manifest.json +2 -2
  45. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  46. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  47. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  48. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  49. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  50. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  51. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  52. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  53. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  54. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  55. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  56. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  57. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  58. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  59. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  60. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  61. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  62. package/dist/web/standalone/.next/server/app/api/boot/route.js.nft.json +1 -1
  63. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js.nft.json +1 -1
  64. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js.nft.json +1 -1
  65. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js.nft.json +1 -1
  66. package/dist/web/standalone/.next/server/app/api/captures/route.js.nft.json +1 -1
  67. package/dist/web/standalone/.next/server/app/api/cleanup/route.js.nft.json +1 -1
  68. package/dist/web/standalone/.next/server/app/api/doctor/route.js.nft.json +1 -1
  69. package/dist/web/standalone/.next/server/app/api/export-data/route.js.nft.json +1 -1
  70. package/dist/web/standalone/.next/server/app/api/files/route.js.nft.json +1 -1
  71. package/dist/web/standalone/.next/server/app/api/forensics/route.js.nft.json +1 -1
  72. package/dist/web/standalone/.next/server/app/api/git/route.js.nft.json +1 -1
  73. package/dist/web/standalone/.next/server/app/api/history/route.js.nft.json +1 -1
  74. package/dist/web/standalone/.next/server/app/api/hooks/route.js.nft.json +1 -1
  75. package/dist/web/standalone/.next/server/app/api/inspect/route.js.nft.json +1 -1
  76. package/dist/web/standalone/.next/server/app/api/knowledge/route.js.nft.json +1 -1
  77. package/dist/web/standalone/.next/server/app/api/live-state/route.js.nft.json +1 -1
  78. package/dist/web/standalone/.next/server/app/api/notifications/route.js.nft.json +1 -1
  79. package/dist/web/standalone/.next/server/app/api/onboarding/route.js.nft.json +1 -1
  80. package/dist/web/standalone/.next/server/app/api/projects/route.js.nft.json +1 -1
  81. package/dist/web/standalone/.next/server/app/api/recovery/route.js.nft.json +1 -1
  82. package/dist/web/standalone/.next/server/app/api/session/browser/route.js.nft.json +1 -1
  83. package/dist/web/standalone/.next/server/app/api/session/command/route.js.nft.json +1 -1
  84. package/dist/web/standalone/.next/server/app/api/session/events/route.js.nft.json +1 -1
  85. package/dist/web/standalone/.next/server/app/api/session/manage/route.js.nft.json +1 -1
  86. package/dist/web/standalone/.next/server/app/api/settings-data/route.js.nft.json +1 -1
  87. package/dist/web/standalone/.next/server/app/api/skill-health/route.js.nft.json +1 -1
  88. package/dist/web/standalone/.next/server/app/api/steer/route.js.nft.json +1 -1
  89. package/dist/web/standalone/.next/server/app/api/switch-root/route.js.nft.json +1 -1
  90. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js.nft.json +1 -1
  91. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js.nft.json +1 -1
  92. package/dist/web/standalone/.next/server/app/api/undo/route.js.nft.json +1 -1
  93. package/dist/web/standalone/.next/server/app/api/visualizer/route.js.nft.json +1 -1
  94. package/dist/web/standalone/.next/server/app/index.html +1 -1
  95. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  96. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  97. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  98. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  99. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  100. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  101. package/dist/web/standalone/.next/server/app-paths-manifest.json +12 -12
  102. package/dist/web/standalone/.next/server/chunks/6336.js +1 -0
  103. package/dist/web/standalone/.next/server/chunks/6897.js +1 -1
  104. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  105. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  106. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  107. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  108. package/package.json +1 -1
  109. package/packages/mcp-server/dist/workflow-tools.d.ts +6 -0
  110. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  111. package/packages/mcp-server/dist/workflow-tools.js +56 -2
  112. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  113. package/packages/mcp-server/src/parse-workflow-args.test.ts +80 -0
  114. package/packages/mcp-server/src/workflow-tools.ts +61 -2
  115. package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
  116. package/src/resources/extensions/browser-tools/tools/intent.ts +13 -2
  117. package/src/resources/extensions/gsd/auto-dispatch.ts +4 -60
  118. package/src/resources/extensions/gsd/auto-post-unit.ts +7 -26
  119. package/src/resources/extensions/gsd/auto-start.ts +1 -8
  120. package/src/resources/extensions/gsd/auto-worktree.ts +61 -204
  121. package/src/resources/extensions/gsd/auto.ts +23 -6
  122. package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +9 -84
  123. package/src/resources/extensions/gsd/commands-codebase.ts +2 -2
  124. package/src/resources/extensions/gsd/commands-handlers.ts +5 -5
  125. package/src/resources/extensions/gsd/commands-logs.ts +2 -2
  126. package/src/resources/extensions/gsd/commands-scan.ts +2 -2
  127. package/src/resources/extensions/gsd/commands-ship.ts +2 -2
  128. package/src/resources/extensions/gsd/commands-workflow-templates.ts +5 -5
  129. package/src/resources/extensions/gsd/db-writer.ts +16 -83
  130. package/src/resources/extensions/gsd/dispatch-guard.ts +6 -11
  131. package/src/resources/extensions/gsd/doctor-engine-checks.ts +2 -2
  132. package/src/resources/extensions/gsd/gsd-db.ts +85 -8
  133. package/src/resources/extensions/gsd/guided-flow.ts +35 -8
  134. package/src/resources/extensions/gsd/markdown-renderer.ts +13 -64
  135. package/src/resources/extensions/gsd/parallel-merge.ts +14 -13
  136. package/src/resources/extensions/gsd/parallel-monitor-overlay.ts +5 -2
  137. package/src/resources/extensions/gsd/paths.ts +55 -1
  138. package/src/resources/extensions/gsd/prompts/plan-milestone.md +6 -0
  139. package/src/resources/extensions/gsd/queue-order.ts +6 -1
  140. package/src/resources/extensions/gsd/rethink.ts +2 -2
  141. package/src/resources/extensions/gsd/state.ts +91 -389
  142. package/src/resources/extensions/gsd/tests/artifact-corruption-2630.test.ts +1 -0
  143. package/src/resources/extensions/gsd/tests/auto-paused-session-validation.test.ts +6 -0
  144. package/src/resources/extensions/gsd/tests/auto-remediate-slice-status.test.ts +21 -34
  145. package/src/resources/extensions/gsd/tests/complete-task-rollback-evidence.test.ts +6 -7
  146. package/src/resources/extensions/gsd/tests/complete-task.test.ts +8 -6
  147. package/src/resources/extensions/gsd/tests/completed-at-reconcile.test.ts +12 -27
  148. package/src/resources/extensions/gsd/tests/completed-units-metrics-sync.test.ts +18 -5
  149. package/src/resources/extensions/gsd/tests/db-path-worktree-symlink.test.ts +4 -4
  150. package/src/resources/extensions/gsd/tests/db-writer.test.ts +14 -16
  151. package/src/resources/extensions/gsd/tests/derive-state-crossval.test.ts +6 -5
  152. package/src/resources/extensions/gsd/tests/derive-state-db-disk-reconcile.test.ts +10 -38
  153. package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +136 -56
  154. package/src/resources/extensions/gsd/tests/derive-state-draft.test.ts +3 -0
  155. package/src/resources/extensions/gsd/tests/derive-state-helpers.test.ts +119 -61
  156. package/src/resources/extensions/gsd/tests/derive-state.test.ts +4 -0
  157. package/src/resources/extensions/gsd/tests/dispatch-complete-milestone-guard.test.ts +6 -20
  158. package/src/resources/extensions/gsd/tests/dispatch-guard.test.ts +4 -5
  159. package/src/resources/extensions/gsd/tests/dispatcher-stuck-planning.test.ts +14 -15
  160. package/src/resources/extensions/gsd/tests/ensure-db-open.test.ts +11 -16
  161. package/src/resources/extensions/gsd/tests/escalation.test.ts +2 -1
  162. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +2 -1
  163. package/src/resources/extensions/gsd/tests/gsdroot-worktree-detection.test.ts +15 -36
  164. package/src/resources/extensions/gsd/tests/handler-worktree-write-isolation.test.ts +57 -0
  165. package/src/resources/extensions/gsd/tests/integration/parallel-merge.test.ts +15 -15
  166. package/src/resources/extensions/gsd/tests/integration/state-machine-edge-cases.test.ts +15 -5
  167. package/src/resources/extensions/gsd/tests/markdown-renderer.test.ts +14 -8
  168. package/src/resources/extensions/gsd/tests/md-importer.test.ts +2 -1
  169. package/src/resources/extensions/gsd/tests/memory-store.test.ts +3 -2
  170. package/src/resources/extensions/gsd/tests/park-milestone.test.ts +2 -0
  171. package/src/resources/extensions/gsd/tests/progressive-planning.test.ts +25 -16
  172. package/src/resources/extensions/gsd/tests/projection-regression.test.ts +1 -0
  173. package/src/resources/extensions/gsd/tests/ready-phrase-no-files-4573.test.ts +184 -0
  174. package/src/resources/extensions/gsd/tests/register-hooks-compaction-checkpoint.test.ts +6 -1
  175. package/src/resources/extensions/gsd/tests/replan-slice.test.ts +3 -0
  176. package/src/resources/extensions/gsd/tests/resolve-ts.mjs +4 -0
  177. package/src/resources/extensions/gsd/tests/rogue-file-detection.test.ts +3 -4
  178. package/src/resources/extensions/gsd/tests/slice-disk-reconcile.test.ts +10 -56
  179. package/src/resources/extensions/gsd/tests/stale-slice-rows.test.ts +15 -16
  180. package/src/resources/extensions/gsd/tests/state-corruption-2945.test.ts +1 -0
  181. package/src/resources/extensions/gsd/tests/state-machine-full-walkthrough.test.ts +23 -27
  182. package/src/resources/extensions/gsd/tests/steer-worktree-path.test.ts +13 -14
  183. package/src/resources/extensions/gsd/tests/stop-auto-remote.test.ts +4 -3
  184. package/src/resources/extensions/gsd/tests/sync-worktree-skip-current.test.ts +10 -33
  185. package/src/resources/extensions/gsd/tests/validate-milestone-write-order.test.ts +7 -8
  186. package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +9 -15
  187. package/src/resources/extensions/gsd/tests/workflow-logger-wiring.test.ts +12 -7
  188. package/src/resources/extensions/gsd/tests/workflow-mcp-auto-prep.test.ts +4 -4
  189. package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +24 -1
  190. package/src/resources/extensions/gsd/tests/worktree-db-same-file.test.ts +13 -0
  191. package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +65 -71
  192. package/src/resources/extensions/gsd/tests/worktree-sync-tasks.test.ts +26 -151
  193. package/src/resources/extensions/gsd/tools/complete-milestone.ts +7 -5
  194. package/src/resources/extensions/gsd/tools/complete-slice.ts +7 -14
  195. package/src/resources/extensions/gsd/tools/complete-task.ts +19 -34
  196. package/src/resources/extensions/gsd/tools/validate-milestone.ts +7 -5
  197. package/src/resources/extensions/gsd/workflow-manifest.ts +4 -1
  198. package/src/resources/extensions/gsd/workflow-mcp-auto-prep.ts +2 -18
  199. package/src/resources/extensions/gsd/workflow-reconcile.ts +3 -3
  200. package/src/resources/extensions/gsd/worktree-command.ts +4 -3
  201. package/dist/web/standalone/.next/server/chunks/8527.js +0 -1
  202. /package/dist/web/standalone/.next/static/{rk1EN3FQTE6Z1yalkW_GE → oZGTPvJBQX_IDKKnuV8Bt}/_buildManifest.js +0 -0
  203. /package/dist/web/standalone/.next/static/{rk1EN3FQTE6Z1yalkW_GE → oZGTPvJBQX_IDKKnuV8Bt}/_ssgManifest.js +0 -0
@@ -135,7 +135,7 @@ test("stopAutoRemote sends SIGTERM to a live process and returns found:true", {
135
135
 
136
136
  // ─── Lock path: original project root vs worktree ────────────────────────
137
137
 
138
- test("lock file should be discoverable at project root, not worktree path", () => {
138
+ test("lock file should be discoverable from project root and worktree path", () => {
139
139
  const projectRoot = makeTmpBase();
140
140
  const worktreePath = join(projectRoot, ".gsd", "worktrees", "M001");
141
141
  mkdirSync(join(worktreePath, ".gsd"), { recursive: true });
@@ -149,9 +149,10 @@ test("lock file should be discoverable at project root, not worktree path", () =
149
149
  assert.ok(lock, "lock should be found at project root");
150
150
  assert.equal(lock!.unitType, "execute-task");
151
151
 
152
- // Worktree path should NOT have a lock
152
+ // Worktree path resolves to the same canonical project .gsd lock.
153
153
  const worktreeLock = readCrashLock(worktreePath);
154
- assert.equal(worktreeLock, null, "lock should NOT exist at worktree path");
154
+ assert.ok(worktreeLock, "lock should be discoverable via worktree path");
155
+ assert.equal(worktreeLock!.unitType, "execute-task");
155
156
  } finally {
156
157
  cleanup(projectRoot);
157
158
  }
@@ -1,12 +1,9 @@
1
1
  /**
2
- * Regression test for #3641 syncWorktreeStateBack skips current milestone
2
+ * Regression test for DB-authoritative syncWorktreeStateBack behavior.
3
3
  *
4
- * When syncing worktree state back to main, the current milestone being
5
- * merged should be skipped. Its files are already in the milestone branch
6
- * and copying them back would conflict with the squash merge.
7
- *
8
- * The fix adds a `mid === milestoneId` skip guard inside the milestone
9
- * iteration loop in syncWorktreeStateBack.
4
+ * Worktree milestone projections are not authoritative. syncWorktreeStateBack
5
+ * may reconcile a legacy worktree DB and copy diagnostics, but must not copy
6
+ * milestone markdown directories back into the project root.
10
7
  */
11
8
 
12
9
  import { describe, it } from 'node:test'
@@ -20,7 +17,7 @@ const src = readFileSync(
20
17
  'utf-8',
21
18
  )
22
19
 
23
- describe('syncWorktreeStateBack skips current milestone (#3641)', () => {
20
+ describe('syncWorktreeStateBack does not copy worktree milestone projections', () => {
24
21
  it('syncWorktreeStateBack function exists', () => {
25
22
  assert.ok(
26
23
  src.includes('function syncWorktreeStateBack('),
@@ -28,7 +25,7 @@ describe('syncWorktreeStateBack skips current milestone (#3641)', () => {
28
25
  )
29
26
  })
30
27
 
31
- it('mid === milestoneId skip guard exists in the milestone loop', () => {
28
+ it('does not iterate worktree milestones for copy-back', () => {
32
29
  // Find syncWorktreeStateBack
33
30
  const fnStart = src.indexOf('function syncWorktreeStateBack(')
34
31
  assert.ok(fnStart !== -1)
@@ -36,31 +33,11 @@ describe('syncWorktreeStateBack skips current milestone (#3641)', () => {
36
33
  // Get a reasonable portion of the function
37
34
  const fnBlock = extractSourceRegion(src, 'function syncWorktreeStateBack(', { fromIdx: fnStart })
38
35
 
39
- // Find the for loop iterating milestones
40
- const loopIdx = fnBlock.indexOf('for (const mid of wtMilestones)')
41
- assert.ok(loopIdx !== -1, 'milestone iteration loop must exist')
42
-
43
- // After the loop, there should be the skip guard
44
- const loopBody = extractSourceRegion(fnBlock, 'for (const mid of wtMilestones)', { fromIdx: loopIdx })
45
- assert.ok(
46
- loopBody.includes('mid === milestoneId'),
47
- 'mid === milestoneId skip guard must exist inside the milestone loop',
48
- )
49
- assert.ok(
50
- loopBody.includes('continue'),
51
- 'skip guard must use continue to skip the current milestone',
52
- )
36
+ assert.ok(!fnBlock.includes('for (const mid of wtMilestones)'), 'must not iterate worktree milestones')
37
+ assert.ok(!fnBlock.includes('syncMilestoneDir('), 'must not copy milestone markdown projections')
53
38
  })
54
39
 
55
- it('syncMilestoneDir is still called for non-current milestones', () => {
56
- const fnStart = src.indexOf('function syncWorktreeStateBack(')
57
- assert.ok(fnStart !== -1)
58
-
59
- const fnBlock = extractSourceRegion(src, 'function syncWorktreeStateBack(', { fromIdx: fnStart })
60
-
61
- assert.ok(
62
- fnBlock.includes('syncMilestoneDir('),
63
- 'syncMilestoneDir must still be called for other milestones',
64
- )
40
+ it('legacy milestone copy helper has been removed', () => {
41
+ assert.ok(!src.includes('function syncMilestoneDir('), 'syncMilestoneDir helper should not exist')
65
42
  })
66
43
  })
@@ -85,7 +85,7 @@ describe("handleValidateMilestone write ordering (#2725)", () => {
85
85
  assert.doesNotMatch(validationMd, /## Verification Class Compliance/);
86
86
  });
87
87
 
88
- it("rolls back DB row when disk write fails", async () => {
88
+ it("keeps DB row and reports stale projection when disk write fails", async () => {
89
89
  base = makeTmpBase();
90
90
  const dbPath = join(base, ".gsd", "gsd.db");
91
91
  openDatabase(dbPath);
@@ -101,16 +101,15 @@ describe("handleValidateMilestone write ordering (#2725)", () => {
101
101
 
102
102
  const result = await handleValidateMilestone(VALID_PARAMS, base);
103
103
 
104
- // Should return error
105
- assert.ok("error" in result, "should return error when disk write fails");
106
- assert.ok(result.error.includes("disk render failed"));
104
+ assert.ok(!("error" in result), `unexpected error: ${"error" in result ? result.error : ""}`);
105
+ assert.equal(result.stale, true, "result should report stale projection");
107
106
 
108
- // DB row should have been rolled back (deleted)
109
107
  const adapter = _getAdapter()!;
110
108
  const row = adapter.prepare(
111
- `SELECT * FROM assessments WHERE milestone_id = 'M001' AND scope = 'milestone-validation'`,
112
- ).get();
113
- assert.equal(row, undefined, "assessment row should be deleted after disk-write rollback");
109
+ `SELECT status FROM assessments WHERE milestone_id = 'M001' AND scope = 'milestone-validation'`,
110
+ ).get() as { status: string } | undefined;
111
+ assert.ok(row, "assessment row should remain committed");
112
+ assert.equal(row!.status, "pass");
114
113
  });
115
114
 
116
115
  it("persists milestone validation gate_runs rows when UOK gates are enabled", async () => {
@@ -394,7 +394,7 @@ test("dispatch rule skips when skip_milestone_validation preference is set", asy
394
394
  }
395
395
  });
396
396
 
397
- test("dispatch rule fails closed for failure-path SUMMARY when DB milestone is not complete (#4658)", async () => {
397
+ test("dispatch rule ignores failure-path SUMMARY projection when DB milestone is not complete (#4658 superseded)", async () => {
398
398
  const state: GSDState = {
399
399
  activeMilestone: { id: "M001", title: "Test" },
400
400
  activeSlice: null,
@@ -422,17 +422,14 @@ test("dispatch rule fails closed for failure-path SUMMARY when DB milestone is n
422
422
  prefs: undefined,
423
423
  };
424
424
  const result = await resolveDispatch(ctx);
425
- assert.equal(result.action, "stop");
426
- if (result.action === "stop") {
427
- assert.equal(result.level, "warning");
428
- assert.match(result.reason, /failure-path SUMMARY/i);
429
- }
425
+ assert.equal(result.action, "dispatch");
426
+ assert.equal(getMilestone("M001")?.status, "active");
430
427
  } finally {
431
428
  cleanup(base);
432
429
  }
433
430
  });
434
431
 
435
- test("dispatch rule reconciles DB for successful stale SUMMARY (#4658)", async () => {
432
+ test("dispatch rule does not reconcile DB from successful stale SUMMARY projection (#4658 superseded)", async () => {
436
433
  const state: GSDState = {
437
434
  activeMilestone: { id: "M001", title: "Test" },
438
435
  activeSlice: null,
@@ -473,15 +470,15 @@ test("dispatch rule reconciles DB for successful stale SUMMARY (#4658)", async (
473
470
  prefs: undefined,
474
471
  };
475
472
  const result = await resolveDispatch(ctx);
476
- assert.equal(result.action, "skip");
473
+ assert.equal(result.action, "dispatch");
477
474
  const milestone = getMilestone("M001");
478
- assert.equal(milestone?.status, "complete");
475
+ assert.equal(milestone?.status, "active");
479
476
  } finally {
480
477
  cleanup(base);
481
478
  }
482
479
  });
483
480
 
484
- test("dispatch rule fails closed for ambiguous stale SUMMARY (#4658)", async () => {
481
+ test("dispatch rule ignores ambiguous stale SUMMARY projection (#4658 superseded)", async () => {
485
482
  const state: GSDState = {
486
483
  activeMilestone: { id: "M001", title: "Test" },
487
484
  activeSlice: null,
@@ -509,11 +506,8 @@ test("dispatch rule fails closed for ambiguous stale SUMMARY (#4658)", async ()
509
506
  prefs: undefined,
510
507
  };
511
508
  const result = await resolveDispatch(ctx);
512
- assert.equal(result.action, "stop");
513
- if (result.action === "stop") {
514
- assert.equal(result.level, "warning");
515
- assert.match(result.reason, /ambiguous SUMMARY/i);
516
- }
509
+ assert.equal(result.action, "dispatch");
510
+ assert.equal(getMilestone("M001")?.status, "active");
517
511
  } finally {
518
512
  cleanup(base);
519
513
  }
@@ -205,17 +205,22 @@ test("readTransaction logs ROLLBACK failures as split-brain signal", () => {
205
205
  );
206
206
  });
207
207
 
208
- // ─── Runtime: state.ts and workflow-projections.ts log silent bailouts ─────
208
+ // ─── Runtime: startup/projection diagnostics stay explicit ─────────────────
209
209
 
210
- test("state.ts logs roadmap read failures instead of silently continuing", () => {
211
- const stateSrc = readFileSync(
212
- join(import.meta.dirname, "..", "state.ts"),
210
+ test("auto-start initializes the DB without implicit markdown migration", () => {
211
+ const autoStartSrc = readFileSync(
212
+ join(import.meta.dirname, "..", "auto-start.ts"),
213
213
  "utf-8",
214
214
  );
215
+ assert.doesNotMatch(
216
+ autoStartSrc,
217
+ /migrateFromMarkdown|md-importer/,
218
+ "auto-start must not import markdown into the runtime DB implicitly",
219
+ );
215
220
  assert.match(
216
- stateSrc,
217
- /logWarning\("state",\s*"reconcileDiskToDb: roadmap read failed/,
218
- "state.ts reconcileDiskToDb should log roadmap read failures",
221
+ autoStartSrc,
222
+ /failed to initialize project database/,
223
+ "DB initialization failures should still be logged",
219
224
  );
220
225
  });
221
226
 
@@ -15,7 +15,7 @@ test("shouldAutoPrepareWorkflowMcp enables prep for externalCli local transport"
15
15
  assert.equal(result, true);
16
16
  });
17
17
 
18
- test("shouldAutoPrepareWorkflowMcp enables prep when claude-code provider is ready", () => {
18
+ test("shouldAutoPrepareWorkflowMcp stays disabled for non-Claude active provider even when claude-code is ready", () => {
19
19
  const result = shouldAutoPrepareWorkflowMcp({
20
20
  model: { provider: "openai", baseUrl: "https://api.openai.com" },
21
21
  modelRegistry: {
@@ -24,10 +24,10 @@ test("shouldAutoPrepareWorkflowMcp enables prep when claude-code provider is rea
24
24
  },
25
25
  });
26
26
 
27
- assert.equal(result, true);
27
+ assert.equal(result, false);
28
28
  });
29
29
 
30
- test("shouldAutoPrepareWorkflowMcp enables prep when claude-code provider is registered", () => {
30
+ test("shouldAutoPrepareWorkflowMcp stays disabled for non-Claude active provider even when claude-code is registered", () => {
31
31
  const result = shouldAutoPrepareWorkflowMcp({
32
32
  model: { provider: "openai", baseUrl: "https://api.openai.com" },
33
33
  modelRegistry: {
@@ -36,7 +36,7 @@ test("shouldAutoPrepareWorkflowMcp enables prep when claude-code provider is reg
36
36
  },
37
37
  });
38
38
 
39
- assert.equal(result, true);
39
+ assert.equal(result, false);
40
40
  });
41
41
 
42
42
  test("shouldAutoPrepareWorkflowMcp stays disabled when neither transport nor provider readiness match", () => {
@@ -1,6 +1,6 @@
1
1
  import test from "node:test";
2
2
  import assert from "node:assert/strict";
3
- import { existsSync, mkdtempSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
3
+ import { existsSync, mkdtempSync, mkdirSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync } from "node:fs";
4
4
  import { dirname, join } from "node:path";
5
5
  import { tmpdir } from "node:os";
6
6
  import { fileURLToPath } from "node:url";
@@ -30,6 +30,17 @@ function readSrc(file: string): string {
30
30
  return readFileSync(join(gsdDir, file), "utf-8");
31
31
  }
32
32
 
33
+ function listTsFiles(dir: string): string[] {
34
+ const out: string[] = [];
35
+ for (const entry of readdirSync(dir)) {
36
+ const full = join(dir, entry);
37
+ const stat = statSync(full);
38
+ if (stat.isDirectory()) out.push(...listTsFiles(full));
39
+ else if (entry.endsWith(".ts")) out.push(full);
40
+ }
41
+ return out;
42
+ }
43
+
33
44
  function extractElicitPayload(request: unknown): ElicitPayload {
34
45
  const payload = (request as { params?: unknown }).params ?? request;
35
46
  return payload as ElicitPayload;
@@ -772,3 +783,15 @@ test("workflow transport error guidance includes /gsd mcp init hint", () => {
772
783
  const src = readSrc("workflow-mcp.ts");
773
784
  assert.match(src, /Please run \/gsd mcp init \./);
774
785
  });
786
+
787
+ test("buildWorkflowMcpServers is only imported by tests or Claude Code MCP boundary code", () => {
788
+ const offenders: string[] = [];
789
+ for (const file of listTsFiles(gsdDir)) {
790
+ const rel = file.slice(gsdDir.length + 1).replace(/\\/g, "/");
791
+ if (rel === "workflow-mcp.ts" || rel.startsWith("tests/")) continue;
792
+ const src = readFileSync(file, "utf-8");
793
+ if (/\bimport\b[\s\S]*\bbuildWorkflowMcpServers\b/.test(src)) offenders.push(rel);
794
+ }
795
+
796
+ assert.deepEqual(offenders, []);
797
+ });
@@ -15,6 +15,7 @@ import {
15
15
  existsSync,
16
16
  mkdirSync,
17
17
  mkdtempSync,
18
+ readFileSync,
18
19
  rmSync,
19
20
  symlinkSync,
20
21
  writeFileSync,
@@ -157,6 +158,18 @@ describe("#2823: reconcileWorktreeDb same-file guard", () => {
157
158
  });
158
159
  });
159
160
 
161
+ test("merge-time DB reconciliation only runs when legacy worktree DB exists", () => {
162
+ const src = readFileSync(join(import.meta.dirname, "..", "auto-worktree.ts"), "utf-8");
163
+ const reconcileIdx = src.indexOf("reconcileWorktreeDb(mainDbPath, worktreeDbPath)");
164
+ assert.ok(reconcileIdx !== -1, "merge-time reconcile call exists");
165
+ const guardWindow = src.slice(Math.max(0, reconcileIdx - 240), reconcileIdx);
166
+
167
+ assert.ok(
168
+ guardWindow.includes("existsSync(worktreeDbPath)") && guardWindow.includes("!isSamePath(worktreeDbPath, mainDbPath)"),
169
+ "merge-time reconcile requires a real legacy worktree DB and distinct DB paths",
170
+ );
171
+ });
172
+
160
173
  // ─── Fix 3: infrastructure error classification ─────────────────────
161
174
 
162
175
  describe("#2823: malformed DB classified as infrastructure error", () => {
@@ -5,8 +5,8 @@
5
5
  * from the main repo's .gsd/ into the worktree's .gsd/ for the
6
6
  * specified milestone, and deletes gsd.db so it rebuilds from fresh state.
7
7
  *
8
- * Also verifies that syncWorktreeStateBack recurses into tasks/ subdirectories
9
- * so task-level summaries are not dropped on milestone teardown (#1678).
8
+ * Also verifies that syncWorktreeStateBack does not import worktree markdown
9
+ * projections back into the project root.
10
10
  *
11
11
  * Covers:
12
12
  * - Milestone directory synced from main to worktree
@@ -15,12 +15,12 @@
15
15
  * - No-op when paths are equal
16
16
  * - No-op when milestoneId is null
17
17
  * - Non-existent directories handled gracefully
18
- * - syncWorktreeStateBack recurses into tasks/ subdirectory (#1678)
19
- * - syncWorktreeStateBack syncs root-level .gsd/ files (REQUIREMENTS, PROJECT, etc.)
20
- * - syncWorktreeStateBack syncs ALL milestone directories, not just the current one
21
- * - syncWorktreeStateBack handles next-milestone artifacts created during completion
18
+ * - syncWorktreeStateBack skips milestone markdown projections
19
+ * - syncWorktreeStateBack does not import root-level .gsd/ state projections
20
+ * - syncWorktreeStateBack does not copy worktree milestone projections back
21
+ * - syncWorktreeStateBack leaves next-milestone projections DB/project-root authoritative
22
22
  * - syncGsdStateToWorktree syncs non-standard milestone dir names (#1547)
23
- * - syncWorktreeStateBack syncs non-standard milestone dir names (#1547)
23
+ * - syncWorktreeStateBack skips non-standard milestone projection dir names
24
24
  */
25
25
 
26
26
  import { mkdtempSync, mkdirSync, writeFileSync, rmSync, existsSync, readFileSync } from 'node:fs';
@@ -213,8 +213,8 @@ describe('worktree-sync-milestones', async () => {
213
213
  }
214
214
  }
215
215
 
216
- // ─── 8. syncWorktreeStateBack recurses into tasks/ (#1678) ───────────
217
- console.log('\n=== 8. syncWorktreeStateBack copies tasks/ subdirectory (#1678) ===');
216
+ // ─── 8. syncWorktreeStateBack does not copy task projections ───────────
217
+ console.log('\n=== 8. syncWorktreeStateBack leaves task projections in worktree ===');
218
218
  {
219
219
  const mainBase = mkdtempSync(join(tmpdir(), 'gsd-wt-back-main-'));
220
220
  const wtBase = mkdtempSync(join(tmpdir(), 'gsd-wt-back-wt-'));
@@ -232,27 +232,26 @@ describe('worktree-sync-milestones', async () => {
232
232
  // Main project root starts with only the milestone directory (no slices yet)
233
233
  mkdirSync(join(mainBase, '.gsd', 'milestones', 'M002'), { recursive: true });
234
234
 
235
- // Pass M001 as milestoneId (the one being merged/skipped), M002 should still sync
236
235
  const { synced } = syncWorktreeStateBack(mainBase, wtBase, 'M001');
237
236
 
238
237
  const mainSliceDir = join(mainBase, '.gsd', 'milestones', 'M002', 'slices', 'S01');
239
238
  const mainTasksDir = join(mainSliceDir, 'tasks');
240
239
 
241
240
  assert.ok(
242
- existsSync(join(mainSliceDir, 'S01-SUMMARY.md')),
243
- '#1678: slice SUMMARY synced to project root',
241
+ !existsSync(join(mainSliceDir, 'S01-SUMMARY.md')),
242
+ 'slice SUMMARY projection is not copied to project root',
244
243
  );
245
244
  assert.ok(
246
- existsSync(join(mainTasksDir, 'T01-SUMMARY.md')),
247
- '#1678: task T01-SUMMARY synced to project root',
245
+ !existsSync(join(mainTasksDir, 'T01-SUMMARY.md')),
246
+ 'task T01-SUMMARY projection is not copied to project root',
248
247
  );
249
248
  assert.ok(
250
- existsSync(join(mainTasksDir, 'T02-SUMMARY.md')),
251
- '#1678: task T02-SUMMARY synced to project root',
249
+ !existsSync(join(mainTasksDir, 'T02-SUMMARY.md')),
250
+ 'task T02-SUMMARY projection is not copied to project root',
252
251
  );
253
252
  assert.ok(
254
- synced.some((p) => p.includes('tasks/T01-SUMMARY.md')),
255
- '#1678: task summary appears in synced list',
253
+ !synced.some((p) => p.includes('tasks/T01-SUMMARY.md')),
254
+ 'task summary does not appear in synced list',
256
255
  );
257
256
  } finally {
258
257
  rmSync(mainBase, { recursive: true, force: true });
@@ -260,8 +259,8 @@ describe('worktree-sync-milestones', async () => {
260
259
  }
261
260
  }
262
261
 
263
- // ─── 9. syncWorktreeStateBack syncs root-level .gsd/ files ──────────
264
- console.log('\n=== 9. syncWorktreeStateBack syncs root-level files (REQUIREMENTS, PROJECT) ===');
262
+ // ─── 9. syncWorktreeStateBack does not import root-level state projections ──────────
263
+ console.log('\n=== 9. syncWorktreeStateBack leaves root-level state projections authoritative ===');
265
264
  {
266
265
  const mainBase = mkdtempSync(join(tmpdir(), 'gsd-wt-back-root-main-'));
267
266
  const wtBase = mkdtempSync(join(tmpdir(), 'gsd-wt-back-root-wt-'));
@@ -281,31 +280,31 @@ describe('worktree-sync-milestones', async () => {
281
280
 
282
281
  const { synced } = syncWorktreeStateBack(mainBase, wtBase, 'M001');
283
282
 
284
- // Root-level files should be overwritten with worktree versions
283
+ // Root-level state projections must not be overwritten with worktree versions.
285
284
  const reqContent = readFileSync(join(mainBase, '.gsd', 'REQUIREMENTS.md'), 'utf-8');
286
285
  assert.ok(
287
- reqContent.includes('R002'),
288
- 'REQUIREMENTS.md updated with worktree content',
286
+ !reqContent.includes('R002'),
287
+ 'REQUIREMENTS.md ignores worktree projection content',
289
288
  );
290
289
 
291
290
  const projContent = readFileSync(join(mainBase, '.gsd', 'PROJECT.md'), 'utf-8');
292
291
  assert.ok(
293
- projContent.includes('M002'),
294
- 'PROJECT.md updated with worktree content',
292
+ !projContent.includes('M002'),
293
+ 'PROJECT.md ignores worktree projection content',
295
294
  );
296
295
 
297
296
  assert.ok(
298
- existsSync(join(mainBase, '.gsd', 'KNOWLEDGE.md')),
299
- 'KNOWLEDGE.md synced from worktree',
297
+ !existsSync(join(mainBase, '.gsd', 'KNOWLEDGE.md')),
298
+ 'KNOWLEDGE.md is not copied back from worktree',
300
299
  );
301
300
 
302
301
  assert.ok(
303
- synced.includes('REQUIREMENTS.md'),
304
- 'REQUIREMENTS.md appears in synced list',
302
+ !synced.includes('REQUIREMENTS.md'),
303
+ 'REQUIREMENTS.md does not appear in synced list',
305
304
  );
306
305
  assert.ok(
307
- synced.includes('PROJECT.md'),
308
- 'PROJECT.md appears in synced list',
306
+ !synced.includes('PROJECT.md'),
307
+ 'PROJECT.md does not appear in synced list',
309
308
  );
310
309
  } finally {
311
310
  rmSync(mainBase, { recursive: true, force: true });
@@ -313,8 +312,8 @@ describe('worktree-sync-milestones', async () => {
313
312
  }
314
313
  }
315
314
 
316
- // ─── 10. syncWorktreeStateBack syncs ALL milestone directories ─────
317
- console.log('\n=== 10. syncWorktreeStateBack syncs all milestone dirs, not just current ===');
315
+ // ─── 10. syncWorktreeStateBack does not copy milestone directories ─────
316
+ console.log('\n=== 10. syncWorktreeStateBack does not copy milestone dirs ===');
318
317
  {
319
318
  const mainBase = mkdtempSync(join(tmpdir(), 'gsd-wt-back-all-main-'));
320
319
  const wtBase = mkdtempSync(join(tmpdir(), 'gsd-wt-back-all-wt-'));
@@ -352,19 +351,19 @@ describe('worktree-sync-milestones', async () => {
352
351
  'M001 SUMMARY NOT synced (current milestone skipped to prevent merge conflicts)',
353
352
  );
354
353
 
355
- // M002 should be synced (other milestone not skipped)
354
+ // M002 should not be synced either; worktree projections are not authoritative.
356
355
  assert.ok(
357
- existsSync(join(mainBase, '.gsd', 'milestones', 'M002-abc123', 'M002-abc123-CONTEXT.md')),
358
- 'M002 CONTEXT synced to main (next-milestone fix)',
356
+ !existsSync(join(mainBase, '.gsd', 'milestones', 'M002-abc123', 'M002-abc123-CONTEXT.md')),
357
+ 'M002 CONTEXT projection is not copied to main',
359
358
  );
360
359
  assert.ok(
361
- existsSync(join(mainBase, '.gsd', 'milestones', 'M002-abc123', 'M002-abc123-ROADMAP.md')),
362
- 'M002 ROADMAP synced to main (next-milestone fix)',
360
+ !existsSync(join(mainBase, '.gsd', 'milestones', 'M002-abc123', 'M002-abc123-ROADMAP.md')),
361
+ 'M002 ROADMAP projection is not copied to main',
363
362
  );
364
363
 
365
364
  assert.ok(
366
- synced.some((p) => p.includes('M002-abc123')),
367
- 'M002 appears in synced list',
365
+ !synced.some((p) => p.includes('M002-abc123')),
366
+ 'M002 does not appear in synced list',
368
367
  );
369
368
  } finally {
370
369
  rmSync(mainBase, { recursive: true, force: true });
@@ -373,7 +372,7 @@ describe('worktree-sync-milestones', async () => {
373
372
  }
374
373
 
375
374
  // ─── 11. Full M006→M007 transition scenario ───────────────────────────
376
- console.log('\n=== 11. complete-milestone creates next-milestone artifacts that survive sync ===');
375
+ console.log('\n=== 11. complete-milestone worktree projections do not overwrite project root ===');
377
376
  {
378
377
  const mainBase = mkdtempSync(join(tmpdir(), 'gsd-wt-transition-main-'));
379
378
  const wtBase = mkdtempSync(join(tmpdir(), 'gsd-wt-transition-wt-'));
@@ -419,27 +418,27 @@ describe('worktree-sync-milestones', async () => {
419
418
  'M006 SUMMARY NOT synced (current milestone skipped)',
420
419
  );
421
420
 
422
- // Verify M007 artifacts synced (the critical fix — other milestones still sync)
421
+ // Verify M007 worktree projections are not copied back.
423
422
  assert.ok(
424
- existsSync(join(mainBase, '.gsd', 'milestones', 'M007-wortc8', 'M007-wortc8-CONTEXT.md')),
425
- 'M007 CONTEXT synced to main (next-milestone)',
423
+ !existsSync(join(mainBase, '.gsd', 'milestones', 'M007-wortc8', 'M007-wortc8-CONTEXT.md')),
424
+ 'M007 CONTEXT projection is not copied to main',
426
425
  );
427
426
  assert.ok(
428
- existsSync(join(mainBase, '.gsd', 'milestones', 'M007-wortc8', 'M007-wortc8-ROADMAP.md')),
429
- 'M007 ROADMAP synced to main (next-milestone)',
427
+ !existsSync(join(mainBase, '.gsd', 'milestones', 'M007-wortc8', 'M007-wortc8-ROADMAP.md')),
428
+ 'M007 ROADMAP projection is not copied to main',
430
429
  );
431
430
 
432
- // Verify root-level files updated
431
+ // Verify root-level projections remain project-root authoritative.
433
432
  const reqContent = readFileSync(join(mainBase, '.gsd', 'REQUIREMENTS.md'), 'utf-8');
434
433
  assert.ok(
435
- reqContent.includes('R090'),
436
- 'REQUIREMENTS.md has R090 from worktree',
434
+ !reqContent.includes('R090'),
435
+ 'REQUIREMENTS.md ignores worktree projection updates',
437
436
  );
438
437
 
439
438
  const projContent = readFileSync(join(mainBase, '.gsd', 'PROJECT.md'), 'utf-8');
440
439
  assert.ok(
441
- projContent.includes('M007'),
442
- 'PROJECT.md has M007 from worktree',
440
+ !projContent.includes('M007'),
441
+ 'PROJECT.md ignores worktree projection updates',
443
442
  );
444
443
  } finally {
445
444
  rmSync(mainBase, { recursive: true, force: true });
@@ -478,8 +477,8 @@ describe('worktree-sync-milestones', async () => {
478
477
  }
479
478
  }
480
479
 
481
- // ─── 13. syncWorktreeStateBack syncs QUEUE.md and completed-units.json (#1787) ──
482
- console.log('\n=== 13. QUEUE.md and completed-units.json synced from worktree (#1787) ===');
480
+ // ─── 13. syncWorktreeStateBack skips QUEUE.md but preserves completed-units diagnostics ──
481
+ console.log('\n=== 13. QUEUE.md skipped; completed-units.json diagnostic synced ===');
483
482
  {
484
483
  const mainBase = mkdtempSync(join(tmpdir(), 'gsd-wt-back-queue-main-'));
485
484
  const wtBase = mkdtempSync(join(tmpdir(), 'gsd-wt-back-queue-wt-'));
@@ -488,7 +487,7 @@ describe('worktree-sync-milestones', async () => {
488
487
  mkdirSync(join(mainBase, '.gsd', 'milestones', 'M001'), { recursive: true });
489
488
  mkdirSync(join(wtBase, '.gsd', 'milestones', 'M001'), { recursive: true });
490
489
 
491
- // Worktree has QUEUE.md and completed-units.json written during milestone closeout
490
+ // Worktree has QUEUE.md projection and completed-units.json diagnostic.
492
491
  writeFileSync(join(wtBase, '.gsd', 'QUEUE.md'), '# Queue\n- M002 next');
493
492
  writeFileSync(
494
493
  join(wtBase, '.gsd', 'completed-units.json'),
@@ -507,22 +506,17 @@ describe('worktree-sync-milestones', async () => {
507
506
 
508
507
  const { synced } = syncWorktreeStateBack(mainBase, wtBase, 'M001');
509
508
 
510
- // QUEUE.md should be synced
511
- assert.ok(
512
- existsSync(join(mainBase, '.gsd', 'QUEUE.md')),
513
- '#1787: QUEUE.md synced from worktree to main',
514
- );
515
- const queueContent = readFileSync(join(mainBase, '.gsd', 'QUEUE.md'), 'utf-8');
509
+ // QUEUE.md is state/projection content and should not be copied back.
516
510
  assert.ok(
517
- queueContent.includes('M002 next'),
518
- '#1787: QUEUE.md has correct content',
511
+ !existsSync(join(mainBase, '.gsd', 'QUEUE.md')),
512
+ 'QUEUE.md is not synced from worktree to main',
519
513
  );
520
514
  assert.ok(
521
- synced.includes('QUEUE.md'),
522
- '#1787: QUEUE.md appears in synced list',
515
+ !synced.includes('QUEUE.md'),
516
+ 'QUEUE.md does not appear in synced list',
523
517
  );
524
518
 
525
- // completed-units.json should be synced
519
+ // completed-units.json is diagnostic and may be copied for operator visibility.
526
520
  assert.ok(
527
521
  existsSync(join(mainBase, '.gsd', 'completed-units.json')),
528
522
  '#1787: completed-units.json synced from worktree to main',
@@ -578,8 +572,8 @@ describe('worktree-sync-milestones', async () => {
578
572
  }
579
573
  }
580
574
 
581
- // ─── 15. syncWorktreeStateBack syncs non-standard milestone dir names (#1547) ──
582
- console.log('\n=== 15. syncWorktreeStateBack syncs non-standard milestone dir names (#1547) ===');
575
+ // ─── 15. syncWorktreeStateBack skips non-standard milestone dir names ──
576
+ console.log('\n=== 15. syncWorktreeStateBack skips non-standard milestone dir names ===');
583
577
  {
584
578
  const mainBase = mkdtempSync(join(tmpdir(), 'gsd-wt-back-custom-main-'));
585
579
  const wtBase = mkdtempSync(join(tmpdir(), 'gsd-wt-back-custom-wt-'));
@@ -601,12 +595,12 @@ describe('worktree-sync-milestones', async () => {
601
595
  const { synced } = syncWorktreeStateBack(mainBase, wtBase, 'M001');
602
596
 
603
597
  assert.ok(
604
- existsSync(join(mainBase, '.gsd', 'milestones', 'sprint-beta', 'SUMMARY.md')),
605
- '#1547: non-standard milestone dir "sprint-beta" synced back to main',
598
+ !existsSync(join(mainBase, '.gsd', 'milestones', 'sprint-beta', 'SUMMARY.md')),
599
+ 'non-standard milestone projection is not copied back to main',
606
600
  );
607
601
  assert.ok(
608
- synced.some((p) => p.includes('sprint-beta')),
609
- '#1547: sprint-beta appears in synced list',
602
+ !synced.some((p) => p.includes('sprint-beta')),
603
+ 'sprint-beta does not appear in synced list',
610
604
  );
611
605
  } finally {
612
606
  rmSync(mainBase, { recursive: true, force: true });