gsd-pi 2.44.0-dev.62b5d6c → 2.44.0-dev.73f2fd5

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 (326) hide show
  1. package/README.md +30 -12
  2. package/dist/resources/extensions/gsd/auto/infra-errors.js +3 -0
  3. package/dist/resources/extensions/gsd/auto/phases.js +36 -36
  4. package/dist/resources/extensions/gsd/auto-prompts.js +24 -1
  5. package/dist/resources/extensions/gsd/auto-start.js +10 -0
  6. package/dist/resources/extensions/gsd/auto-timers.js +57 -3
  7. package/dist/resources/extensions/gsd/auto-worktree-sync.js +4 -0
  8. package/dist/resources/extensions/gsd/auto-worktree.js +9 -6
  9. package/dist/resources/extensions/gsd/auto.js +30 -3
  10. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +156 -0
  11. package/dist/resources/extensions/gsd/bootstrap/system-context.js +46 -12
  12. package/dist/resources/extensions/gsd/commands/catalog.js +6 -1
  13. package/dist/resources/extensions/gsd/commands/handlers/core.js +1 -0
  14. package/dist/resources/extensions/gsd/commands/handlers/ops.js +5 -0
  15. package/dist/resources/extensions/gsd/commands/handlers/workflow.js +5 -0
  16. package/dist/resources/extensions/gsd/commands-mcp-status.js +187 -0
  17. package/dist/resources/extensions/gsd/db-writer.js +34 -16
  18. package/dist/resources/extensions/gsd/doctor.js +8 -0
  19. package/dist/resources/extensions/gsd/git-service.js +8 -3
  20. package/dist/resources/extensions/gsd/gsd-db.js +12 -1
  21. package/dist/resources/extensions/gsd/markdown-renderer.js +1 -1
  22. package/dist/resources/extensions/gsd/preferences.js +9 -1
  23. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +2 -4
  24. package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  25. package/dist/resources/extensions/gsd/prompts/reassess-roadmap.md +6 -6
  26. package/dist/resources/extensions/gsd/prompts/replan-slice.md +3 -14
  27. package/dist/resources/extensions/gsd/prompts/validate-milestone.md +7 -37
  28. package/dist/resources/extensions/gsd/provider-error-pause.js +7 -0
  29. package/dist/resources/extensions/gsd/state.js +19 -2
  30. package/dist/resources/extensions/gsd/tools/plan-slice.js +1 -0
  31. package/dist/resources/extensions/gsd/tools/plan-task.js +1 -0
  32. package/dist/resources/extensions/gsd/tools/replan-slice.js +2 -0
  33. package/dist/resources/extensions/gsd/tools/validate-milestone.js +88 -0
  34. package/dist/resources/extensions/gsd/worktree-resolver.js +6 -0
  35. package/dist/resources/extensions/mcp-client/index.js +14 -0
  36. package/dist/web/standalone/.next/BUILD_ID +1 -1
  37. package/dist/web/standalone/.next/app-path-routes-manifest.json +17 -17
  38. package/dist/web/standalone/.next/build-manifest.json +2 -2
  39. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  40. package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
  41. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  42. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  43. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  44. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  45. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  46. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  47. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  48. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  49. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  50. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  51. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  52. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  53. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  54. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  55. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  56. package/dist/web/standalone/.next/server/app/index.html +1 -1
  57. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  58. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  59. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  60. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  61. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  62. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  63. package/dist/web/standalone/.next/server/app-paths-manifest.json +17 -17
  64. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  65. package/dist/web/standalone/.next/server/pages/500.html +2 -2
  66. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  67. package/package.json +1 -1
  68. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts +3 -1
  69. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts.map +1 -1
  70. package/packages/pi-coding-agent/dist/core/auth-storage.js +15 -1
  71. package/packages/pi-coding-agent/dist/core/auth-storage.js.map +1 -1
  72. package/packages/pi-coding-agent/dist/core/auth-storage.test.js +6 -8
  73. package/packages/pi-coding-agent/dist/core/auth-storage.test.js.map +1 -1
  74. package/packages/pi-coding-agent/dist/core/extensions/runner.test.js +24 -26
  75. package/packages/pi-coding-agent/dist/core/extensions/runner.test.js.map +1 -1
  76. package/packages/pi-coding-agent/dist/core/fs-utils.test.js +29 -48
  77. package/packages/pi-coding-agent/dist/core/fs-utils.test.js.map +1 -1
  78. package/packages/pi-coding-agent/dist/core/local-model-check.d.ts +15 -0
  79. package/packages/pi-coding-agent/dist/core/local-model-check.d.ts.map +1 -0
  80. package/packages/pi-coding-agent/dist/core/local-model-check.js +41 -0
  81. package/packages/pi-coding-agent/dist/core/local-model-check.js.map +1 -0
  82. package/packages/pi-coding-agent/dist/core/model-registry.d.ts +11 -0
  83. package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
  84. package/packages/pi-coding-agent/dist/core/model-registry.js +20 -1
  85. package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
  86. package/packages/pi-coding-agent/dist/core/resolve-config-value.test.js +34 -44
  87. package/packages/pi-coding-agent/dist/core/resolve-config-value.test.js.map +1 -1
  88. package/packages/pi-coding-agent/dist/core/session-manager.test.js +30 -34
  89. package/packages/pi-coding-agent/dist/core/session-manager.test.js.map +1 -1
  90. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +3 -0
  91. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
  92. package/packages/pi-coding-agent/dist/core/settings-manager.js +6 -0
  93. package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
  94. package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.js +10 -12
  95. package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.js.map +1 -1
  96. package/packages/pi-coding-agent/dist/main.d.ts.map +1 -1
  97. package/packages/pi-coding-agent/dist/main.js +17 -0
  98. package/packages/pi-coding-agent/dist/main.js.map +1 -1
  99. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.d.ts +2 -0
  100. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.d.ts.map +1 -0
  101. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.js +32 -0
  102. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.js.map +1 -0
  103. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts +3 -1
  104. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  105. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js +8 -1
  106. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js.map +1 -1
  107. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts +2 -0
  108. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  109. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js +12 -0
  110. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js.map +1 -1
  111. package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.d.ts +15 -0
  112. package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.d.ts.map +1 -0
  113. package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.js +40 -0
  114. package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.js.map +1 -0
  115. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  116. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +4 -1
  117. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
  118. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.d.ts +5 -2
  119. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.d.ts.map +1 -1
  120. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.js +13 -2
  121. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.js.map +1 -1
  122. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  123. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +17 -8
  124. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  125. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  126. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +7 -3
  127. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  128. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.test.js +43 -47
  129. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.test.js.map +1 -1
  130. package/packages/pi-coding-agent/src/core/auth-storage.test.ts +7 -7
  131. package/packages/pi-coding-agent/src/core/auth-storage.ts +15 -1
  132. package/packages/pi-coding-agent/src/core/extensions/runner.test.ts +26 -26
  133. package/packages/pi-coding-agent/src/core/fs-utils.test.ts +31 -43
  134. package/packages/pi-coding-agent/src/core/local-model-check.ts +45 -0
  135. package/packages/pi-coding-agent/src/core/model-registry.ts +21 -1
  136. package/packages/pi-coding-agent/src/core/resolve-config-value.test.ts +40 -45
  137. package/packages/pi-coding-agent/src/core/session-manager.test.ts +33 -33
  138. package/packages/pi-coding-agent/src/core/settings-manager.ts +9 -0
  139. package/packages/pi-coding-agent/src/core/tools/edit-diff.test.ts +17 -17
  140. package/packages/pi-coding-agent/src/main.ts +19 -0
  141. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/timestamp.test.ts +38 -0
  142. package/packages/pi-coding-agent/src/modes/interactive/components/assistant-message.ts +10 -0
  143. package/packages/pi-coding-agent/src/modes/interactive/components/settings-selector.ts +15 -0
  144. package/packages/pi-coding-agent/src/modes/interactive/components/timestamp.ts +48 -0
  145. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +3 -1
  146. package/packages/pi-coding-agent/src/modes/interactive/components/user-message.ts +18 -3
  147. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +16 -7
  148. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +8 -1
  149. package/packages/pi-coding-agent/src/resources/extensions/memory/storage.test.ts +74 -74
  150. package/src/resources/extensions/gsd/auto/infra-errors.ts +3 -0
  151. package/src/resources/extensions/gsd/auto/phases.ts +45 -48
  152. package/src/resources/extensions/gsd/auto-prompts.ts +24 -1
  153. package/src/resources/extensions/gsd/auto-start.ts +14 -0
  154. package/src/resources/extensions/gsd/auto-timers.ts +64 -3
  155. package/src/resources/extensions/gsd/auto-worktree-sync.ts +5 -0
  156. package/src/resources/extensions/gsd/auto-worktree.ts +9 -6
  157. package/src/resources/extensions/gsd/auto.ts +37 -3
  158. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +148 -0
  159. package/src/resources/extensions/gsd/bootstrap/system-context.ts +48 -11
  160. package/src/resources/extensions/gsd/commands/catalog.ts +6 -1
  161. package/src/resources/extensions/gsd/commands/handlers/core.ts +1 -0
  162. package/src/resources/extensions/gsd/commands/handlers/ops.ts +5 -0
  163. package/src/resources/extensions/gsd/commands/handlers/workflow.ts +8 -0
  164. package/src/resources/extensions/gsd/commands-mcp-status.ts +247 -0
  165. package/src/resources/extensions/gsd/db-writer.ts +39 -17
  166. package/src/resources/extensions/gsd/doctor.ts +7 -1
  167. package/src/resources/extensions/gsd/git-service.ts +6 -2
  168. package/src/resources/extensions/gsd/gsd-db.ts +16 -1
  169. package/src/resources/extensions/gsd/markdown-renderer.ts +1 -1
  170. package/src/resources/extensions/gsd/preferences.ts +11 -1
  171. package/src/resources/extensions/gsd/prompts/complete-milestone.md +2 -4
  172. package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  173. package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +6 -6
  174. package/src/resources/extensions/gsd/prompts/replan-slice.md +3 -14
  175. package/src/resources/extensions/gsd/prompts/validate-milestone.md +7 -37
  176. package/src/resources/extensions/gsd/provider-error-pause.ts +9 -0
  177. package/src/resources/extensions/gsd/state.ts +19 -1
  178. package/src/resources/extensions/gsd/tests/all-milestones-complete-merge.test.ts +99 -99
  179. package/src/resources/extensions/gsd/tests/auto-lock-creation.test.ts +14 -16
  180. package/src/resources/extensions/gsd/tests/auto-paused-session-validation.test.ts +43 -57
  181. package/src/resources/extensions/gsd/tests/auto-pr-bugs.test.ts +88 -0
  182. package/src/resources/extensions/gsd/tests/auto-preflight.test.ts +11 -13
  183. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +465 -523
  184. package/src/resources/extensions/gsd/tests/auto-secrets-gate.test.ts +73 -75
  185. package/src/resources/extensions/gsd/tests/auto-start-needs-discussion.test.ts +34 -56
  186. package/src/resources/extensions/gsd/tests/auto-worktree-milestone-merge.test.ts +533 -656
  187. package/src/resources/extensions/gsd/tests/auto-worktree.test.ts +165 -143
  188. package/src/resources/extensions/gsd/tests/cache-staleness-regression.test.ts +29 -52
  189. package/src/resources/extensions/gsd/tests/captures.test.ts +148 -176
  190. package/src/resources/extensions/gsd/tests/claude-import-tui.test.ts +32 -33
  191. package/src/resources/extensions/gsd/tests/collect-from-manifest.test.ts +141 -143
  192. package/src/resources/extensions/gsd/tests/commands-inspect-open-db.test.ts +25 -25
  193. package/src/resources/extensions/gsd/tests/commands-logs.test.ts +81 -81
  194. package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +38 -59
  195. package/src/resources/extensions/gsd/tests/complete-slice.test.ts +228 -263
  196. package/src/resources/extensions/gsd/tests/complete-task.test.ts +250 -302
  197. package/src/resources/extensions/gsd/tests/completed-units-metrics-sync.test.ts +114 -0
  198. package/src/resources/extensions/gsd/tests/context-store.test.ts +354 -367
  199. package/src/resources/extensions/gsd/tests/continue-here.test.ts +68 -72
  200. package/src/resources/extensions/gsd/tests/cost-projection.test.ts +92 -106
  201. package/src/resources/extensions/gsd/tests/crash-recovery.test.ts +27 -35
  202. package/src/resources/extensions/gsd/tests/dashboard-budget.test.ts +220 -237
  203. package/src/resources/extensions/gsd/tests/db-writer.test.ts +465 -416
  204. package/src/resources/extensions/gsd/tests/definition-loader.test.ts +76 -92
  205. package/src/resources/extensions/gsd/tests/derive-state-crossval.test.ts +68 -83
  206. package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +210 -181
  207. package/src/resources/extensions/gsd/tests/derive-state-deps.test.ts +78 -101
  208. package/src/resources/extensions/gsd/tests/derive-state.test.ts +192 -227
  209. package/src/resources/extensions/gsd/tests/detection.test.ts +232 -278
  210. package/src/resources/extensions/gsd/tests/dev-engine-wrapper.test.ts +30 -34
  211. package/src/resources/extensions/gsd/tests/dispatch-guard.test.ts +164 -180
  212. package/src/resources/extensions/gsd/tests/dispatch-missing-task-plans.test.ts +43 -49
  213. package/src/resources/extensions/gsd/tests/dispatch-uat-last-completed.test.ts +28 -32
  214. package/src/resources/extensions/gsd/tests/doctor-completion-deferral.test.ts +27 -29
  215. package/src/resources/extensions/gsd/tests/doctor-delimiter-fix.test.ts +34 -38
  216. package/src/resources/extensions/gsd/tests/doctor-enhancements.test.ts +54 -75
  217. package/src/resources/extensions/gsd/tests/doctor-environment-worktree.test.ts +21 -32
  218. package/src/resources/extensions/gsd/tests/doctor-environment.test.ts +72 -97
  219. package/src/resources/extensions/gsd/tests/doctor-fixlevel.test.ts +38 -44
  220. package/src/resources/extensions/gsd/tests/doctor-git.test.ts +104 -145
  221. package/src/resources/extensions/gsd/tests/doctor-proactive.test.ts +84 -106
  222. package/src/resources/extensions/gsd/tests/doctor-roadmap-summary-atomicity.test.ts +54 -60
  223. package/src/resources/extensions/gsd/tests/doctor-runtime.test.ts +72 -93
  224. package/src/resources/extensions/gsd/tests/doctor.test.ts +104 -134
  225. package/src/resources/extensions/gsd/tests/ensure-db-open.test.ts +123 -131
  226. package/src/resources/extensions/gsd/tests/est-annotation-timeout.test.ts +120 -0
  227. package/src/resources/extensions/gsd/tests/exit-command.test.ts +20 -24
  228. package/src/resources/extensions/gsd/tests/feature-branch-lifecycle-integration.test.ts +48 -57
  229. package/src/resources/extensions/gsd/tests/files-loadfile-eisdir.test.ts +5 -7
  230. package/src/resources/extensions/gsd/tests/flag-file-db.test.ts +30 -42
  231. package/src/resources/extensions/gsd/tests/freeform-decisions.test.ts +198 -206
  232. package/src/resources/extensions/gsd/tests/git-locale.test.ts +13 -27
  233. package/src/resources/extensions/gsd/tests/git-service.test.ts +285 -388
  234. package/src/resources/extensions/gsd/tests/gitignore-tracked-gsd.test.ts +31 -39
  235. package/src/resources/extensions/gsd/tests/graph-operations.test.ts +63 -69
  236. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +255 -264
  237. package/src/resources/extensions/gsd/tests/gsd-inspect.test.ts +108 -119
  238. package/src/resources/extensions/gsd/tests/gsd-recover.test.ts +81 -103
  239. package/src/resources/extensions/gsd/tests/gsd-tools.test.ts +229 -262
  240. package/src/resources/extensions/gsd/tests/headless-answers.test.ts +13 -13
  241. package/src/resources/extensions/gsd/tests/health-widget.test.ts +29 -37
  242. package/src/resources/extensions/gsd/tests/idle-recovery.test.ts +81 -102
  243. package/src/resources/extensions/gsd/tests/infra-error.test.ts +20 -2
  244. package/src/resources/extensions/gsd/tests/init-wizard.test.ts +16 -18
  245. package/src/resources/extensions/gsd/tests/integration-edge.test.ts +41 -46
  246. package/src/resources/extensions/gsd/tests/integration-lifecycle.test.ts +42 -53
  247. package/src/resources/extensions/gsd/tests/integration-mixed-milestones.test.ts +75 -91
  248. package/src/resources/extensions/gsd/tests/integration-proof.test.ts +18 -18
  249. package/src/resources/extensions/gsd/tests/knowledge.test.ts +89 -0
  250. package/src/resources/extensions/gsd/tests/markdown-renderer.test.ts +150 -194
  251. package/src/resources/extensions/gsd/tests/mcp-status.test.ts +103 -0
  252. package/src/resources/extensions/gsd/tests/md-importer.test.ts +101 -125
  253. package/src/resources/extensions/gsd/tests/memory-extractor.test.ts +45 -54
  254. package/src/resources/extensions/gsd/tests/memory-store.test.ts +80 -93
  255. package/src/resources/extensions/gsd/tests/merge-conflict-stops-loop.test.ts +66 -0
  256. package/src/resources/extensions/gsd/tests/migrate-command.test.ts +57 -66
  257. package/src/resources/extensions/gsd/tests/migrate-hierarchy.test.ts +83 -93
  258. package/src/resources/extensions/gsd/tests/migrate-parser.test.ts +161 -170
  259. package/src/resources/extensions/gsd/tests/migrate-transformer.test.ts +125 -141
  260. package/src/resources/extensions/gsd/tests/migrate-validator-parsers.test.ts +107 -131
  261. package/src/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +87 -96
  262. package/src/resources/extensions/gsd/tests/migrate-writer.test.ts +125 -164
  263. package/src/resources/extensions/gsd/tests/must-have-parser.test.ts +81 -94
  264. package/src/resources/extensions/gsd/tests/none-mode-gates.test.ts +35 -36
  265. package/src/resources/extensions/gsd/tests/overrides.test.ts +99 -106
  266. package/src/resources/extensions/gsd/tests/parallel-crash-recovery.test.ts +40 -47
  267. package/src/resources/extensions/gsd/tests/parallel-worker-monitoring.test.ts +25 -28
  268. package/src/resources/extensions/gsd/tests/parallel-workers-multi-milestone-e2e.test.ts +66 -83
  269. package/src/resources/extensions/gsd/tests/park-edge-cases.test.ts +54 -77
  270. package/src/resources/extensions/gsd/tests/park-milestone.test.ts +68 -115
  271. package/src/resources/extensions/gsd/tests/parsers.test.ts +546 -611
  272. package/src/resources/extensions/gsd/tests/paths.test.ts +72 -87
  273. package/src/resources/extensions/gsd/tests/post-unit-hooks.test.ts +77 -117
  274. package/src/resources/extensions/gsd/tests/preferences.test.ts +27 -0
  275. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +11 -7
  276. package/src/resources/extensions/gsd/tests/prompt-db.test.ts +56 -56
  277. package/src/resources/extensions/gsd/tests/queue-draft-detection.test.ts +93 -119
  278. package/src/resources/extensions/gsd/tests/queue-order.test.ts +70 -82
  279. package/src/resources/extensions/gsd/tests/queue-reorder-e2e.test.ts +42 -55
  280. package/src/resources/extensions/gsd/tests/quick-auto-guard.test.ts +100 -0
  281. package/src/resources/extensions/gsd/tests/quick-branch-lifecycle.test.ts +45 -73
  282. package/src/resources/extensions/gsd/tests/reassess-prompt.test.ts +28 -38
  283. package/src/resources/extensions/gsd/tests/replan-slice.test.ts +73 -80
  284. package/src/resources/extensions/gsd/tests/repo-identity-worktree.test.ts +71 -74
  285. package/src/resources/extensions/gsd/tests/requirements.test.ts +70 -75
  286. package/src/resources/extensions/gsd/tests/retry-state-reset.test.ts +44 -66
  287. package/src/resources/extensions/gsd/tests/roadmap-parse-regression.test.ts +114 -181
  288. package/src/resources/extensions/gsd/tests/rule-registry.test.ts +63 -65
  289. package/src/resources/extensions/gsd/tests/run-uat.test.ts +66 -128
  290. package/src/resources/extensions/gsd/tests/session-lock-multipath.test.ts +18 -25
  291. package/src/resources/extensions/gsd/tests/session-lock-regression.test.ts +37 -44
  292. package/src/resources/extensions/gsd/tests/shared-wal.test.ts +19 -26
  293. package/src/resources/extensions/gsd/tests/sqlite-unavailable-gate.test.ts +63 -0
  294. package/src/resources/extensions/gsd/tests/stalled-tool-recovery.test.ts +6 -8
  295. package/src/resources/extensions/gsd/tests/stop-auto-merge-back.test.ts +67 -0
  296. package/src/resources/extensions/gsd/tests/symlink-numbered-variants.test.ts +22 -28
  297. package/src/resources/extensions/gsd/tests/terminated-transient.test.ts +49 -0
  298. package/src/resources/extensions/gsd/tests/token-savings.test.ts +54 -56
  299. package/src/resources/extensions/gsd/tests/tool-call-loop-guard.test.ts +23 -25
  300. package/src/resources/extensions/gsd/tests/tool-naming.test.ts +10 -11
  301. package/src/resources/extensions/gsd/tests/unique-milestone-ids.test.ts +66 -82
  302. package/src/resources/extensions/gsd/tests/unit-runtime.test.ts +46 -47
  303. package/src/resources/extensions/gsd/tests/visualizer-critical-path.test.ts +20 -22
  304. package/src/resources/extensions/gsd/tests/visualizer-data.test.ts +84 -86
  305. package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +41 -43
  306. package/src/resources/extensions/gsd/tests/visualizer-views.test.ts +94 -96
  307. package/src/resources/extensions/gsd/tests/windows-path-normalization.test.ts +11 -13
  308. package/src/resources/extensions/gsd/tests/worker-registry.test.ts +27 -29
  309. package/src/resources/extensions/gsd/tests/workflow-templates.test.ts +50 -52
  310. package/src/resources/extensions/gsd/tests/worktree-bugfix.test.ts +10 -13
  311. package/src/resources/extensions/gsd/tests/worktree-db-integration.test.ts +14 -18
  312. package/src/resources/extensions/gsd/tests/worktree-db.test.ts +38 -39
  313. package/src/resources/extensions/gsd/tests/worktree-e2e.test.ts +17 -21
  314. package/src/resources/extensions/gsd/tests/worktree-health.test.ts +25 -30
  315. package/src/resources/extensions/gsd/tests/worktree-integration.test.ts +30 -37
  316. package/src/resources/extensions/gsd/tests/worktree-symlink-removal.test.ts +15 -22
  317. package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +59 -66
  318. package/src/resources/extensions/gsd/tests/worktree.test.ts +44 -50
  319. package/src/resources/extensions/gsd/tools/plan-slice.ts +2 -0
  320. package/src/resources/extensions/gsd/tools/plan-task.ts +2 -0
  321. package/src/resources/extensions/gsd/tools/replan-slice.ts +3 -0
  322. package/src/resources/extensions/gsd/tools/validate-milestone.ts +127 -0
  323. package/src/resources/extensions/gsd/worktree-resolver.ts +7 -0
  324. package/src/resources/extensions/mcp-client/index.ts +20 -0
  325. /package/dist/web/standalone/.next/static/{fOnWQBjWXMKUs4bqTg530 → kxxAA66bah_yhPYqLBHE2}/_buildManifest.js +0 -0
  326. /package/dist/web/standalone/.next/static/{fOnWQBjWXMKUs4bqTg530 → kxxAA66bah_yhPYqLBHE2}/_ssgManifest.js +0 -0
