gsd-pi 2.44.0 → 2.45.0-dev.6b9da3e

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 (463) hide show
  1. package/README.md +30 -12
  2. package/dist/resources/extensions/gsd/activity-log.js +7 -0
  3. package/dist/resources/extensions/gsd/auto/infra-errors.js +3 -0
  4. package/dist/resources/extensions/gsd/auto/phases.js +37 -36
  5. package/dist/resources/extensions/gsd/auto-prompts.js +24 -1
  6. package/dist/resources/extensions/gsd/auto-start.js +31 -2
  7. package/dist/resources/extensions/gsd/auto-timers.js +57 -3
  8. package/dist/resources/extensions/gsd/auto-worktree-sync.js +4 -0
  9. package/dist/resources/extensions/gsd/auto-worktree.js +9 -6
  10. package/dist/resources/extensions/gsd/auto.js +30 -3
  11. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +156 -0
  12. package/dist/resources/extensions/gsd/bootstrap/system-context.js +46 -12
  13. package/dist/resources/extensions/gsd/commands/catalog.js +7 -1
  14. package/dist/resources/extensions/gsd/commands/handlers/core.js +2 -0
  15. package/dist/resources/extensions/gsd/commands/handlers/ops.js +10 -0
  16. package/dist/resources/extensions/gsd/commands/handlers/workflow.js +5 -0
  17. package/dist/resources/extensions/gsd/commands-mcp-status.js +187 -0
  18. package/dist/resources/extensions/gsd/db-writer.js +34 -16
  19. package/dist/resources/extensions/gsd/doctor.js +8 -0
  20. package/dist/resources/extensions/gsd/git-service.js +8 -3
  21. package/dist/resources/extensions/gsd/gsd-db.js +12 -1
  22. package/dist/resources/extensions/gsd/markdown-renderer.js +1 -1
  23. package/dist/resources/extensions/gsd/preferences.js +9 -1
  24. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +2 -4
  25. package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  26. package/dist/resources/extensions/gsd/prompts/reassess-roadmap.md +6 -6
  27. package/dist/resources/extensions/gsd/prompts/replan-slice.md +3 -14
  28. package/dist/resources/extensions/gsd/prompts/rethink.md +78 -0
  29. package/dist/resources/extensions/gsd/prompts/validate-milestone.md +7 -37
  30. package/dist/resources/extensions/gsd/provider-error-pause.js +7 -0
  31. package/dist/resources/extensions/gsd/repo-identity.js +45 -7
  32. package/dist/resources/extensions/gsd/rethink.js +115 -0
  33. package/dist/resources/extensions/gsd/state.js +41 -3
  34. package/dist/resources/extensions/gsd/tools/plan-slice.js +1 -0
  35. package/dist/resources/extensions/gsd/tools/plan-task.js +1 -0
  36. package/dist/resources/extensions/gsd/tools/replan-slice.js +2 -0
  37. package/dist/resources/extensions/gsd/tools/validate-milestone.js +88 -0
  38. package/dist/resources/extensions/gsd/worktree-manager.js +32 -2
  39. package/dist/resources/extensions/gsd/worktree-resolver.js +6 -0
  40. package/dist/resources/extensions/mcp-client/index.js +14 -0
  41. package/dist/web/standalone/.next/BUILD_ID +1 -1
  42. package/dist/web/standalone/.next/app-path-routes-manifest.json +8 -8
  43. package/dist/web/standalone/.next/build-manifest.json +4 -4
  44. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  45. package/dist/web/standalone/.next/react-loadable-manifest.json +2 -2
  46. package/dist/web/standalone/.next/required-server-files.json +3 -3
  47. package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
  48. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  49. package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
  50. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  51. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  52. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  53. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  54. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  55. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  56. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  57. package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
  58. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  59. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  60. package/dist/web/standalone/.next/server/app/_not-found.rsc +5 -5
  61. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +5 -5
  62. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  63. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +4 -4
  64. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  65. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  66. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  67. package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
  68. package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
  69. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
  70. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
  71. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
  72. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
  73. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
  74. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
  75. package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
  76. package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
  77. package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
  78. package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
  79. package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
  80. package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
  81. package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
  82. package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
  83. package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
  84. package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
  85. package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
  86. package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
  87. package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
  88. package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
  89. package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
  90. package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
  91. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  92. package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
  93. package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
  94. package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
  95. package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
  96. package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
  97. package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
  98. package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
  99. package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
  100. package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
  101. package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
  102. package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
  103. package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
  104. package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
  105. package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
  106. package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
  107. package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
  108. package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
  109. package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
  110. package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
  111. package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +5 -5
  112. package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
  113. package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
  114. package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
  115. package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
  116. package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
  117. package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
  118. package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
  119. package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
  120. package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
  121. package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
  122. package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
  123. package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
  124. package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
  125. package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
  126. package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
  127. package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
  128. package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
  129. package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
  130. package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
  131. package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +2 -2
  132. package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
  133. package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
  134. package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
  135. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +2 -2
  136. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
  137. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +4 -4
  138. package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
  139. package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
  140. package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
  141. package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
  142. package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
  143. package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
  144. package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
  145. package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
  146. package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
  147. package/dist/web/standalone/.next/server/app/index.html +1 -1
  148. package/dist/web/standalone/.next/server/app/index.rsc +6 -6
  149. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  150. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +6 -6
  151. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  152. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +4 -4
  153. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  154. package/dist/web/standalone/.next/server/app/page.js +2 -2
  155. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  156. package/dist/web/standalone/.next/server/app-paths-manifest.json +8 -8
  157. package/dist/web/standalone/.next/server/chunks/229.js +1 -1
  158. package/dist/web/standalone/.next/server/chunks/471.js +3 -3
  159. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  160. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  161. package/dist/web/standalone/.next/server/middleware.js +2 -2
  162. package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
  163. package/dist/web/standalone/.next/server/next-font-manifest.json +1 -1
  164. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  165. package/dist/web/standalone/.next/server/pages/500.html +2 -2
  166. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  167. package/dist/web/standalone/.next/static/chunks/4024.11ca5c01938e5948.js +9 -0
  168. package/dist/web/standalone/.next/static/chunks/{3721.bf31263de6d5fa46.js → 485.243af25f0cdf50d6.js} +2 -2
  169. package/dist/web/standalone/.next/static/chunks/app/_not-found/{page-2f24283c162b6ab3.js → page-f2a7482d42a5614b.js} +1 -1
  170. package/dist/web/standalone/.next/static/chunks/app/{layout-9ecfd95f343793f0.js → layout-a16c7a7ecdf0c2cf.js} +1 -1
  171. package/dist/web/standalone/.next/static/chunks/app/page-6654a8cca61a3d1c.js +1 -0
  172. package/dist/web/standalone/.next/static/chunks/main-app-fdab67f7802d7832.js +1 -0
  173. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-459824ffb8c323dd.js +1 -0
  174. package/dist/web/standalone/.next/static/chunks/webpack-0a4cd455ec4197d2.js +1 -0
  175. package/dist/web/standalone/.next/static/css/dd4ae3f58ac9b600.css +1 -0
  176. package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
  177. package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
  178. package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
  179. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api.target.mk +14 -14
  180. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_except.target.mk +14 -14
  181. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_maybe.target.mk +14 -14
  182. package/dist/web/standalone/server.js +1 -1
  183. package/package.json +1 -1
  184. package/packages/native/dist/stream-process/index.js +2 -2
  185. package/packages/native/src/__tests__/stream-process.test.mjs +34 -0
  186. package/packages/native/src/stream-process/index.ts +2 -2
  187. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts +3 -1
  188. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts.map +1 -1
  189. package/packages/pi-coding-agent/dist/core/auth-storage.js +15 -1
  190. package/packages/pi-coding-agent/dist/core/auth-storage.js.map +1 -1
  191. package/packages/pi-coding-agent/dist/core/auth-storage.test.js +6 -8
  192. package/packages/pi-coding-agent/dist/core/auth-storage.test.js.map +1 -1
  193. package/packages/pi-coding-agent/dist/core/extensions/runner.test.js +24 -26
  194. package/packages/pi-coding-agent/dist/core/extensions/runner.test.js.map +1 -1
  195. package/packages/pi-coding-agent/dist/core/fs-utils.test.js +29 -48
  196. package/packages/pi-coding-agent/dist/core/fs-utils.test.js.map +1 -1
  197. package/packages/pi-coding-agent/dist/core/local-model-check.d.ts +15 -0
  198. package/packages/pi-coding-agent/dist/core/local-model-check.d.ts.map +1 -0
  199. package/packages/pi-coding-agent/dist/core/local-model-check.js +41 -0
  200. package/packages/pi-coding-agent/dist/core/local-model-check.js.map +1 -0
  201. package/packages/pi-coding-agent/dist/core/model-registry.d.ts +11 -0
  202. package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
  203. package/packages/pi-coding-agent/dist/core/model-registry.js +20 -1
  204. package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
  205. package/packages/pi-coding-agent/dist/core/resolve-config-value.test.js +34 -44
  206. package/packages/pi-coding-agent/dist/core/resolve-config-value.test.js.map +1 -1
  207. package/packages/pi-coding-agent/dist/core/session-manager.test.js +30 -34
  208. package/packages/pi-coding-agent/dist/core/session-manager.test.js.map +1 -1
  209. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +3 -0
  210. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
  211. package/packages/pi-coding-agent/dist/core/settings-manager.js +6 -0
  212. package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
  213. package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.js +10 -12
  214. package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.js.map +1 -1
  215. package/packages/pi-coding-agent/dist/main.d.ts.map +1 -1
  216. package/packages/pi-coding-agent/dist/main.js +17 -0
  217. package/packages/pi-coding-agent/dist/main.js.map +1 -1
  218. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.d.ts +2 -0
  219. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.d.ts.map +1 -0
  220. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.js +32 -0
  221. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.js.map +1 -0
  222. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts +3 -1
  223. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  224. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js +8 -1
  225. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js.map +1 -1
  226. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts +2 -0
  227. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  228. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js +12 -0
  229. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js.map +1 -1
  230. package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.d.ts +15 -0
  231. package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.d.ts.map +1 -0
  232. package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.js +40 -0
  233. package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.js.map +1 -0
  234. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  235. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +4 -1
  236. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
  237. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.d.ts +5 -2
  238. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.d.ts.map +1 -1
  239. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.js +13 -2
  240. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.js.map +1 -1
  241. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  242. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +17 -8
  243. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  244. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  245. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +7 -3
  246. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  247. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.test.js +43 -47
  248. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.test.js.map +1 -1
  249. package/packages/pi-coding-agent/package.json +1 -1
  250. package/packages/pi-coding-agent/src/core/auth-storage.test.ts +7 -7
  251. package/packages/pi-coding-agent/src/core/auth-storage.ts +15 -1
  252. package/packages/pi-coding-agent/src/core/extensions/runner.test.ts +26 -26
  253. package/packages/pi-coding-agent/src/core/fs-utils.test.ts +31 -43
  254. package/packages/pi-coding-agent/src/core/local-model-check.ts +45 -0
  255. package/packages/pi-coding-agent/src/core/model-registry.ts +21 -1
  256. package/packages/pi-coding-agent/src/core/resolve-config-value.test.ts +40 -45
  257. package/packages/pi-coding-agent/src/core/session-manager.test.ts +33 -33
  258. package/packages/pi-coding-agent/src/core/settings-manager.ts +9 -0
  259. package/packages/pi-coding-agent/src/core/tools/edit-diff.test.ts +17 -17
  260. package/packages/pi-coding-agent/src/main.ts +19 -0
  261. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/timestamp.test.ts +38 -0
  262. package/packages/pi-coding-agent/src/modes/interactive/components/assistant-message.ts +10 -0
  263. package/packages/pi-coding-agent/src/modes/interactive/components/settings-selector.ts +15 -0
  264. package/packages/pi-coding-agent/src/modes/interactive/components/timestamp.ts +48 -0
  265. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +3 -1
  266. package/packages/pi-coding-agent/src/modes/interactive/components/user-message.ts +18 -3
  267. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +16 -7
  268. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +8 -1
  269. package/packages/pi-coding-agent/src/resources/extensions/memory/storage.test.ts +74 -74
  270. package/pkg/package.json +1 -1
  271. package/src/resources/extensions/gsd/activity-log.ts +1 -0
  272. package/src/resources/extensions/gsd/auto/infra-errors.ts +3 -0
  273. package/src/resources/extensions/gsd/auto/phases.ts +46 -48
  274. package/src/resources/extensions/gsd/auto-prompts.ts +24 -1
  275. package/src/resources/extensions/gsd/auto-start.ts +39 -2
  276. package/src/resources/extensions/gsd/auto-timers.ts +64 -3
  277. package/src/resources/extensions/gsd/auto-worktree-sync.ts +5 -0
  278. package/src/resources/extensions/gsd/auto-worktree.ts +9 -6
  279. package/src/resources/extensions/gsd/auto.ts +37 -3
  280. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +148 -0
  281. package/src/resources/extensions/gsd/bootstrap/system-context.ts +48 -11
  282. package/src/resources/extensions/gsd/commands/catalog.ts +7 -1
  283. package/src/resources/extensions/gsd/commands/handlers/core.ts +2 -0
  284. package/src/resources/extensions/gsd/commands/handlers/ops.ts +10 -0
  285. package/src/resources/extensions/gsd/commands/handlers/workflow.ts +8 -0
  286. package/src/resources/extensions/gsd/commands-mcp-status.ts +247 -0
  287. package/src/resources/extensions/gsd/db-writer.ts +39 -17
  288. package/src/resources/extensions/gsd/doctor.ts +7 -1
  289. package/src/resources/extensions/gsd/git-service.ts +6 -2
  290. package/src/resources/extensions/gsd/gsd-db.ts +16 -1
  291. package/src/resources/extensions/gsd/markdown-renderer.ts +1 -1
  292. package/src/resources/extensions/gsd/preferences.ts +11 -1
  293. package/src/resources/extensions/gsd/prompts/complete-milestone.md +2 -4
  294. package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  295. package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +6 -6
  296. package/src/resources/extensions/gsd/prompts/replan-slice.md +3 -14
  297. package/src/resources/extensions/gsd/prompts/rethink.md +78 -0
  298. package/src/resources/extensions/gsd/prompts/validate-milestone.md +7 -37
  299. package/src/resources/extensions/gsd/provider-error-pause.ts +9 -0
  300. package/src/resources/extensions/gsd/repo-identity.ts +46 -7
  301. package/src/resources/extensions/gsd/rethink.ts +154 -0
  302. package/src/resources/extensions/gsd/state.ts +41 -1
  303. package/src/resources/extensions/gsd/tests/all-milestones-complete-merge.test.ts +99 -99
  304. package/src/resources/extensions/gsd/tests/auto-lock-creation.test.ts +14 -16
  305. package/src/resources/extensions/gsd/tests/auto-paused-session-validation.test.ts +43 -57
  306. package/src/resources/extensions/gsd/tests/auto-pr-bugs.test.ts +88 -0
  307. package/src/resources/extensions/gsd/tests/auto-preflight.test.ts +11 -13
  308. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +465 -523
  309. package/src/resources/extensions/gsd/tests/auto-secrets-gate.test.ts +73 -75
  310. package/src/resources/extensions/gsd/tests/auto-start-needs-discussion.test.ts +34 -56
  311. package/src/resources/extensions/gsd/tests/auto-worktree-milestone-merge.test.ts +533 -656
  312. package/src/resources/extensions/gsd/tests/auto-worktree.test.ts +165 -143
  313. package/src/resources/extensions/gsd/tests/cache-staleness-regression.test.ts +29 -52
  314. package/src/resources/extensions/gsd/tests/captures.test.ts +148 -176
  315. package/src/resources/extensions/gsd/tests/claude-import-tui.test.ts +32 -33
  316. package/src/resources/extensions/gsd/tests/collect-from-manifest.test.ts +141 -143
  317. package/src/resources/extensions/gsd/tests/commands-inspect-open-db.test.ts +25 -25
  318. package/src/resources/extensions/gsd/tests/commands-logs.test.ts +81 -81
  319. package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +38 -59
  320. package/src/resources/extensions/gsd/tests/complete-slice.test.ts +228 -263
  321. package/src/resources/extensions/gsd/tests/complete-task.test.ts +250 -302
  322. package/src/resources/extensions/gsd/tests/completed-units-metrics-sync.test.ts +114 -0
  323. package/src/resources/extensions/gsd/tests/context-store.test.ts +354 -367
  324. package/src/resources/extensions/gsd/tests/continue-here.test.ts +68 -72
  325. package/src/resources/extensions/gsd/tests/cost-projection.test.ts +92 -106
  326. package/src/resources/extensions/gsd/tests/crash-recovery.test.ts +27 -35
  327. package/src/resources/extensions/gsd/tests/dashboard-budget.test.ts +220 -237
  328. package/src/resources/extensions/gsd/tests/db-writer.test.ts +465 -416
  329. package/src/resources/extensions/gsd/tests/definition-loader.test.ts +76 -92
  330. package/src/resources/extensions/gsd/tests/derive-state-crossval.test.ts +68 -83
  331. package/src/resources/extensions/gsd/tests/derive-state-db-disk-reconcile.test.ts +121 -0
  332. package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +210 -181
  333. package/src/resources/extensions/gsd/tests/derive-state-deps.test.ts +78 -101
  334. package/src/resources/extensions/gsd/tests/derive-state.test.ts +192 -227
  335. package/src/resources/extensions/gsd/tests/detection.test.ts +232 -278
  336. package/src/resources/extensions/gsd/tests/dev-engine-wrapper.test.ts +30 -34
  337. package/src/resources/extensions/gsd/tests/dispatch-guard.test.ts +164 -180
  338. package/src/resources/extensions/gsd/tests/dispatch-missing-task-plans.test.ts +43 -49
  339. package/src/resources/extensions/gsd/tests/dispatch-uat-last-completed.test.ts +28 -32
  340. package/src/resources/extensions/gsd/tests/doctor-completion-deferral.test.ts +27 -29
  341. package/src/resources/extensions/gsd/tests/doctor-delimiter-fix.test.ts +34 -38
  342. package/src/resources/extensions/gsd/tests/doctor-enhancements.test.ts +54 -75
  343. package/src/resources/extensions/gsd/tests/doctor-environment-worktree.test.ts +21 -32
  344. package/src/resources/extensions/gsd/tests/doctor-environment.test.ts +72 -97
  345. package/src/resources/extensions/gsd/tests/doctor-fixlevel.test.ts +38 -44
  346. package/src/resources/extensions/gsd/tests/doctor-git.test.ts +104 -145
  347. package/src/resources/extensions/gsd/tests/doctor-proactive.test.ts +84 -106
  348. package/src/resources/extensions/gsd/tests/doctor-roadmap-summary-atomicity.test.ts +54 -60
  349. package/src/resources/extensions/gsd/tests/doctor-runtime.test.ts +72 -93
  350. package/src/resources/extensions/gsd/tests/doctor.test.ts +104 -134
  351. package/src/resources/extensions/gsd/tests/ensure-db-open.test.ts +123 -131
  352. package/src/resources/extensions/gsd/tests/est-annotation-timeout.test.ts +120 -0
  353. package/src/resources/extensions/gsd/tests/exit-command.test.ts +20 -24
  354. package/src/resources/extensions/gsd/tests/feature-branch-lifecycle-integration.test.ts +48 -57
  355. package/src/resources/extensions/gsd/tests/files-loadfile-eisdir.test.ts +5 -7
  356. package/src/resources/extensions/gsd/tests/flag-file-db.test.ts +30 -42
  357. package/src/resources/extensions/gsd/tests/freeform-decisions.test.ts +198 -206
  358. package/src/resources/extensions/gsd/tests/git-locale.test.ts +13 -27
  359. package/src/resources/extensions/gsd/tests/git-service.test.ts +285 -388
  360. package/src/resources/extensions/gsd/tests/gitignore-tracked-gsd.test.ts +31 -39
  361. package/src/resources/extensions/gsd/tests/graph-operations.test.ts +63 -69
  362. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +255 -264
  363. package/src/resources/extensions/gsd/tests/gsd-inspect.test.ts +108 -119
  364. package/src/resources/extensions/gsd/tests/gsd-recover.test.ts +81 -103
  365. package/src/resources/extensions/gsd/tests/gsd-tools.test.ts +229 -262
  366. package/src/resources/extensions/gsd/tests/headless-answers.test.ts +13 -13
  367. package/src/resources/extensions/gsd/tests/health-widget.test.ts +29 -37
  368. package/src/resources/extensions/gsd/tests/idle-recovery.test.ts +81 -102
  369. package/src/resources/extensions/gsd/tests/infra-error.test.ts +20 -2
  370. package/src/resources/extensions/gsd/tests/inherited-repo-home-dir.test.ts +121 -0
  371. package/src/resources/extensions/gsd/tests/init-wizard.test.ts +16 -18
  372. package/src/resources/extensions/gsd/tests/integration-edge.test.ts +41 -46
  373. package/src/resources/extensions/gsd/tests/integration-lifecycle.test.ts +42 -53
  374. package/src/resources/extensions/gsd/tests/integration-mixed-milestones.test.ts +75 -91
  375. package/src/resources/extensions/gsd/tests/integration-proof.test.ts +18 -18
  376. package/src/resources/extensions/gsd/tests/knowledge.test.ts +89 -0
  377. package/src/resources/extensions/gsd/tests/markdown-renderer.test.ts +150 -194
  378. package/src/resources/extensions/gsd/tests/mcp-status.test.ts +103 -0
  379. package/src/resources/extensions/gsd/tests/md-importer.test.ts +101 -125
  380. package/src/resources/extensions/gsd/tests/memory-extractor.test.ts +45 -54
  381. package/src/resources/extensions/gsd/tests/memory-store.test.ts +80 -93
  382. package/src/resources/extensions/gsd/tests/merge-conflict-stops-loop.test.ts +66 -0
  383. package/src/resources/extensions/gsd/tests/migrate-command.test.ts +57 -66
  384. package/src/resources/extensions/gsd/tests/migrate-hierarchy.test.ts +83 -93
  385. package/src/resources/extensions/gsd/tests/migrate-parser.test.ts +161 -170
  386. package/src/resources/extensions/gsd/tests/migrate-transformer.test.ts +125 -141
  387. package/src/resources/extensions/gsd/tests/migrate-validator-parsers.test.ts +107 -131
  388. package/src/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +87 -96
  389. package/src/resources/extensions/gsd/tests/migrate-writer.test.ts +125 -164
  390. package/src/resources/extensions/gsd/tests/must-have-parser.test.ts +81 -94
  391. package/src/resources/extensions/gsd/tests/none-mode-gates.test.ts +35 -36
  392. package/src/resources/extensions/gsd/tests/overrides.test.ts +99 -106
  393. package/src/resources/extensions/gsd/tests/parallel-crash-recovery.test.ts +40 -47
  394. package/src/resources/extensions/gsd/tests/parallel-worker-monitoring.test.ts +25 -28
  395. package/src/resources/extensions/gsd/tests/parallel-workers-multi-milestone-e2e.test.ts +66 -83
  396. package/src/resources/extensions/gsd/tests/park-edge-cases.test.ts +54 -77
  397. package/src/resources/extensions/gsd/tests/park-milestone.test.ts +68 -115
  398. package/src/resources/extensions/gsd/tests/parsers.test.ts +546 -611
  399. package/src/resources/extensions/gsd/tests/paths.test.ts +72 -87
  400. package/src/resources/extensions/gsd/tests/post-unit-hooks.test.ts +77 -117
  401. package/src/resources/extensions/gsd/tests/preferences.test.ts +27 -0
  402. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +11 -7
  403. package/src/resources/extensions/gsd/tests/prompt-db.test.ts +56 -56
  404. package/src/resources/extensions/gsd/tests/queue-draft-detection.test.ts +93 -119
  405. package/src/resources/extensions/gsd/tests/queue-order.test.ts +70 -82
  406. package/src/resources/extensions/gsd/tests/queue-reorder-e2e.test.ts +42 -55
  407. package/src/resources/extensions/gsd/tests/quick-auto-guard.test.ts +100 -0
  408. package/src/resources/extensions/gsd/tests/quick-branch-lifecycle.test.ts +45 -73
  409. package/src/resources/extensions/gsd/tests/reassess-prompt.test.ts +28 -38
  410. package/src/resources/extensions/gsd/tests/recovery-attempts-reset.test.ts +176 -0
  411. package/src/resources/extensions/gsd/tests/replan-slice.test.ts +73 -80
  412. package/src/resources/extensions/gsd/tests/repo-identity-worktree.test.ts +71 -74
  413. package/src/resources/extensions/gsd/tests/requirements.test.ts +70 -75
  414. package/src/resources/extensions/gsd/tests/retry-state-reset.test.ts +44 -66
  415. package/src/resources/extensions/gsd/tests/roadmap-parse-regression.test.ts +114 -181
  416. package/src/resources/extensions/gsd/tests/rule-registry.test.ts +63 -65
  417. package/src/resources/extensions/gsd/tests/run-uat.test.ts +66 -128
  418. package/src/resources/extensions/gsd/tests/session-lock-multipath.test.ts +18 -25
  419. package/src/resources/extensions/gsd/tests/session-lock-regression.test.ts +37 -44
  420. package/src/resources/extensions/gsd/tests/shared-wal.test.ts +19 -26
  421. package/src/resources/extensions/gsd/tests/sqlite-unavailable-gate.test.ts +63 -0
  422. package/src/resources/extensions/gsd/tests/stalled-tool-recovery.test.ts +6 -8
  423. package/src/resources/extensions/gsd/tests/stop-auto-merge-back.test.ts +67 -0
  424. package/src/resources/extensions/gsd/tests/survivor-branch-complete.test.ts +108 -0
  425. package/src/resources/extensions/gsd/tests/symlink-numbered-variants.test.ts +22 -28
  426. package/src/resources/extensions/gsd/tests/terminated-transient.test.ts +49 -0
  427. package/src/resources/extensions/gsd/tests/token-savings.test.ts +54 -56
  428. package/src/resources/extensions/gsd/tests/tool-call-loop-guard.test.ts +23 -25
  429. package/src/resources/extensions/gsd/tests/tool-naming.test.ts +10 -11
  430. package/src/resources/extensions/gsd/tests/unique-milestone-ids.test.ts +66 -82
  431. package/src/resources/extensions/gsd/tests/unit-runtime.test.ts +46 -47
  432. package/src/resources/extensions/gsd/tests/visualizer-critical-path.test.ts +20 -22
  433. package/src/resources/extensions/gsd/tests/visualizer-data.test.ts +84 -86
  434. package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +41 -43
  435. package/src/resources/extensions/gsd/tests/visualizer-views.test.ts +94 -96
  436. package/src/resources/extensions/gsd/tests/windows-path-normalization.test.ts +11 -13
  437. package/src/resources/extensions/gsd/tests/worker-registry.test.ts +27 -29
  438. package/src/resources/extensions/gsd/tests/workflow-templates.test.ts +50 -52
  439. package/src/resources/extensions/gsd/tests/worktree-bugfix.test.ts +10 -13
  440. package/src/resources/extensions/gsd/tests/worktree-db-integration.test.ts +14 -18
  441. package/src/resources/extensions/gsd/tests/worktree-db.test.ts +38 -39
  442. package/src/resources/extensions/gsd/tests/worktree-e2e.test.ts +17 -21
  443. package/src/resources/extensions/gsd/tests/worktree-health.test.ts +25 -30
  444. package/src/resources/extensions/gsd/tests/worktree-integration.test.ts +30 -37
  445. package/src/resources/extensions/gsd/tests/worktree-submodule-safety.test.ts +65 -0
  446. package/src/resources/extensions/gsd/tests/worktree-symlink-removal.test.ts +15 -22
  447. package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +59 -66
  448. package/src/resources/extensions/gsd/tests/worktree.test.ts +44 -50
  449. package/src/resources/extensions/gsd/tools/plan-slice.ts +2 -0
  450. package/src/resources/extensions/gsd/tools/plan-task.ts +2 -0
  451. package/src/resources/extensions/gsd/tools/replan-slice.ts +3 -0
  452. package/src/resources/extensions/gsd/tools/validate-milestone.ts +127 -0
  453. package/src/resources/extensions/gsd/worktree-manager.ts +43 -2
  454. package/src/resources/extensions/gsd/worktree-resolver.ts +7 -0
  455. package/src/resources/extensions/mcp-client/index.ts +20 -0
  456. package/dist/web/standalone/.next/static/chunks/4024.0de81b543b28b9fe.js +0 -9
  457. package/dist/web/standalone/.next/static/chunks/app/page-7e9530a7122506c5.js +0 -1
  458. package/dist/web/standalone/.next/static/chunks/main-app-d3d4c336195465f9.js +0 -1
  459. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-ab5a8926e07ec673.js +0 -1
  460. package/dist/web/standalone/.next/static/chunks/webpack-9014b5adb127a98a.js +0 -1
  461. package/dist/web/standalone/.next/static/css/8a727f372cf53002.css +0 -1
  462. /package/dist/web/standalone/.next/static/{mgkxN0mGP6gSUmGPEzbk_ → rzO54ZboyINyEt7cVM_uS}/_buildManifest.js +0 -0
  463. /package/dist/web/standalone/.next/static/{mgkxN0mGP6gSUmGPEzbk_ → rzO54ZboyINyEt7cVM_uS}/_ssgManifest.js +0 -0