@@ -50,14 +50,14 @@ If all criteria have at least one remaining owning slice, the coverage check pas
50
50
 
51
51
  **If the roadmap is still good:**
52
52
 
53
- Write `{{assessmentPath}}` with a brief confirmation that roadmap coverage still holds after {{completedSliceId}}. If requirements exist, explicitly note whether requirement coverage remains sound. If `gsd_reassess_roadmap` is available, use it with `verdict: "roadmap-confirmed"`, an empty `sliceChanges` object, and the assessment text — the tool writes the assessment to the DB and renders ASSESSMENT.md.
53
+ Use `gsd_reassess_roadmap` with `verdict: "roadmap-confirmed"`, an empty `sliceChanges` object, and the assessment text — the tool writes the assessment to the DB and renders `{{assessmentPath}}`. If requirements exist, explicitly note whether requirement coverage remains sound.
54
54
 
55
55
  **If changes are needed:**
56
56
 
57
- 1. **Persist changes through `gsd_reassess_roadmap`.** Pass: `milestoneId`, `completedSliceId`, `verdict` (e.g. "roadmap-adjusted"), `assessment` (text explaining the decision), and `sliceChanges` with `modified` (array of sliceId, title, risk, depends, demo), `added` (same shape), `removed` (array of slice ID strings). The tool structurally enforces preservation of completed slices, writes the assessment to the DB, re-renders ROADMAP.md, and renders ASSESSMENT.md. Skip step 2 when this tool succeeds.
58
- 2. **Degraded fallback — direct file writes:** If `gsd_reassess_roadmap` is not available, rewrite the remaining (unchecked) slices in `{{roadmapPath}}` directly. Keep completed slices exactly as they are (`[x]`). Update the boundary map for changed slices. Update the proof strategy if risks changed. Update requirement coverage if ownership or scope changed.
59
- 3. Write `{{assessmentPath}}` explaining what changed and why keep it brief and concrete.
60
- 4. If `.gsd/REQUIREMENTS.md` exists and requirement ownership or status changed, update it.
61
- 5. {{commitInstruction}}
57
+ **Persist changes through `gsd_reassess_roadmap`.** Pass: `milestoneId`, `completedSliceId`, `verdict` (e.g. "roadmap-adjusted"), `assessment` (text explaining the decision), and `sliceChanges` with `modified` (array of sliceId, title, risk, depends, demo), `added` (same shape), `removed` (array of slice ID strings). The tool structurally enforces preservation of completed slices, writes the assessment to the DB, re-renders `{{roadmapPath}}`, and renders `{{assessmentPath}}`.
58
+
59
+ If `.gsd/REQUIREMENTS.md` exists and requirement ownership or status changed, update it.
60
+
61
+ {{commitInstruction}}
62
62
 
63
63
  When done, say: "Roadmap reassessed."
@@ -32,19 +32,8 @@ Consider these captures when rewriting the remaining tasks — they represent th
32
32
 
33
33
  1. Read the blocker task summary carefully. Understand exactly what was discovered and why it blocks the current plan.
34
34
  2. Analyze the remaining `[ ]` tasks in the slice plan. Determine which are still valid, which need modification, and which should be replaced.
35
- 3. **Persist replan state through `gsd_replan_slice`.** Call it with the following parameters: `milestoneId`, `sliceId`, `blockerTaskId`, `blockerDescription`, `whatChanged`, `updatedTasks` (array of task objects with taskId, title, description, estimate, files, verify, inputs, expectedOutput), `removedTaskIds` (array of task ID strings). The tool structurally enforces preservation of completed tasks, writes replan history to the DB, re-renders PLAN.md, and renders REPLAN.md. Skip steps 4–5 when this tool succeeds.
36
- 4. **Degraded fallback direct file writes:** If `gsd_replan_slice` is not available, fall back to writing files directly. Write `{{replanPath}}` documenting:
37
- - What blocker was discovered and in which task
38
- - What changed in the plan and why
39
- - Which incomplete tasks were modified, added, or removed
40
- - Any new risks or considerations introduced by the replan
41
- 5. If using the degraded fallback, rewrite `{{planPath}}` with the updated slice plan:
42
- - Keep all `[x]` tasks exactly as they were (same IDs, same descriptions, same checkmarks)
43
- - Update the `[ ]` tasks to address the blocker
44
- - Ensure the slice Goal and Demo sections are still achievable with the new tasks, or update them if the blocker fundamentally changes what the slice can deliver
45
- - Update the Files Likely Touched section if the replan changes which files are affected
46
- - If a DB-backed planning tool exists for this phase, use it as the source of truth and make any rewritten `PLAN.md` reflect that persisted state rather than bypassing it
47
- 6. If any incomplete task had a `T0x-PLAN.md`, remove or rewrite it to match the new task description.
48
- 7. Do not commit manually — the system auto-commits your changes after this unit completes.
35
+ 3. **Persist replan state through `gsd_replan_slice`.** Call it with: `milestoneId`, `sliceId`, `blockerTaskId`, `blockerDescription`, `whatChanged`, `updatedTasks` (array of task objects with taskId, title, description, estimate, files, verify, inputs, expectedOutput), `removedTaskIds` (array of task ID strings). The tool structurally enforces preservation of completed tasks, writes replan history to the DB, re-renders `{{planPath}}`, and renders `{{replanPath}}`.
36
+ 4. If any incomplete task had a `T0x-PLAN.md`, remove or rewrite it to match the new task description.
37
+ 5. Do not commit manually the system auto-commits your changes after this unit completes.
49
38
 