@@ -39,444 +39,400 @@ function cleanup(base: string): void {
39
39
 
40
40
  // ─── resolveExpectedArtifactPath ──────────────────────────────────────────
41
41
 
42
- test("resolveExpectedArtifactPath returns correct path for research-milestone", () => {
42
+ test("resolveExpectedArtifactPath returns correct path for research-milestone", (t) => {
43
43
  const base = makeTmpBase();
44
- try {
45
- const result = resolveExpectedArtifactPath("research-milestone", "M001", base);
46
- assert.ok(result);
47
- assert.ok(result!.includes("M001"));
48
- assert.ok(result!.includes("RESEARCH"));
49
- } finally {
50
- cleanup(base);
51
- }
44
+ t.after(() => cleanup(base));
45
+
46
+ const result = resolveExpectedArtifactPath("research-milestone", "M001", base);
47
+ assert.ok(result);
48
+ assert.ok(result!.includes("M001"));
49
+ assert.ok(result!.includes("RESEARCH"));
52
50
  });
53
51
 
54
- test("resolveExpectedArtifactPath returns correct path for execute-task", () => {
52
+ test("resolveExpectedArtifactPath returns correct path for execute-task", (t) => {
55
53
  const base = makeTmpBase();
56
- try {
57
- const result = resolveExpectedArtifactPath("execute-task", "M001/S01/T01", base);
58
- assert.ok(result);
59
- assert.ok(result!.includes("tasks"));
60
- assert.ok(result!.includes("SUMMARY"));
61
- } finally {
62
- cleanup(base);
63
- }
54
+ t.after(() => cleanup(base));
55
+
56
+ const result = resolveExpectedArtifactPath("execute-task", "M001/S01/T01", base);
57
+ assert.ok(result);
58
+ assert.ok(result!.includes("tasks"));
59
+ assert.ok(result!.includes("SUMMARY"));
64
60
  });
65
61
 
66
- test("resolveExpectedArtifactPath returns correct path for complete-slice", () => {
62
+ test("resolveExpectedArtifactPath returns correct path for complete-slice", (t) => {
67
63
  const base = makeTmpBase();
68
- try {
69
- const result = resolveExpectedArtifactPath("complete-slice", "M001/S01", base);
70
- assert.ok(result);
71
- assert.ok(result!.includes("SUMMARY"));
72
- } finally {
73
- cleanup(base);
74
- }
64
+ t.after(() => cleanup(base));
65
+
66
+ const result = resolveExpectedArtifactPath("complete-slice", "M001/S01", base);
67
+ assert.ok(result);
68
+ assert.ok(result!.includes("SUMMARY"));
75
69
  });
76
70
 
77
- test("resolveExpectedArtifactPath returns correct path for plan-slice", () => {
71
+ test("resolveExpectedArtifactPath returns correct path for plan-slice", (t) => {
78
72
  const base = makeTmpBase();
79
- try {
80
- const result = resolveExpectedArtifactPath("plan-slice", "M001/S01", base);
81
- assert.ok(result);
82
- assert.ok(result!.includes("PLAN"));
83
- } finally {
84
- cleanup(base);
85
- }
73
+ t.after(() => cleanup(base));
74
+
75
+ const result = resolveExpectedArtifactPath("plan-slice", "M001/S01", base);
76
+ assert.ok(result);
77
+ assert.ok(result!.includes("PLAN"));
86
78
  });
87
79
 
88
- test("resolveExpectedArtifactPath returns null for unknown type", () => {
80
+ test("resolveExpectedArtifactPath returns null for unknown type", (t) => {
89
81
  const base = makeTmpBase();
90
- try {
91
- const result = resolveExpectedArtifactPath("unknown-type", "M001", base);
92
- assert.equal(result, null);
93
- } finally {
94
- cleanup(base);
95
- }
82
+ t.after(() => cleanup(base));
83
+
84
+ const result = resolveExpectedArtifactPath("unknown-type", "M001", base);
85
+ assert.equal(result, null);
96
86
  });
97
87
 
98
- test("resolveExpectedArtifactPath returns correct path for all milestone-level types", () => {
88
+ test("resolveExpectedArtifactPath returns correct path for all milestone-level types", (t) => {
99
89
  const base = makeTmpBase();
100
- try {
101
- const planResult = resolveExpectedArtifactPath("plan-milestone", "M001", base);
102
- assert.ok(planResult);
103
- assert.ok(planResult!.includes("ROADMAP"));
90
+ t.after(() => cleanup(base));
104
91
 
105
- const completeResult = resolveExpectedArtifactPath("complete-milestone", "M001", base);
106
- assert.ok(completeResult);
107
- assert.ok(completeResult!.includes("SUMMARY"));
108
- } finally {
109
- cleanup(base);
110
- }
92
+ const planResult = resolveExpectedArtifactPath("plan-milestone", "M001", base);
93
+ assert.ok(planResult);
94
+ assert.ok(planResult!.includes("ROADMAP"));
95
+
96
+ const completeResult = resolveExpectedArtifactPath("complete-milestone", "M001", base);
97
+ assert.ok(completeResult);
98
+ assert.ok(completeResult!.includes("SUMMARY"));
111
99
  });
112
100
 
113
- test("resolveExpectedArtifactPath returns correct path for all slice-level types", () => {
101
+ test("resolveExpectedArtifactPath returns correct path for all slice-level types", (t) => {
114
102
  const base = makeTmpBase();
115
- try {
116
- const researchResult = resolveExpectedArtifactPath("research-slice", "M001/S01", base);
117
- assert.ok(researchResult);
118
- assert.ok(researchResult!.includes("RESEARCH"));
103
+ t.after(() => cleanup(base));
119
104
 
120
- const assessResult = resolveExpectedArtifactPath("reassess-roadmap", "M001/S01", base);
121
- assert.ok(assessResult);
122
- assert.ok(assessResult!.includes("ASSESSMENT"));
105
+ const researchResult = resolveExpectedArtifactPath("research-slice", "M001/S01", base);
106
+ assert.ok(researchResult);
107
+ assert.ok(researchResult!.includes("RESEARCH"));
123
108
 
124
- const uatResult = resolveExpectedArtifactPath("run-uat", "M001/S01", base);
125
- assert.ok(uatResult);
126
- assert.ok(uatResult!.includes("UAT-RESULT"));
127
- } finally {
128
- cleanup(base);
129
- }
109
+ const assessResult = resolveExpectedArtifactPath("reassess-roadmap", "M001/S01", base);
110
+ assert.ok(assessResult);
111
+ assert.ok(assessResult!.includes("ASSESSMENT"));
112
+
113
+ const uatResult = resolveExpectedArtifactPath("run-uat", "M001/S01", base);
114
+ assert.ok(uatResult);
115
+ assert.ok(uatResult!.includes("UAT-RESULT"));
130
116
  });
131
117
 
132
118
  // ─── diagnoseExpectedArtifact ─────────────────────────────────────────────
133
119
 
134
- test("diagnoseExpectedArtifact returns description for known types", () => {
120
+ test("diagnoseExpectedArtifact returns description for known types", (t) => {
135
121
  const base = makeTmpBase();
136
- try {
137
- const research = diagnoseExpectedArtifact("research-milestone", "M001", base);
138
- assert.ok(research);
139
- assert.ok(research!.includes("research"));
122
+ t.after(() => cleanup(base));
140
123
 
141
- const plan = diagnoseExpectedArtifact("plan-slice", "M001/S01", base);
142
- assert.ok(plan);
143
- assert.ok(plan!.includes("plan"));
124
+ const research = diagnoseExpectedArtifact("research-milestone", "M001", base);
125
+ assert.ok(research);
126
+ assert.ok(research!.includes("research"));
144
127
 
145
- const task = diagnoseExpectedArtifact("execute-task", "M001/S01/T01", base);
146
- assert.ok(task);
147
- assert.ok(task!.includes("T01"));
148
- } finally {
149
- cleanup(base);
150
- }
128
+ const plan = diagnoseExpectedArtifact("plan-slice", "M001/S01", base);
129
+ assert.ok(plan);
130
+ assert.ok(plan!.includes("plan"));
131
+
132
+ const task = diagnoseExpectedArtifact("execute-task", "M001/S01/T01", base);
133
+ assert.ok(task);
134
+ assert.ok(task!.includes("T01"));
151
135
  });
152
136
 
153
- test("diagnoseExpectedArtifact returns null for unknown type", () => {
137
+ test("diagnoseExpectedArtifact returns null for unknown type", (t) => {
154
138
  const base = makeTmpBase();
155
- try {
156
- assert.equal(diagnoseExpectedArtifact("unknown", "M001", base), null);
157
- } finally {
158
- cleanup(base);
159
- }
139
+ t.after(() => cleanup(base));
140
+
141
+ assert.equal(diagnoseExpectedArtifact("unknown", "M001", base), null);
160
142
  });
161
143
 
162
144
  // ─── buildLoopRemediationSteps ────────────────────────────────────────────
163
145
 
164
- test("buildLoopRemediationSteps returns steps for execute-task", () => {
146
+ test("buildLoopRemediationSteps returns steps for execute-task", (t) => {
165
147
  const base = makeTmpBase();
166
- try {
167
- const steps = buildLoopRemediationSteps("execute-task", "M001/S01/T01", base);
168
- assert.ok(steps);
169
- assert.ok(steps!.includes("T01"));
170
- assert.ok(steps!.includes("gsd undo-task"));
171
- } finally {
172
- cleanup(base);
173
- }
148
+ t.after(() => cleanup(base));
149
+
150
+ const steps = buildLoopRemediationSteps("execute-task", "M001/S01/T01", base);
151
+ assert.ok(steps);
152
+ assert.ok(steps!.includes("T01"));
153
+ assert.ok(steps!.includes("gsd undo-task"));
174
154
  });
175
155
 
176
- test("buildLoopRemediationSteps returns steps for plan-slice", () => {
156
+ test("buildLoopRemediationSteps returns steps for plan-slice", (t) => {
177
157
  const base = makeTmpBase();
178
- try {
179
- const steps = buildLoopRemediationSteps("plan-slice", "M001/S01", base);
180
- assert.ok(steps);
181
- assert.ok(steps!.includes("PLAN"));
182
- assert.ok(steps!.includes("gsd recover"));
183
- } finally {
184
- cleanup(base);
185
- }
158
+ t.after(() => cleanup(base));
159
+
160
+ const steps = buildLoopRemediationSteps("plan-slice", "M001/S01", base);
161
+ assert.ok(steps);
162
+ assert.ok(steps!.includes("PLAN"));
163
+ assert.ok(steps!.includes("gsd recover"));
186
164
  });
187
165
 
188
- test("buildLoopRemediationSteps returns steps for complete-slice", () => {
166
+ test("buildLoopRemediationSteps returns steps for complete-slice", (t) => {
189
167
  const base = makeTmpBase();
190
- try {
191
- const steps = buildLoopRemediationSteps("complete-slice", "M001/S01", base);
192
- assert.ok(steps);
193
- assert.ok(steps!.includes("S01"));
194
- assert.ok(steps!.includes("gsd reset-slice"));
195
- } finally {
196
- cleanup(base);
197
- }
168
+ t.after(() => cleanup(base));
169
+
170
+ const steps = buildLoopRemediationSteps("complete-slice", "M001/S01", base);
171
+ assert.ok(steps);
172
+ assert.ok(steps!.includes("S01"));
173
+ assert.ok(steps!.includes("gsd reset-slice"));
198
174
  });
199
175
 
200
- test("buildLoopRemediationSteps returns null for unknown type", () => {
176
+ test("buildLoopRemediationSteps returns null for unknown type", (t) => {
201
177
  const base = makeTmpBase();
202
- try {
203
- assert.equal(buildLoopRemediationSteps("unknown", "M001", base), null);
204
- } finally {
205
- cleanup(base);
206
- }
178
+ t.after(() => cleanup(base));
179
+
180
+ assert.equal(buildLoopRemediationSteps("unknown", "M001", base), null);
207
181
  });
208
182
 
209
183
  // ─── verifyExpectedArtifact: parse cache collision regression ─────────────
210
184
 
211
- test("verifyExpectedArtifact detects roadmap [x] change despite parse cache", () => {
185
+ test("verifyExpectedArtifact detects roadmap [x] change despite parse cache", (t) => {
212
186
  // Regression test: cacheKey collision when [ ] → [x] doesn't change
213
187
  // file length or first/last 100 chars. Without the fix, parseRoadmap
214
188
  // returns stale cached data with done=false even though the file has [x].
215
189
  const base = makeTmpBase();
216
- try {
217
- // Build a roadmap long enough that the [x] change is outside the first/last 100 chars
218
- const padding = "A".repeat(200);
219
- const roadmapBefore = [
220
- `# M001: Test Milestone ${padding}`,
221
- "",
222
- "## Slices",
223
- "",
224
- "- [ ] **S01: First slice** `risk:low`",
225
- "",
226
- `## Footer ${padding}`,
227
- ].join("\n");
228
- const roadmapAfter = roadmapBefore.replace("- [ ] **S01:", "- [x] **S01:");
229
-
230
- // Verify lengths are identical (the key collision condition)
231
- assert.equal(roadmapBefore.length, roadmapAfter.length);
232
-
233
- // Populate parse cache with the pre-edit roadmap
234
- const before = parseRoadmap(roadmapBefore);
235
- const sliceBefore = before.slices.find(s => s.id === "S01");
236
- assert.ok(sliceBefore);
237
- assert.equal(sliceBefore!.done, false);
238
-
239
- // Now write the post-edit roadmap to disk and create required artifacts
240
- const roadmapPath = join(base, ".gsd", "milestones", "M001", "M001-ROADMAP.md");
241
- writeFileSync(roadmapPath, roadmapAfter);
242
- const summaryPath = join(base, ".gsd", "milestones", "M001", "slices", "S01", "S01-SUMMARY.md");
243
- writeFileSync(summaryPath, "# Summary\nDone.");
244
- const uatPath = join(base, ".gsd", "milestones", "M001", "slices", "S01", "S01-UAT.md");
245
- writeFileSync(uatPath, "# UAT\nPassed.");
246
-
247
- // verifyExpectedArtifact should see the [x] despite the parse cache
248
- // having the [ ] version. The fix clears the parse cache inside verify.
249
- const verified = verifyExpectedArtifact("complete-slice", "M001/S01", base);
250
- assert.equal(verified, true, "verifyExpectedArtifact should return true when roadmap has [x]");
251
- } finally {
190
+ t.after(() => {
252
191
  clearParseCache();
253
192
  cleanup(base);
254
- }
193
+ });
194
+
195
+ // Build a roadmap long enough that the [x] change is outside the first/last 100 chars
196
+ const padding = "A".repeat(200);
197
+ const roadmapBefore = [
198
+ `# M001: Test Milestone ${padding}`,
199
+ "",
200
+ "## Slices",
201
+ "",
202
+ "- [ ] **S01: First slice** `risk:low`",
203
+ "",
204
+ `## Footer ${padding}`,
205
+ ].join("\n");
206
+ const roadmapAfter = roadmapBefore.replace("- [ ] **S01:", "- [x] **S01:");
207
+
208
+ // Verify lengths are identical (the key collision condition)
209
+ assert.equal(roadmapBefore.length, roadmapAfter.length);
210
+
211
+ // Populate parse cache with the pre-edit roadmap
212
+ const before = parseRoadmap(roadmapBefore);
213
+ const sliceBefore = before.slices.find(s => s.id === "S01");
214
+ assert.ok(sliceBefore);
215
+ assert.equal(sliceBefore!.done, false);
216
+
217
+ // Now write the post-edit roadmap to disk and create required artifacts
218
+ const roadmapPath = join(base, ".gsd", "milestones", "M001", "M001-ROADMAP.md");
219
+ writeFileSync(roadmapPath, roadmapAfter);
220
+ const summaryPath = join(base, ".gsd", "milestones", "M001", "slices", "S01", "S01-SUMMARY.md");
221
+ writeFileSync(summaryPath, "# Summary\nDone.");
222
+ const uatPath = join(base, ".gsd", "milestones", "M001", "slices", "S01", "S01-UAT.md");
223
+ writeFileSync(uatPath, "# UAT\nPassed.");
224
+
225
+ // verifyExpectedArtifact should see the [x] despite the parse cache
226
+ // having the [ ] version. The fix clears the parse cache inside verify.
227
+ const verified = verifyExpectedArtifact("complete-slice", "M001/S01", base);
228
+ assert.equal(verified, true, "verifyExpectedArtifact should return true when roadmap has [x]");
255
229
  });
256
230
 
257
231
  // ─── verifyExpectedArtifact: plan-slice empty scaffold regression (#699) ──
258
232
 
259
- test("verifyExpectedArtifact rejects plan-slice with empty scaffold", () => {
233
+ test("verifyExpectedArtifact rejects plan-slice with empty scaffold", (t) => {
260
234
  const base = makeTmpBase();
261
- try {
262
- const sliceDir = join(base, ".gsd", "milestones", "M001", "slices", "S01");
263
- mkdirSync(sliceDir, { recursive: true });
264
- writeFileSync(join(sliceDir, "S01-PLAN.md"), "# S01: Test Slice\n\n## Tasks\n\n");
265
- assert.strictEqual(
266
- verifyExpectedArtifact("plan-slice", "M001/S01", base),
267
- false,
268
- "Empty scaffold should not be treated as completed artifact",
269
- );
270
- } finally {
271
- cleanup(base);
272
- }
273
- });
235
+ t.after(() => cleanup(base));
274
236
 
275
- test("verifyExpectedArtifact accepts plan-slice with actual tasks", () => {
276
- const base = makeTmpBase();
277
- try {
278
- const sliceDir = join(base, ".gsd", "milestones", "M001", "slices", "S01");
279
- const tasksDir = join(sliceDir, "tasks");
280
- mkdirSync(tasksDir, { recursive: true });
281
- writeFileSync(join(sliceDir, "S01-PLAN.md"), [
282
- "# S01: Test Slice",
283
- "",
284
- "## Tasks",
285
- "",
286
- "- [ ] **T01: Implement feature** `est:2h`",
287
- "- [ ] **T02: Write tests** `est:1h`",
288
- ].join("\n"));
289
- writeFileSync(join(tasksDir, "T01-PLAN.md"), "# T01 Plan");
290
- writeFileSync(join(tasksDir, "T02-PLAN.md"), "# T02 Plan");
291
- assert.strictEqual(
292
- verifyExpectedArtifact("plan-slice", "M001/S01", base),
293
- true,
294
- "Plan with task entries should be treated as completed artifact",
295
- );
296
- } finally {
297
- cleanup(base);
298
- }
237
+ const sliceDir = join(base, ".gsd", "milestones", "M001", "slices", "S01");
238
+ mkdirSync(sliceDir, { recursive: true });
239
+ writeFileSync(join(sliceDir, "S01-PLAN.md"), "# S01: Test Slice\n\n## Tasks\n\n");
240
+ assert.strictEqual(
241
+ verifyExpectedArtifact("plan-slice", "M001/S01", base),
242
+ false,
243
+ "Empty scaffold should not be treated as completed artifact",
244
+ );
299
245
  });
300
246
 
301
- test("verifyExpectedArtifact accepts plan-slice with completed tasks", () => {
247
+ test("verifyExpectedArtifact accepts plan-slice with actual tasks", (t) => {
302
248
  const base = makeTmpBase();
303
- try {
304
- const sliceDir = join(base, ".gsd", "milestones", "M001", "slices", "S01");
305
- const tasksDir = join(sliceDir, "tasks");
306
- mkdirSync(tasksDir, { recursive: true });
307
- writeFileSync(join(sliceDir, "S01-PLAN.md"), [
308
- "# S01: Test Slice",
309
- "",
310
- "## Tasks",
311
- "",
312
- "- [x] **T01: Implement feature** `est:2h`",
313
- "- [ ] **T02: Write tests** `est:1h`",
314
- ].join("\n"));
315
- writeFileSync(join(tasksDir, "T01-PLAN.md"), "# T01 Plan");
316
- writeFileSync(join(tasksDir, "T02-PLAN.md"), "# T02 Plan");
317
- assert.strictEqual(
318
- verifyExpectedArtifact("plan-slice", "M001/S01", base),
319
- true,
320
- "Plan with completed task entries should be treated as completed artifact",
321
- );
322
- } finally {
323
- cleanup(base);
324
- }
249
+ t.after(() => cleanup(base));
250
+
251
+ const sliceDir = join(base, ".gsd", "milestones", "M001", "slices", "S01");
252
+ const tasksDir = join(sliceDir, "tasks");
253
+ mkdirSync(tasksDir, { recursive: true });
254
+ writeFileSync(join(sliceDir, "S01-PLAN.md"), [
255
+ "# S01: Test Slice",
256
+ "",
257
+ "## Tasks",
258
+ "",
259
+ "- [ ] **T01: Implement feature** `est:2h`",
260
+ "- [ ] **T02: Write tests** `est:1h`",
261
+ ].join("\n"));
262
+ writeFileSync(join(tasksDir, "T01-PLAN.md"), "# T01 Plan");
263
+ writeFileSync(join(tasksDir, "T02-PLAN.md"), "# T02 Plan");
264
+ assert.strictEqual(
265
+ verifyExpectedArtifact("plan-slice", "M001/S01", base),
266
+ true,
267
+ "Plan with task entries should be treated as completed artifact",
268
+ );
269
+ });
270
+
271
+ test("verifyExpectedArtifact accepts plan-slice with completed tasks", (t) => {
272
+ const base = makeTmpBase();
273
+ t.after(() => cleanup(base));
274
+
275
+ const sliceDir = join(base, ".gsd", "milestones", "M001", "slices", "S01");
276
+ const tasksDir = join(sliceDir, "tasks");
277
+ mkdirSync(tasksDir, { recursive: true });
278
+ writeFileSync(join(sliceDir, "S01-PLAN.md"), [
279
+ "# S01: Test Slice",
280
+ "",
281
+ "## Tasks",
282
+ "",
283
+ "- [x] **T01: Implement feature** `est:2h`",
284
+ "- [ ] **T02: Write tests** `est:1h`",
285
+ ].join("\n"));
286
+ writeFileSync(join(tasksDir, "T01-PLAN.md"), "# T01 Plan");
287
+ writeFileSync(join(tasksDir, "T02-PLAN.md"), "# T02 Plan");
288
+ assert.strictEqual(
289
+ verifyExpectedArtifact("plan-slice", "M001/S01", base),
290
+ true,
291
+ "Plan with completed task entries should be treated as completed artifact",
292
+ );
325
293
  });
326
294
 
327
295
  // ─── verifyExpectedArtifact: plan-slice task plan check (#739) ────────────
328
296
 
329
- test("verifyExpectedArtifact plan-slice passes when all task plan files exist", () => {
297
+ test("verifyExpectedArtifact plan-slice passes when all task plan files exist", (t) => {
330
298
  const base = makeTmpBase();
331
- try {
332
- const tasksDir = join(base, ".gsd", "milestones", "M001", "slices", "S01", "tasks");
333
- const planPath = join(base, ".gsd", "milestones", "M001", "slices", "S01", "S01-PLAN.md");
334
- const planContent = [
335
- "# S01: Test Slice",
336
- "",
337
- "## Tasks",
338
- "",
339
- "- [ ] **T01: First task** `est:1h`",
340
- "- [ ] **T02: Second task** `est:2h`",
341
- ].join("\n");
342
- writeFileSync(planPath, planContent);
343
- writeFileSync(join(tasksDir, "T01-PLAN.md"), "# T01 Plan\n\nDo the thing.");
344
- writeFileSync(join(tasksDir, "T02-PLAN.md"), "# T02 Plan\n\nDo the other thing.");
345
-
346
- const result = verifyExpectedArtifact("plan-slice", "M001/S01", base);
347
- assert.equal(result, true, "should pass when all task plan files exist");
348
- } finally {
349
- cleanup(base);
350
- }
351
- });
352
-
353
- test("verifyExpectedArtifact plan-slice fails when a task plan file is missing (#739)", () => {
299
+ t.after(() => cleanup(base));
300
+
301
+ const tasksDir = join(base, ".gsd", "milestones", "M001", "slices", "S01", "tasks");
302
+ const planPath = join(base, ".gsd", "milestones", "M001", "slices", "S01", "S01-PLAN.md");
303
+ const planContent = [
304
+ "# S01: Test Slice",
305
+ "",
306
+ "## Tasks",
307
+ "",
308
+ "- [ ] **T01: First task** `est:1h`",
309
+ "- [ ] **T02: Second task** `est:2h`",
310
+ ].join("\n");
311
+ writeFileSync(planPath, planContent);
312
+ writeFileSync(join(tasksDir, "T01-PLAN.md"), "# T01 Plan\n\nDo the thing.");
313
+ writeFileSync(join(tasksDir, "T02-PLAN.md"), "# T02 Plan\n\nDo the other thing.");
314
+
315
+ const result = verifyExpectedArtifact("plan-slice", "M001/S01", base);
316
+ assert.equal(result, true, "should pass when all task plan files exist");
317
+ });
318
+
319
+ test("verifyExpectedArtifact plan-slice fails when a task plan file is missing (#739)", (t) => {
354
320
  const base = makeTmpBase();
355
- try {
356
- const tasksDir = join(base, ".gsd", "milestones", "M001", "slices", "S01", "tasks");
357
- const planPath = join(base, ".gsd", "milestones", "M001", "slices", "S01", "S01-PLAN.md");
358
- const planContent = [
359
- "# S01: Test Slice",
360
- "",
361
- "## Tasks",
362
- "",
363
- "- [ ] **T01: First task** `est:1h`",
364
- "- [ ] **T02: Second task** `est:2h`",
365
- ].join("\n");
366
- writeFileSync(planPath, planContent);
367
- // Only write T01-PLAN.md — T02 is missing
368
- writeFileSync(join(tasksDir, "T01-PLAN.md"), "# T01 Plan\n\nDo the thing.");
369
-
370
- const result = verifyExpectedArtifact("plan-slice", "M001/S01", base);
371
- assert.equal(result, false, "should fail when T02-PLAN.md is missing");
372
- } finally {
373
- cleanup(base);
374
- }
375
- });
376
-
377
- test("verifyExpectedArtifact plan-slice fails for plan with no tasks (#699)", () => {
321
+ t.after(() => cleanup(base));
322
+
323
+ const tasksDir = join(base, ".gsd", "milestones", "M001", "slices", "S01", "tasks");
324
+ const planPath = join(base, ".gsd", "milestones", "M001", "slices", "S01", "S01-PLAN.md");
325
+ const planContent = [
326
+ "# S01: Test Slice",
327
+ "",
328
+ "## Tasks",
329
+ "",
330
+ "- [ ] **T01: First task** `est:1h`",
331
+ "- [ ] **T02: Second task** `est:2h`",
332
+ ].join("\n");
333
+ writeFileSync(planPath, planContent);
334
+ // Only write T01-PLAN.md T02 is missing
335
+ writeFileSync(join(tasksDir, "T01-PLAN.md"), "# T01 Plan\n\nDo the thing.");
336
+
337
+ const result = verifyExpectedArtifact("plan-slice", "M001/S01", base);
338
+ assert.equal(result, false, "should fail when T02-PLAN.md is missing");
339
+ });
340
+
341
+ test("verifyExpectedArtifact plan-slice fails for plan with no tasks (#699)", (t) => {
378
342
  const base = makeTmpBase();
379
- try {
380
- const planPath = join(base, ".gsd", "milestones", "M001", "slices", "S01", "S01-PLAN.md");
381
- const planContent = [
382
- "# S01: Test Slice",
383
- "",
384
- "## Goal",
385
- "",
386
- "Just some documentation updates, no tasks.",
387
- ].join("\n");
388
- writeFileSync(planPath, planContent);
343
+ t.after(() => cleanup(base));
389
344
 
390
- const result = verifyExpectedArtifact("plan-slice", "M001/S01", base);
391
- assert.equal(result, false, "should fail when plan has no task entries (empty scaffold, #699)");
392
- } finally {
393
- cleanup(base);
394
- }
345
+ const planPath = join(base, ".gsd", "milestones", "M001", "slices", "S01", "S01-PLAN.md");
346
+ const planContent = [
347
+ "# S01: Test Slice",
348
+ "",
349
+ "## Goal",
350
+ "",
351
+ "Just some documentation updates, no tasks.",
352
+ ].join("\n");
353
+ writeFileSync(planPath, planContent);
354
+
355
+ const result = verifyExpectedArtifact("plan-slice", "M001/S01", base);
356
+ assert.equal(result, false, "should fail when plan has no task entries (empty scaffold, #699)");
395
357
  });
396
358
 
397
359
  // ─── verifyExpectedArtifact: heading-style plan tasks (#1691) ─────────────
398
360
 
399
- test("verifyExpectedArtifact accepts plan-slice with heading-style tasks (### T01 --)", () => {
361
+ test("verifyExpectedArtifact accepts plan-slice with heading-style tasks (### T01 --)", (t) => {
400
362
  const base = makeTmpBase();
401
- try {
402
- const sliceDir = join(base, ".gsd", "milestones", "M001", "slices", "S01");
403
- const tasksDir = join(sliceDir, "tasks");
404
- mkdirSync(tasksDir, { recursive: true });
405
- writeFileSync(join(sliceDir, "S01-PLAN.md"), [
406
- "# S01: Test Slice",
407
- "",
408
- "## Tasks",
409
- "",
410
- "### T01 -- Implement feature",
411
- "",
412
- "Feature description.",
413
- "",
414
- "### T02 -- Write tests",
415
- "",
416
- "Test description.",
417
- ].join("\n"));
418
- writeFileSync(join(tasksDir, "T01-PLAN.md"), "# T01 Plan");
419
- writeFileSync(join(tasksDir, "T02-PLAN.md"), "# T02 Plan");
420
- assert.strictEqual(
421
- verifyExpectedArtifact("plan-slice", "M001/S01", base),
422
- true,
423
- "Heading-style plan with task entries should be treated as completed artifact",
424
- );
425
- } finally {
426
- cleanup(base);
427
- }
428
- });
429
-
430
- test("verifyExpectedArtifact accepts plan-slice with colon-style heading tasks (### T01:)", () => {
363
+ t.after(() => cleanup(base));
364
+
365
+ const sliceDir = join(base, ".gsd", "milestones", "M001", "slices", "S01");
366
+ const tasksDir = join(sliceDir, "tasks");
367
+ mkdirSync(tasksDir, { recursive: true });
368
+ writeFileSync(join(sliceDir, "S01-PLAN.md"), [
369
+ "# S01: Test Slice",
370
+ "",
371
+ "## Tasks",
372
+ "",
373
+ "### T01 -- Implement feature",
374
+ "",
375
+ "Feature description.",
376
+ "",
377
+ "### T02 -- Write tests",
378
+ "",
379
+ "Test description.",
380
+ ].join("\n"));
381
+ writeFileSync(join(tasksDir, "T01-PLAN.md"), "# T01 Plan");
382
+ writeFileSync(join(tasksDir, "T02-PLAN.md"), "# T02 Plan");
383
+ assert.strictEqual(
384
+ verifyExpectedArtifact("plan-slice", "M001/S01", base),
385
+ true,
386
+ "Heading-style plan with task entries should be treated as completed artifact",
387
+ );
388
+ });
389
+
390
+ test("verifyExpectedArtifact accepts plan-slice with colon-style heading tasks (### T01:)", (t) => {
431
391
  const base = makeTmpBase();
432
- try {
433
- const sliceDir = join(base, ".gsd", "milestones", "M001", "slices", "S01");
434
- const tasksDir = join(sliceDir, "tasks");
435
- mkdirSync(tasksDir, { recursive: true });
436
- writeFileSync(join(sliceDir, "S01-PLAN.md"), [
437
- "# S01: Test Slice",
438
- "",
439
- "## Tasks",
440
- "",
441
- "### T01: Implement feature",
442
- "",
443
- "Feature description.",
444
- ].join("\n"));
445
- writeFileSync(join(tasksDir, "T01-PLAN.md"), "# T01 Plan");
446
- assert.strictEqual(
447
- verifyExpectedArtifact("plan-slice", "M001/S01", base),
448
- true,
449
- "Colon heading-style plan should be treated as completed artifact",
450
- );
451
- } finally {
452
- cleanup(base);
453
- }
454
- });
455
-
456
- test("verifyExpectedArtifact execute-task passes for heading-style plan entry (#1691)", () => {
392
+ t.after(() => cleanup(base));
393
+
394
+ const sliceDir = join(base, ".gsd", "milestones", "M001", "slices", "S01");
395
+ const tasksDir = join(sliceDir, "tasks");
396
+ mkdirSync(tasksDir, { recursive: true });
397
+ writeFileSync(join(sliceDir, "S01-PLAN.md"), [
398
+ "# S01: Test Slice",
399
+ "",
400
+ "## Tasks",
401
+ "",
402
+ "### T01: Implement feature",
403
+ "",
404
+ "Feature description.",
405
+ ].join("\n"));
406
+ writeFileSync(join(tasksDir, "T01-PLAN.md"), "# T01 Plan");
407
+ assert.strictEqual(
408
+ verifyExpectedArtifact("plan-slice", "M001/S01", base),
409
+ true,
410
+ "Colon heading-style plan should be treated as completed artifact",
411
+ );
412
+ });
413
+
414
+ test("verifyExpectedArtifact execute-task passes for heading-style plan entry (#1691)", (t) => {
457
415
  const base = makeTmpBase();
458
- try {
459
- const sliceDir = join(base, ".gsd", "milestones", "M001", "slices", "S01");
460
- const tasksDir = join(sliceDir, "tasks");
461
- mkdirSync(tasksDir, { recursive: true });
462
- writeFileSync(join(sliceDir, "S01-PLAN.md"), [
463
- "# S01: Test Slice",
464
- "",
465
- "## Tasks",
466
- "",
467
- "### T01 -- Implement feature",
468
- "",
469
- "Feature description.",
470
- ].join("\n"));
471
- writeFileSync(join(tasksDir, "T01-SUMMARY.md"), "# T01 Summary\n\nDone.");
472
- assert.strictEqual(
473
- verifyExpectedArtifact("execute-task", "M001/S01/T01", base),
474
- true,
475
- "execute-task should pass for heading-style plan entry when summary exists",
476
- );
477
- } finally {
478
- cleanup(base);
479
- }
416
+ t.after(() => cleanup(base));
417
+
418
+ const sliceDir = join(base, ".gsd", "milestones", "M001", "slices", "S01");
419
+ const tasksDir = join(sliceDir, "tasks");
420
+ mkdirSync(tasksDir, { recursive: true });
421
+ writeFileSync(join(sliceDir, "S01-PLAN.md"), [
422
+ "# S01: Test Slice",
423
+ "",
424
+ "## Tasks",
425
+ "",
426
+ "### T01 -- Implement feature",
427
+ "",
428
+ "Feature description.",
429
+ ].join("\n"));
430
+ writeFileSync(join(tasksDir, "T01-SUMMARY.md"), "# T01 Summary\n\nDone.");
431
+ assert.strictEqual(
432
+ verifyExpectedArtifact("execute-task", "M001/S01/T01", base),
433
+ true,
434
+ "execute-task should pass for heading-style plan entry when summary exists",
435
+ );
480
436
  });
481
437
 
482
438
  test("verifyExpectedArtifact plan-slice passes for rendered slice/task plan artifacts from DB", async () => {
@@ -618,83 +574,81 @@ test("verifyExpectedArtifact plan-slice fails after deleting a rendered task pla
618
574
 
619
575
  // ─── selfHealRuntimeRecords — worktree base path (#769) ──────────────────
620
576
 
621
- test("selfHealRuntimeRecords clears stale dispatched records (#769)", async () => {
577
+ test("selfHealRuntimeRecords clears stale dispatched records (#769)", async (t) => {
622
578
  // selfHealRuntimeRecords now only clears stale dispatched records (>1h).
623
579
  // No completedKeySet parameter — deriveState is sole authority.
624
580
  const worktreeBase = makeTmpBase();
625
581
  const mainBase = makeTmpBase();
626
- try {
627
- const { writeUnitRuntimeRecord, readUnitRuntimeRecord } = await import("../unit-runtime.ts");
582
+ t.after(() => {
583
+ cleanup(worktreeBase);
584
+ cleanup(mainBase);
585
+ });
628
586
 
629
- // Write a stale runtime record in the worktree .gsd/runtime/units/
630
- writeUnitRuntimeRecord(worktreeBase, "run-uat", "M001/S01", Date.now() - 7200_000, {
631
- phase: "dispatched",
632
- });
587
+ const { writeUnitRuntimeRecord, readUnitRuntimeRecord } = await import("../unit-runtime.ts");
633
588
 
634
- // Verify the runtime record exists before heal
635
- const before = readUnitRuntimeRecord(worktreeBase, "run-uat", "M001/S01");
636
- assert.ok(before, "runtime record should exist before heal");
589
+ // Write a stale runtime record in the worktree .gsd/runtime/units/
590
+ writeUnitRuntimeRecord(worktreeBase, "run-uat", "M001/S01", Date.now() - 7200_000, {
591
+ phase: "dispatched",
592
+ });
637
593
 
638
- // Mock ExtensionContext with minimal notify
639
- const notifications: string[] = [];
640
- const mockCtx = {
641
- ui: { notify: (msg: string) => { notifications.push(msg); } },
642
- } as any;
594
+ // Verify the runtime record exists before heal
595
+ const before = readUnitRuntimeRecord(worktreeBase, "run-uat", "M001/S01");
596
+ assert.ok(before, "runtime record should exist before heal");
643
597
 
644
- // Call selfHeal with worktreeBase — should clear the stale record
645
- await selfHealRuntimeRecords(worktreeBase, mockCtx);
598
+ // Mock ExtensionContext with minimal notify
599
+ const notifications: string[] = [];
600
+ const mockCtx = {
601
+ ui: { notify: (msg: string) => { notifications.push(msg); } },
602
+ } as any;
646
603
 
647
- // The stale record should be cleared
648
- const after = readUnitRuntimeRecord(worktreeBase, "run-uat", "M001/S01");
649
- assert.equal(after, null, "runtime record should be cleared after heal");
650
- assert.ok(notifications.some(n => n.includes("Self-heal")), "should emit self-heal notification");
604
+ // Call selfHeal with worktreeBase — should clear the stale record
605
+ await selfHealRuntimeRecords(worktreeBase, mockCtx);
651
606
 
652
- // Write a stale record at mainBase
653
- writeUnitRuntimeRecord(mainBase, "run-uat", "M001/S01", Date.now() - 7200_000, {
654
- phase: "dispatched",
655
- });
656
- await selfHealRuntimeRecords(mainBase, mockCtx);
607
+ // The stale record should be cleared
608
+ const after = readUnitRuntimeRecord(worktreeBase, "run-uat", "M001/S01");
609
+ assert.equal(after, null, "runtime record should be cleared after heal");
610
+ assert.ok(notifications.some(n => n.includes("Self-heal")), "should emit self-heal notification");
657
611
 
658
- // The record at mainBase should also be cleared by the stale timeout (>1h)
659
- const afterMain = readUnitRuntimeRecord(mainBase, "run-uat", "M001/S01");
660
- assert.equal(afterMain, null, "stale record at main base should be cleared by timeout");
661
- } finally {
662
- cleanup(worktreeBase);
663
- cleanup(mainBase);
664
- }
612
+ // Write a stale record at mainBase
613
+ writeUnitRuntimeRecord(mainBase, "run-uat", "M001/S01", Date.now() - 7200_000, {
614
+ phase: "dispatched",
615
+ });
616
+ await selfHealRuntimeRecords(mainBase, mockCtx);
617
+
618
+ // The record at mainBase should also be cleared by the stale timeout (>1h)
619
+ const afterMain = readUnitRuntimeRecord(mainBase, "run-uat", "M001/S01");
620
+ assert.equal(afterMain, null, "stale record at main base should be cleared by timeout");
665
621
  });
666
622
 
667
623
  // ─── #1625: selfHealRuntimeRecords on resume clears paused-session leftovers ──
668
624
 
669
- test("selfHealRuntimeRecords clears recently-paused dispatched records on resume (#1625)", async () => {
625
+ test("selfHealRuntimeRecords clears recently-paused dispatched records on resume (#1625)", async (t) => {
670
626
  // When pauseAuto closes out a unit but clearUnitRuntimeRecord silently fails
671
627
  // (e.g. permission error), selfHealRuntimeRecords on resume should still
672
628
  // clean up stale dispatched records that are >1h old.
673
629
  const base = makeTmpBase();
674
- try {
675
- const { writeUnitRuntimeRecord, readUnitRuntimeRecord } = await import("../unit-runtime.ts");
630
+ t.after(() => cleanup(base));
676
631
 
677
- // Simulate a record left behind after a pause — aged >1h to be considered stale
678
- writeUnitRuntimeRecord(base, "execute-task", "M001/S01/T01", Date.now() - 3700_000, {
679
- phase: "dispatched",
680
- });
632
+ const { writeUnitRuntimeRecord, readUnitRuntimeRecord } = await import("../unit-runtime.ts");
681
633
 
682
- const before = readUnitRuntimeRecord(base, "execute-task", "M001/S01/T01");
683
- assert.ok(before, "dispatched record should exist before resume heal");
684
- assert.equal(before!.phase, "dispatched");
634
+ // Simulate a record left behind after a pause — aged >1h to be considered stale
635
+ writeUnitRuntimeRecord(base, "execute-task", "M001/S01/T01", Date.now() - 3700_000, {
636
+ phase: "dispatched",
637
+ });
685
638
 
686
- const notifications: string[] = [];
687
- const mockCtx = {
688
- ui: { notify: (msg: string) => { notifications.push(msg); } },
689
- } as any;
639
+ const before = readUnitRuntimeRecord(base, "execute-task", "M001/S01/T01");
640
+ assert.ok(before, "dispatched record should exist before resume heal");
641
+ assert.equal(before!.phase, "dispatched");
690
642
 
691
- await selfHealRuntimeRecords(base, mockCtx);
643
+ const notifications: string[] = [];
644
+ const mockCtx = {
645
+ ui: { notify: (msg: string) => { notifications.push(msg); } },
646
+ } as any;
692
647
 
693
- const after = readUnitRuntimeRecord(base, "execute-task", "M001/S01/T01");
694
- assert.equal(after, null, "stale dispatched record should be cleared on resume (#1625)");
695
- } finally {
696
- cleanup(base);
697
- }
648
+ await selfHealRuntimeRecords(base, mockCtx);
649
+
650
+ const after = readUnitRuntimeRecord(base, "execute-task", "M001/S01/T01");
651
+ assert.equal(after, null, "stale dispatched record should be cleared on resume (#1625)");
698
652
  });
699
653
 
700
654
  // ─── #793: invalidateAllCaches unblocks skip-loop ─────────────────────────
@@ -702,51 +656,49 @@ test("selfHealRuntimeRecords clears recently-paused dispatched records on resume
702
656
  // just invalidateStateCache()) to clear path/parse caches that deriveState
703
657
  // depends on. Without this, even after cache invalidation, deriveState reads
704
658
  // stale directory listings and returns the same unit, looping forever.
705
- test("#793: invalidateAllCaches clears all caches so deriveState sees fresh disk state", async () => {
659
+ test("#793: invalidateAllCaches clears all caches so deriveState sees fresh disk state", async (t) => {
706
660
  const base = makeTmpBase();
707
- try {
708
- const mid = "M001";
709
- const sid = "S01";
710
- const planDir = join(base, ".gsd", "milestones", mid, "slices", sid);
711
- const tasksDir = join(planDir, "tasks");
712
- mkdirSync(tasksDir, { recursive: true });
713
- mkdirSync(join(base, ".gsd", "milestones", mid), { recursive: true });
714
-
715
- writeFileSync(
716
- join(base, ".gsd", "milestones", mid, `${mid}-ROADMAP.md`),
717
- `# M001: Test Milestone\n\n**Vision:** test.\n\n## Slices\n\n- [ ] **${sid}: Slice One** \`risk:low\` \`depends:[]\`\n > After this: done.\n`,
718
- );
719
- const planUnchecked = `# ${sid}: Slice One\n\n**Goal:** test.\n\n## Tasks\n\n- [ ] **T01: Task One** \`est:10m\`\n- [ ] **T02: Task Two** \`est:10m\`\n`;
720
- writeFileSync(join(planDir, `${sid}-PLAN.md`), planUnchecked);
721
- writeFileSync(join(tasksDir, "T01-PLAN.md"), "# T01: Task One\n\n**Goal:** t\n\n## Steps\n- step\n\n## Verification\n- v\n");
722
- writeFileSync(join(tasksDir, "T02-PLAN.md"), "# T02: Task Two\n\n**Goal:** t\n\n## Steps\n- step\n\n## Verification\n- v\n");
723
-
724
- // Warm all caches
725
- const state1 = await deriveState(base);
726
- assert.equal(state1.activeTask?.id, "T01", "initial: T01 is active");
727
-
728
- // Simulate task completion on disk (what the LLM does)
729
- const planChecked = `# ${sid}: Slice One\n\n**Goal:** test.\n\n## Tasks\n\n- [x] **T01: Task One** \`est:10m\`\n- [ ] **T02: Task Two** \`est:10m\`\n`;
730
- writeFileSync(join(planDir, `${sid}-PLAN.md`), planChecked);
731
- writeFileSync(join(tasksDir, "T01-SUMMARY.md"), "---\nid: T01\n---\n# Summary\n");
732
-
733
- // invalidateStateCache alone: _stateCache cleared but path/parse caches warm
734
- invalidateStateCache();
735
-
736
- // invalidateAllCaches: all caches cleared — deriveState must re-read disk
737
- invalidateAllCaches();
738
- const state2 = await deriveState(base);
739
-
740
- // After full invalidation, T01 should be complete and T02 should be next
741
- assert.notEqual(state2.activeTask?.id, "T01", "#793: T01 not re-dispatched after full invalidation");
742
-
743
- // Verify the caches are truly cleared by calling clearParseCache and clearPathCache
744
- // do not throw (they should be no-ops after invalidateAllCaches already cleared them)
745
- clearParseCache(); // no-op, but should not throw
746
- assert.ok(true, "clearParseCache after invalidateAllCaches is safe");
747
- } finally {
748
- cleanup(base);
749
- }
661
+ t.after(() => cleanup(base));
662
+
663
+ const mid = "M001";
664
+ const sid = "S01";
665
+ const planDir = join(base, ".gsd", "milestones", mid, "slices", sid);
666
+ const tasksDir = join(planDir, "tasks");
667
+ mkdirSync(tasksDir, { recursive: true });
668
+ mkdirSync(join(base, ".gsd", "milestones", mid), { recursive: true });
669
+
670
+ writeFileSync(
671
+ join(base, ".gsd", "milestones", mid, `${mid}-ROADMAP.md`),
672
+ `# M001: Test Milestone\n\n**Vision:** test.\n\n## Slices\n\n- [ ] **${sid}: Slice One** \`risk:low\` \`depends:[]\`\n > After this: done.\n`,
673
+ );
674
+ const planUnchecked = `# ${sid}: Slice One\n\n**Goal:** test.\n\n## Tasks\n\n- [ ] **T01: Task One** \`est:10m\`\n- [ ] **T02: Task Two** \`est:10m\`\n`;
675
+ writeFileSync(join(planDir, `${sid}-PLAN.md`), planUnchecked);
676
+ writeFileSync(join(tasksDir, "T01-PLAN.md"), "# T01: Task One\n\n**Goal:** t\n\n## Steps\n- step\n\n## Verification\n- v\n");
677
+ writeFileSync(join(tasksDir, "T02-PLAN.md"), "# T02: Task Two\n\n**Goal:** t\n\n## Steps\n- step\n\n## Verification\n- v\n");
678
+
679
+ // Warm all caches
680
+ const state1 = await deriveState(base);
681
+ assert.equal(state1.activeTask?.id, "T01", "initial: T01 is active");
682
+
683
+ // Simulate task completion on disk (what the LLM does)
684
+ const planChecked = `# ${sid}: Slice One\n\n**Goal:** test.\n\n## Tasks\n\n- [x] **T01: Task One** \`est:10m\`\n- [ ] **T02: Task Two** \`est:10m\`\n`;
685
+ writeFileSync(join(planDir, `${sid}-PLAN.md`), planChecked);
686
+ writeFileSync(join(tasksDir, "T01-SUMMARY.md"), "---\nid: T01\n---\n# Summary\n");
687
+
688
+ // invalidateStateCache alone: _stateCache cleared but path/parse caches warm
689
+ invalidateStateCache();
690
+
691
+ // invalidateAllCaches: all caches cleared — deriveState must re-read disk
692
+ invalidateAllCaches();
693
+ const state2 = await deriveState(base);
694
+
695
+ // After full invalidation, T01 should be complete and T02 should be next
696
+ assert.notEqual(state2.activeTask?.id, "T01", "#793: T01 not re-dispatched after full invalidation");
697
+
698
+ // Verify the caches are truly cleared by calling clearParseCache and clearPathCache
699
+ // do not throw (they should be no-ops after invalidateAllCaches already cleared them)
700
+ clearParseCache(); // no-op, but should not throw
701
+ assert.ok(true, "clearParseCache after invalidateAllCaches is safe");
750
702
  });
751
703
 
752
704
  // ─── hasImplementationArtifacts (#1703) ───────────────────────────────────
@@ -766,88 +718,78 @@ function makeGitBase(): string {
766
718
  return base;
767
719
  }
768
720
 
769
- test("hasImplementationArtifacts returns false when only .gsd/ files committed (#1703)", () => {
721
+ test("hasImplementationArtifacts returns false when only .gsd/ files committed (#1703)", (t) => {
770
722
  const base = makeGitBase();
771
- try {
772
- // Create a feature branch and commit only .gsd/ files
773
- execFileSync("git", ["checkout", "-b", "feat/test-milestone"], { cwd: base, stdio: "ignore" });
774
- mkdirSync(join(base, ".gsd", "milestones", "M001"), { recursive: true });
775
- writeFileSync(join(base, ".gsd", "milestones", "M001", "M001-ROADMAP.md"), "# Roadmap");
776
- writeFileSync(join(base, ".gsd", "milestones", "M001", "M001-SUMMARY.md"), "# Summary");
777
- execFileSync("git", ["add", "."], { cwd: base, stdio: "ignore" });
778
- execFileSync("git", ["commit", "-m", "chore: add plan files"], { cwd: base, stdio: "ignore" });
779
-
780
- const result = hasImplementationArtifacts(base);
781
- assert.equal(result, false, "should return false when only .gsd/ files were committed");
782
- } finally {
783
- cleanup(base);
784
- }
723
+ t.after(() => cleanup(base));
724
+
725
+ // Create a feature branch and commit only .gsd/ files
726
+ execFileSync("git", ["checkout", "-b", "feat/test-milestone"], { cwd: base, stdio: "ignore" });
727
+ mkdirSync(join(base, ".gsd", "milestones", "M001"), { recursive: true });
728
+ writeFileSync(join(base, ".gsd", "milestones", "M001", "M001-ROADMAP.md"), "# Roadmap");
729
+ writeFileSync(join(base, ".gsd", "milestones", "M001", "M001-SUMMARY.md"), "# Summary");
730
+ execFileSync("git", ["add", "."], { cwd: base, stdio: "ignore" });
731
+ execFileSync("git", ["commit", "-m", "chore: add plan files"], { cwd: base, stdio: "ignore" });
732
+
733
+ const result = hasImplementationArtifacts(base);
734
+ assert.equal(result, false, "should return false when only .gsd/ files were committed");
785
735
  });
786
736
 
787
- test("hasImplementationArtifacts returns true when implementation files committed (#1703)", () => {
737
+ test("hasImplementationArtifacts returns true when implementation files committed (#1703)", (t) => {
788
738
  const base = makeGitBase();
789
- try {
790
- // Create a feature branch with both .gsd/ and implementation files
791
- execFileSync("git", ["checkout", "-b", "feat/test-impl"], { cwd: base, stdio: "ignore" });
792
- mkdirSync(join(base, ".gsd", "milestones", "M001"), { recursive: true });
793
- writeFileSync(join(base, ".gsd", "milestones", "M001", "M001-ROADMAP.md"), "# Roadmap");
794
- mkdirSync(join(base, "src"), { recursive: true });
795
- writeFileSync(join(base, "src", "feature.ts"), "export function feature() {}");
796
- execFileSync("git", ["add", "."], { cwd: base, stdio: "ignore" });
797
- execFileSync("git", ["commit", "-m", "feat: add feature"], { cwd: base, stdio: "ignore" });
798
-
799
- const result = hasImplementationArtifacts(base);
800
- assert.equal(result, true, "should return true when implementation files are present");
801
- } finally {
802
- cleanup(base);
803
- }
739
+ t.after(() => cleanup(base));
740
+
741
+ // Create a feature branch with both .gsd/ and implementation files
742
+ execFileSync("git", ["checkout", "-b", "feat/test-impl"], { cwd: base, stdio: "ignore" });
743
+ mkdirSync(join(base, ".gsd", "milestones", "M001"), { recursive: true });
744
+ writeFileSync(join(base, ".gsd", "milestones", "M001", "M001-ROADMAP.md"), "# Roadmap");
745
+ mkdirSync(join(base, "src"), { recursive: true });
746
+ writeFileSync(join(base, "src", "feature.ts"), "export function feature() {}");
747
+ execFileSync("git", ["add", "."], { cwd: base, stdio: "ignore" });
748
+ execFileSync("git", ["commit", "-m", "feat: add feature"], { cwd: base, stdio: "ignore" });
749
+
750
+ const result = hasImplementationArtifacts(base);
751
+ assert.equal(result, true, "should return true when implementation files are present");
804
752
  });
805
753
 
806
- test("hasImplementationArtifacts returns true on non-git directory (fail-open)", () => {
754
+ test("hasImplementationArtifacts returns true on non-git directory (fail-open)", (t) => {
807
755
  const base = join(tmpdir(), `gsd-test-nogit-${randomUUID()}`);
808
756
  mkdirSync(base, { recursive: true });
809
- try {
810
- const result = hasImplementationArtifacts(base);
811
- assert.equal(result, true, "should return true (fail-open) in non-git directory");
812
- } finally {
813
- cleanup(base);
814
- }
757
+ t.after(() => cleanup(base));
758
+
759
+ const result = hasImplementationArtifacts(base);
760
+ assert.equal(result, true, "should return true (fail-open) in non-git directory");
815
761
  });
816
762
 
817
763
  // ─── verifyExpectedArtifact: complete-milestone requires impl artifacts (#1703) ──
818
764
 
819
- test("verifyExpectedArtifact complete-milestone fails with only .gsd/ files (#1703)", () => {
765
+ test("verifyExpectedArtifact complete-milestone fails with only .gsd/ files (#1703)", (t) => {
820
766
  const base = makeGitBase();
821
- try {
822
- // Create feature branch with only .gsd/ files
823
- execFileSync("git", ["checkout", "-b", "feat/ms-only-gsd"], { cwd: base, stdio: "ignore" });
824
- mkdirSync(join(base, ".gsd", "milestones", "M001"), { recursive: true });
825
- writeFileSync(join(base, ".gsd", "milestones", "M001", "M001-SUMMARY.md"), "# Milestone Summary\nDone.");
826
- execFileSync("git", ["add", "."], { cwd: base, stdio: "ignore" });
827
- execFileSync("git", ["commit", "-m", "chore: milestone plan files"], { cwd: base, stdio: "ignore" });
828
-
829
- const result = verifyExpectedArtifact("complete-milestone", "M001", base);
830
- assert.equal(result, false, "complete-milestone should fail verification when only .gsd/ files present");
831
- } finally {
832
- cleanup(base);
833
- }
767
+ t.after(() => cleanup(base));
768
+
769
+ // Create feature branch with only .gsd/ files
770
+ execFileSync("git", ["checkout", "-b", "feat/ms-only-gsd"], { cwd: base, stdio: "ignore" });
771
+ mkdirSync(join(base, ".gsd", "milestones", "M001"), { recursive: true });
772
+ writeFileSync(join(base, ".gsd", "milestones", "M001", "M001-SUMMARY.md"), "# Milestone Summary\nDone.");
773
+ execFileSync("git", ["add", "."], { cwd: base, stdio: "ignore" });
774
+ execFileSync("git", ["commit", "-m", "chore: milestone plan files"], { cwd: base, stdio: "ignore" });
775
+
776
+ const result = verifyExpectedArtifact("complete-milestone", "M001", base);
777
+ assert.equal(result, false, "complete-milestone should fail verification when only .gsd/ files present");
834
778
  });
835
779
 
836
- test("verifyExpectedArtifact complete-milestone passes with impl files (#1703)", () => {
780
+ test("verifyExpectedArtifact complete-milestone passes with impl files (#1703)", (t) => {
837
781
  const base = makeGitBase();
838
- try {
839
- // Create feature branch with implementation files AND milestone summary
840
- execFileSync("git", ["checkout", "-b", "feat/ms-with-impl"], { cwd: base, stdio: "ignore" });
841
- mkdirSync(join(base, ".gsd", "milestones", "M001"), { recursive: true });
842
- writeFileSync(join(base, ".gsd", "milestones", "M001", "M001-SUMMARY.md"), "# Milestone Summary\nDone.");
843
- mkdirSync(join(base, "src"), { recursive: true });
844
- writeFileSync(join(base, "src", "app.ts"), "console.log('hello');");
845
- execFileSync("git", ["add", "."], { cwd: base, stdio: "ignore" });
846
- execFileSync("git", ["commit", "-m", "feat: implementation"], { cwd: base, stdio: "ignore" });
847
-
848
- const result = verifyExpectedArtifact("complete-milestone", "M001", base);
849
- assert.equal(result, true, "complete-milestone should pass verification with implementation files");
850
- } finally {
851
- cleanup(base);
852
- }
782
+ t.after(() => cleanup(base));
783
+
784
+ // Create feature branch with implementation files AND milestone summary
785
+ execFileSync("git", ["checkout", "-b", "feat/ms-with-impl"], { cwd: base, stdio: "ignore" });
786
+ mkdirSync(join(base, ".gsd", "milestones", "M001"), { recursive: true });
787
+ writeFileSync(join(base, ".gsd", "milestones", "M001", "M001-SUMMARY.md"), "# Milestone Summary\nDone.");
788
+ mkdirSync(join(base, "src"), { recursive: true });
789
+ writeFileSync(join(base, "src", "app.ts"), "console.log('hello');");
790
+ execFileSync("git", ["add", "."], { cwd: base, stdio: "ignore" });
791
+ execFileSync("git", ["commit", "-m", "feat: implementation"], { cwd: base, stdio: "ignore" });
792
+
793
+ const result = verifyExpectedArtifact("complete-milestone", "M001", base);
794
+ assert.equal(result, true, "complete-milestone should pass verification with implementation files");
853
795
  });