50
39
  When done, say: "Slice {{sliceId}} replanned."
@@ -16,6 +16,8 @@ All relevant context has been preloaded below — the roadmap, all slice summari
16
16
 
17
17
  {{inlinedContext}}
18
18
 
19
+ {{skillActivation}}
20
+
19
21
  ## Validation Steps
20
22
 
21
23
  1. For each **success criterion** in `{{roadmapPath}}`, check whether slice summaries and UAT results provide evidence that it was met. Record pass/fail per criterion.
@@ -25,47 +27,15 @@ All relevant context has been preloaded below — the roadmap, all slice summari
25
27
  5. Determine a verdict:
26
28
  - `pass` — all criteria met, all slices delivered, no gaps
27
29
  - `needs-attention` — minor gaps that do not block completion (document them)
28
- - `needs-remediation` — material gaps found; add remediation slices to the roadmap
29
-
30
- ## Output
31
-
32
- Write `{{validationPath}}` with this structure:
33
-
34
- ```markdown
35
- ---
36
- verdict: <pass|needs-attention|needs-remediation>
37
- remediation_round: {{remediationRound}}
38
- ---
39
-
40
- # Milestone Validation: {{milestoneId}}
30
+ - `needs-remediation` — material gaps found; remediation slices must be added to the roadmap
41
31
 
42
- ## Success Criteria Checklist
43
- - [x] Criterion 1 — evidence: ...
44
- - [ ] Criterion 2 — gap: ...
32
+ ## Persist Validation
45
33
 
46
- ## Slice Delivery Audit
47
- | Slice | Claimed | Delivered | Status |
48
- |-------|---------|-----------|--------|
49
- | S01 | ... | ... | pass |
50
-
51
- ## Cross-Slice Integration
52
- (any boundary mismatches)
53
-
54
- ## Requirement Coverage
55
- (any unaddressed requirements)
56
-
57
- ## Verdict Rationale
58
- (why this verdict was chosen)
59
-
60
- ## Remediation Plan
61
- (only if verdict is needs-remediation — list new slices to add to the roadmap)
62
- ```
34
+ **Persist validation results through `gsd_validate_milestone`.** Call it with: `milestoneId`, `verdict`, `remediationRound`, `successCriteriaChecklist`, `sliceDeliveryAudit`, `crossSliceIntegration`, `requirementCoverage`, `verdictRationale`, and `remediationPlan` (if verdict is `needs-remediation`). The tool writes the validation to the DB and renders VALIDATION.md to disk.
63
35
 
64
36
  If verdict is `needs-remediation`:
65
- - Add new slices to `{{roadmapPath}}` with unchecked `[ ]` status
66
- - These slices will be planned and executed before validation re-runs
67
-
68
- **You MUST write `{{validationPath}}` before finishing.**
37
+ - After calling `gsd_validate_milestone`, use `gsd_reassess_roadmap` to add remediation slices. Pass `milestoneId`, a synthetic `completedSliceId` (e.g. "VALIDATION"), `verdict: "roadmap-adjusted"`, `assessment` text, and `sliceChanges` with the new slices in the `added` array. The tool persists the changes to the DB and re-renders ROADMAP.md.
38
+ - These remediation slices will be planned and executed before validation re-runs.
69
39
 
70
40
  **File system safety:** When scanning milestone directories for evidence, use `ls` or `find` to list directory contents first — never pass a directory path (e.g. `tasks/`, `slices/`) directly to the `read` tool. The `read` tool only accepts file paths, not directories.
71
41
 
@@ -19,6 +19,11 @@ export function classifyProviderError(errorMsg: string): {
19
19
  const isRateLimit = /rate.?limit|too many requests|429/i.test(errorMsg);
20
20
  const isServerError = /internal server error|500|502|503|overloaded|server_error|api_error|service.?unavailable/i.test(errorMsg);
21
21
 
22
+ // Connection/process errors — transient, auto-resume after brief backoff (#2309).
23
+ // These indicate the process was killed, the connection was reset, or a network
24
+ // blip occurred. They are NOT permanent failures.
25
+ const isConnectionError = /terminated|connection.?reset|connection.?refused|other side closed|fetch failed|network.?(?:is\s+)?unavailable|ECONNREFUSED|ECONNRESET|EPIPE/i.test(errorMsg);
26
+
22
27
  // Permanent errors — never auto-resume
23
28
  const isPermanent = /auth|unauthorized|forbidden|invalid.*key|invalid.*api|billing|quota exceeded|account/i.test(errorMsg);
24
29
 
@@ -37,6 +42,10 @@ export function classifyProviderError(errorMsg: string): {
37
42
  return { isTransient: true, isRateLimit: false, suggestedDelayMs: 30_000 }; // 30s for server errors
38
43
  }
39
44
 
45
+ if (isConnectionError) {
46
+ return { isTransient: true, isRateLimit: false, suggestedDelayMs: 15_000 }; // 15s for connection errors
47
+ }
48
+
40
49
  // Unknown error — treat as permanent (user reviews)
41
50
  return { isTransient: false, isRateLimit: false, suggestedDelayMs: 0 };
42
51
  }
@@ -48,6 +48,7 @@ import {
48
48
  getSliceTasks,
49
49
  getReplanHistory,
50
50
  getSlice,
51
+ insertMilestone,
51
52
  type MilestoneRow,
52
53
  type SliceRow,
53
54
  type TaskRow,
@@ -257,7 +258,24 @@ function isStatusDone(status: string): boolean {
257
258
  export async function deriveStateFromDb(basePath: string): Promise<GSDState> {
258
259
  const requirements = parseRequirementCounts(await loadFile(resolveGsdRootFile(basePath, "REQUIREMENTS")));
259
260
 
260
- const allMilestones = getAllMilestones();
261
+ let allMilestones = getAllMilestones();
262
+
263
+ // Incremental disk→DB sync: milestone directories created outside the DB
264
+ // write path (via /gsd queue, manual mkdir, or complete-milestone writing the
265
+ // next CONTEXT.md) are never inserted by the initial migration guard in
266
+ // auto-start.ts because that guard only runs when gsd.db doesn't exist yet.
267
+ // Reconcile here so deriveStateFromDb never silently misses queued milestones.
268
+ // insertMilestone uses INSERT OR IGNORE, so this is safe to call every time.
269
+ const dbIdSet = new Set(allMilestones.map(m => m.id));
270
+ const diskIds = findMilestoneIds(basePath);
271
+ let synced = false;
272
+ for (const diskId of diskIds) {
273
+ if (!dbIdSet.has(diskId) && !isGhostMilestone(basePath, diskId)) {
274
+ insertMilestone({ id: diskId, status: 'active' });
275
+ synced = true;
276
+ }
277
+ }
278
+ if (synced) allMilestones = getAllMilestones();
261
279
 
262
280
  // Parallel worker isolation: when locked, filter to just the locked milestone
263
281
  const milestoneLock = process.env.GSD_MILESTONE_LOCK;
@@ -130,119 +130,119 @@ test("auto-loop 'all milestones complete' path merges before stopping (#962)", (
130
130
 
131
131
  // ─── Integration: single milestone completes → merged to main ────────────────
132
132
 
133
- test("single milestone worktree is merged to main when all complete (#962)", () => {
133
+ test("single milestone worktree is merged to main when all complete (#962)", (t) => {
134
134
  const savedCwd = process.cwd();
135
135
  let tempDir = "";
136
136
 
137
- try {
138
- tempDir = createTempRepo();
139
-
140
- // Set up a single milestone
141
- createMilestoneArtifacts(tempDir, "M001");
142
- run("git add .", tempDir);
143
- run('git commit -m "add milestone"', tempDir);
144
-
145
- // Create worktree and simulate work
146
- const wt = createAutoWorktree(tempDir, "M001");
147
- assert.ok(isInAutoWorktree(tempDir), "should be in auto-worktree");
148
-
149
- writeFileSync(join(wt, "feature.ts"), "export const feature = true;\n");
150
- run("git add .", wt);
151
- run('git commit -m "feat(M001): add feature"', wt);
152
-
153
- // Simulate the fix: merge before stopping (what the "all complete" path now does)
154
- const roadmapPath = join(
155
- tempDir,
156
- ".gsd",
157
- "milestones",
158
- "M001",
159
- "M001-ROADMAP.md",
160
- );
161
- const roadmapContent = readFileSync(roadmapPath, "utf-8");
162
- const mergeResult = mergeMilestoneToMain(tempDir, "M001", roadmapContent);
163
-
164
- // Verify work is on main
165
- assert.ok(
166
- existsSync(join(tempDir, "feature.ts")),
167
- "feature.ts should be on main after merge",
168
- );
169
- assert.equal(process.cwd(), tempDir, "cwd restored to project root");
170
- assert.ok(!isInAutoWorktree(tempDir), "no longer in auto-worktree");
171
- assert.equal(getAutoWorktreeOriginalBase(), null, "originalBase cleared");
172
-
173
- // Verify milestone branch was cleaned up
174
- const branches = run("git branch", tempDir);
175
- assert.ok(
176
- !branches.includes("milestone/M001"),
177
- "milestone branch should be deleted",
178
- );
179
-
180
- // Verify squash commit on main
181
- const log = run("git log --oneline -3", tempDir);
182
- assert.ok(
183
- log.includes("M001"),
184
- "squash commit on main should reference M001",
185
- );
186
-
187
- assert.ok(mergeResult.commitMessage.length > 0, "commit message returned");
188
- } finally {
137
+ t.after(() => {
189
138
  process.chdir(savedCwd);
190
139
  if (tempDir && existsSync(tempDir)) {
191
- rmSync(tempDir, { recursive: true, force: true });
140
+ rmSync(tempDir, { recursive: true, force: true });
192
141
  }
193
- }
142
+ });
143
+
144
+ tempDir = createTempRepo();
145
+
146
+ // Set up a single milestone
147
+ createMilestoneArtifacts(tempDir, "M001");
148
+ run("git add .", tempDir);
149
+ run('git commit -m "add milestone"', tempDir);
150
+
151
+ // Create worktree and simulate work
152
+ const wt = createAutoWorktree(tempDir, "M001");
153
+ assert.ok(isInAutoWorktree(tempDir), "should be in auto-worktree");
154
+
155
+ writeFileSync(join(wt, "feature.ts"), "export const feature = true;\n");
156
+ run("git add .", wt);
157
+ run('git commit -m "feat(M001): add feature"', wt);
158
+
159
+ // Simulate the fix: merge before stopping (what the "all complete" path now does)
160
+ const roadmapPath = join(
161
+ tempDir,
162
+ ".gsd",
163
+ "milestones",
164
+ "M001",
165
+ "M001-ROADMAP.md",
166
+ );
167
+ const roadmapContent = readFileSync(roadmapPath, "utf-8");
168
+ const mergeResult = mergeMilestoneToMain(tempDir, "M001", roadmapContent);
169
+
170
+ // Verify work is on main
171
+ assert.ok(
172
+ existsSync(join(tempDir, "feature.ts")),
173
+ "feature.ts should be on main after merge",
174
+ );
175
+ assert.equal(process.cwd(), tempDir, "cwd restored to project root");
176
+ assert.ok(!isInAutoWorktree(tempDir), "no longer in auto-worktree");
177
+ assert.equal(getAutoWorktreeOriginalBase(), null, "originalBase cleared");
178
+
179
+ // Verify milestone branch was cleaned up
180
+ const branches = run("git branch", tempDir);
181
+ assert.ok(
182
+ !branches.includes("milestone/M001"),
183
+ "milestone branch should be deleted",
184
+ );
185
+
186
+ // Verify squash commit on main
187
+ const log = run("git log --oneline -3", tempDir);
188
+ assert.ok(
189
+ log.includes("M001"),
190
+ "squash commit on main should reference M001",
191
+ );
192
+
193
+ assert.ok(mergeResult.commitMessage.length > 0, "commit message returned");
194
194
  });
195
195
 
196
196
  // ─── Integration: last of multiple milestones completes → merged ─────────────
197
197
 
198
- test("last milestone worktree is merged when it's the final one (#962)", () => {
198
+ test("last milestone worktree is merged when it's the final one (#962)", (t) => {
199
199
  const savedCwd = process.cwd();
200
200
  let tempDir = "";
201
201
 
202
- try {
203
- tempDir = createTempRepo();
204
-
205
- // Set up two milestones
206
- createMilestoneArtifacts(tempDir, "M001");
207
- createMilestoneArtifacts(tempDir, "M002");
208
- run("git add .", tempDir);
209
- run('git commit -m "add milestones"', tempDir);
210
-
211
- // Complete M001 first (merge it)
212
- const wt1 = createAutoWorktree(tempDir, "M001");
213
- writeFileSync(join(wt1, "m001-work.ts"), "export const m001 = true;\n");
214
- run("git add .", wt1);
215
- run('git commit -m "feat(M001): m001 work"', wt1);
216
- const roadmap1 = readFileSync(
217
- join(tempDir, ".gsd", "milestones", "M001", "M001-ROADMAP.md"),
218
- "utf-8",
219
- );
220
- mergeMilestoneToMain(tempDir, "M001", roadmap1);
221
-
222
- // Now complete M002 (the LAST milestone — this is the #962 scenario)
223
- const wt2 = createAutoWorktree(tempDir, "M002");
224
- writeFileSync(join(wt2, "m002-work.ts"), "export const m002 = true;\n");
225
- run("git add .", wt2);
226
- run('git commit -m "feat(M002): m002 work"', wt2);
227
- const roadmap2 = readFileSync(
228
- join(tempDir, ".gsd", "milestones", "M002", "M002-ROADMAP.md"),
229
- "utf-8",
230
- );
231
- mergeMilestoneToMain(tempDir, "M002", roadmap2);
232
-
233
- // Both features should now be on main
234
- assert.ok(existsSync(join(tempDir, "m001-work.ts")), "M001 work on main");
235
- assert.ok(existsSync(join(tempDir, "m002-work.ts")), "M002 work on main");
236
- assert.ok(!isInAutoWorktree(tempDir), "not in worktree after final merge");
237
-
238
- // Both milestone branches should be cleaned up
239
- const branches = run("git branch", tempDir);
240
- assert.ok(!branches.includes("milestone/M001"), "M001 branch deleted");
241
- assert.ok(!branches.includes("milestone/M002"), "M002 branch deleted");
242
- } finally {
202
+ t.after(() => {
243
203
  process.chdir(savedCwd);
244
204
  if (tempDir && existsSync(tempDir)) {
245
- rmSync(tempDir, { recursive: true, force: true });
205
+ rmSync(tempDir, { recursive: true, force: true });
246
206
  }
247
- }
207
+ });
208
+
209
+ tempDir = createTempRepo();
210
+
211
+ // Set up two milestones
212
+ createMilestoneArtifacts(tempDir, "M001");
213
+ createMilestoneArtifacts(tempDir, "M002");
214
+ run("git add .", tempDir);
215
+ run('git commit -m "add milestones"', tempDir);
216
+
217
+ // Complete M001 first (merge it)
218
+ const wt1 = createAutoWorktree(tempDir, "M001");
219
+ writeFileSync(join(wt1, "m001-work.ts"), "export const m001 = true;\n");
220
+ run("git add .", wt1);
221
+ run('git commit -m "feat(M001): m001 work"', wt1);
222
+ const roadmap1 = readFileSync(
223
+ join(tempDir, ".gsd", "milestones", "M001", "M001-ROADMAP.md"),
224
+ "utf-8",
225
+ );
226
+ mergeMilestoneToMain(tempDir, "M001", roadmap1);
227
+
228
+ // Now complete M002 (the LAST milestone — this is the #962 scenario)
229
+ const wt2 = createAutoWorktree(tempDir, "M002");
230
+ writeFileSync(join(wt2, "m002-work.ts"), "export const m002 = true;\n");
231
+ run("git add .", wt2);
232
+ run('git commit -m "feat(M002): m002 work"', wt2);
233
+ const roadmap2 = readFileSync(
234
+ join(tempDir, ".gsd", "milestones", "M002", "M002-ROADMAP.md"),
235
+ "utf-8",
236
+ );
237
+ mergeMilestoneToMain(tempDir, "M002", roadmap2);
238
+
239
+ // Both features should now be on main
240
+ assert.ok(existsSync(join(tempDir, "m001-work.ts")), "M001 work on main");
241
+ assert.ok(existsSync(join(tempDir, "m002-work.ts")), "M002 work on main");
242
+ assert.ok(!isInAutoWorktree(tempDir), "not in worktree after final merge");
243
+
244
+ // Both milestone branches should be cleaned up
245
+ const branches = run("git branch", tempDir);
246
+ assert.ok(!branches.includes("milestone/M001"), "M001 branch deleted");
247
+ assert.ok(!branches.includes("milestone/M002"), "M002 branch deleted");
248
248
  });
@@ -110,26 +110,24 @@ test("clearLock is safe when no lock file exists", () => {
110
110
  rmSync(dir, { recursive: true, force: true });
111
111
  });
112
112
 
113
- test("bootstrap cleanup releases session lock artifacts", () => {
113
+ test("bootstrap cleanup releases session lock artifacts", (t) => {
114
114
  const dir = mkdtempSync(join(tmpdir(), "gsd-lock-test-"));
115
115
  mkdirSync(join(dir, ".gsd"), { recursive: true });
116
116
 
117
- try {
118
- const result = acquireSessionLock(dir);
119
- assert.equal(result.acquired, true, "session lock should be acquired");
120
- assert.ok(existsSync(join(dir, ".gsd", "auto.lock")), "auto.lock should exist while lock is held");
121
- if (properLockfileAvailable) {
122
- assert.ok(existsSync(join(dir, ".gsd.lock")), ".gsd.lock should exist while lock is held");
123
- }
124
-
125
- releaseSessionLock(dir);
126
- clearLock(dir);
127
-
128
- assert.ok(!existsSync(join(dir, ".gsd", "auto.lock")), "auto.lock should be removed by bootstrap cleanup");
129
- assert.ok(!existsSync(join(dir, ".gsd.lock")), ".gsd.lock should be removed by bootstrap cleanup");
130
- } finally {
131
- rmSync(dir, { recursive: true, force: true });
117
+ t.after(() => rmSync(dir, { recursive: true, force: true }));
118
+
119
+ const result = acquireSessionLock(dir);
120
+ assert.equal(result.acquired, true, "session lock should be acquired");
121
+ assert.ok(existsSync(join(dir, ".gsd", "auto.lock")), "auto.lock should exist while lock is held");
122
+ if (properLockfileAvailable) {
123
+ assert.ok(existsSync(join(dir, ".gsd.lock")), ".gsd.lock should exist while lock is held");
132
124
  }
125
+
126
+ releaseSessionLock(dir);
127
+ clearLock(dir);
128
+
129
+ assert.ok(!existsSync(join(dir, ".gsd", "auto.lock")), "auto.lock should be removed by bootstrap cleanup");
130
+ assert.ok(!existsSync(join(dir, ".gsd.lock")), ".gsd.lock should be removed by bootstrap cleanup");
133
131
  });
134
132
 
135
133
  // ─── isLockProcessAlive detects live vs dead PIDs ────────────────────────
@@ -51,93 +51,79 @@ function cleanup(base: string): void {
51
51
  try { rmSync(base, { recursive: true, force: true }); } catch { /* */ }
52
52
  }
53
53
 
54
- test("resolveMilestonePath returns null for missing milestone", () => {
54
+ test("resolveMilestonePath returns null for missing milestone", (t) => {
55
55
  const base = makeTmpBase();
56
56
  mkdirSync(join(base, ".gsd", "milestones"), { recursive: true });
57
- try {
58
- const result = resolveMilestonePath(base, "M999");
59
- assert.equal(result, null, "should return null for non-existent milestone");
60
- } finally {
61
- cleanup(base);
62
- }
57
+ t.after(() => cleanup(base));
58
+
59
+ const result = resolveMilestonePath(base, "M999");
60
+ assert.equal(result, null, "should return null for non-existent milestone");
63
61
  });
64
62
 
65
- test("resolveMilestonePath returns path for existing milestone", () => {
63
+ test("resolveMilestonePath returns path for existing milestone", (t) => {
66
64
  const base = makeTmpBase();
67
65
  mkdirSync(join(base, ".gsd", "milestones", "M001"), { recursive: true });
68
- try {
69
- const result = resolveMilestonePath(base, "M001");
70
- assert.ok(result, "should return a path for existing milestone");
71
- assert.ok(result.includes("M001"), "path should contain the milestone ID");
72
- } finally {
73
- cleanup(base);
74
- }
66
+ t.after(() => cleanup(base));
67
+
68
+ const result = resolveMilestonePath(base, "M001");
69
+ assert.ok(result, "should return a path for existing milestone");
70
+ assert.ok(result.includes("M001"), "path should contain the milestone ID");
75
71
  });
76
72
 
77
- test("resolveMilestoneFile returns null when no SUMMARY exists", () => {
73
+ test("resolveMilestoneFile returns null when no SUMMARY exists", (t) => {
78
74
  const base = makeTmpBase();
79
75
  mkdirSync(join(base, ".gsd", "milestones", "M001"), { recursive: true });
80
- try {
81
- const result = resolveMilestoneFile(base, "M001", "SUMMARY");
82
- assert.equal(result, null, "should return null when no SUMMARY file");
83
- } finally {
84
- cleanup(base);
85
- }
76
+ t.after(() => cleanup(base));
77
+
78
+ const result = resolveMilestoneFile(base, "M001", "SUMMARY");
79
+ assert.equal(result, null, "should return null when no SUMMARY file");
86
80
  });
87
81
 
88
- test("resolveMilestoneFile returns path when SUMMARY exists (completed)", () => {
82
+ test("resolveMilestoneFile returns path when SUMMARY exists (completed)", (t) => {
89
83
  const base = makeTmpBase();
90
84
  const mDir = join(base, ".gsd", "milestones", "M001");
91
85
  mkdirSync(mDir, { recursive: true });
92
86
  writeFileSync(join(mDir, "M001-SUMMARY.md"), "# Summary\nDone.");
93
- try {
94
- const result = resolveMilestoneFile(base, "M001", "SUMMARY");
95
- assert.ok(result, "should return a path when SUMMARY exists");
96
- assert.ok(result.includes("SUMMARY"), "path should reference SUMMARY");
97
- } finally {
98
- cleanup(base);
99
- }
87
+ t.after(() => cleanup(base));
88
+
89
+ const result = resolveMilestoneFile(base, "M001", "SUMMARY");
90
+ assert.ok(result, "should return a path when SUMMARY exists");
91
+ assert.ok(result.includes("SUMMARY"), "path should reference SUMMARY");
100
92
  });
101
93
 
102
94
  // ─── Combined validation logic (mirrors auto.ts resume guard) ───────────────
103
95
 
104
- test("stale milestone: missing dir means paused session should be discarded", () => {
96
+ test("stale milestone: missing dir means paused session should be discarded", (t) => {
105
97
  const base = makeTmpBase();
106
98
  mkdirSync(join(base, ".gsd", "milestones"), { recursive: true });
107
- try {
108
- const mDir = resolveMilestonePath(base, "M999");
109
- const summaryFile = resolveMilestoneFile(base, "M999", "SUMMARY");
110
- const isStale = !mDir || !!summaryFile;
111
- assert.ok(isStale, "milestone that doesn't exist should be detected as stale");
112
- } finally {
113
- cleanup(base);
114
- }
99
+ t.after(() => cleanup(base));
100
+
101
+ const mDir = resolveMilestonePath(base, "M999");
102
+ const summaryFile = resolveMilestoneFile(base, "M999", "SUMMARY");
103
+ const isStale = !mDir || !!summaryFile;
104
+ assert.ok(isStale, "milestone that doesn't exist should be detected as stale");
115
105
  });
116
106
 
117
- test("stale milestone: completed (has SUMMARY) means paused session should be discarded", () => {
107
+ test("stale milestone: completed (has SUMMARY) means paused session should be discarded", (t) => {
118
108
  const base = makeTmpBase();
119
109
  const mDir = join(base, ".gsd", "milestones", "M001");
120
110
  mkdirSync(mDir, { recursive: true });
121
111
  writeFileSync(join(mDir, "M001-SUMMARY.md"), "# Summary\nDone.");
122
- try {
123
- const dir = resolveMilestonePath(base, "M001");
124
- const summaryFile = resolveMilestoneFile(base, "M001", "SUMMARY");
125
- const isStale = !dir || !!summaryFile;
126
- assert.ok(isStale, "milestone with SUMMARY should be detected as stale");
127
- } finally {
128
- cleanup(base);
129
- }
112
+ t.after(() => cleanup(base));
113
+
114
+ const dir = resolveMilestonePath(base, "M001");
115
+ const summaryFile = resolveMilestoneFile(base, "M001", "SUMMARY");
116
+ const isStale = !dir || !!summaryFile;
117
+ assert.ok(isStale, "milestone with SUMMARY should be detected as stale");
130
118
  });
131
119
 
132
- test("valid milestone: exists and has no SUMMARY means paused session is valid", () => {
120
+ test("valid milestone: exists and has no SUMMARY means paused session is valid", (t) => {
133
121
  const base = makeTmpBase();
134
122
  mkdirSync(join(base, ".gsd", "milestones", "M001"), { recursive: true });
135
- try {
136
- const dir = resolveMilestonePath(base, "M001");
137
- const summaryFile = resolveMilestoneFile(base, "M001", "SUMMARY");
138
- const isStale = !dir || !!summaryFile;
139
- assert.ok(!isStale, "active milestone should not be detected as stale");
140
- } finally {
141
- cleanup(base);
142
- }
123
+ t.after(() => cleanup(base));
124
+
125
+ const dir = resolveMilestonePath(base, "M001");
126
+ const summaryFile = resolveMilestoneFile(base, "M001", "SUMMARY");
127
+ const isStale = !dir || !!summaryFile;
128
+ assert.ok(!isStale, "active milestone should not be detected as stale");
143
129
  });