gsd-pi 2.44.0-dev.848dd4c → 2.44.0

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 (298) hide show
  1. package/README.md +12 -30
  2. package/dist/resources/extensions/gsd/auto-start.js +0 -10
  3. package/dist/resources/extensions/gsd/commands/handlers/workflow.js +0 -5
  4. package/dist/web/standalone/.next/BUILD_ID +1 -1
  5. package/dist/web/standalone/.next/app-path-routes-manifest.json +14 -14
  6. package/dist/web/standalone/.next/build-manifest.json +3 -3
  7. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  8. package/dist/web/standalone/.next/required-server-files.json +3 -3
  9. package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
  10. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  11. package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
  12. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  13. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  14. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  15. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  16. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  17. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  18. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  19. package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
  20. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  21. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  22. package/dist/web/standalone/.next/server/app/_not-found.rsc +3 -3
  23. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
  24. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  25. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +3 -3
  26. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  27. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  28. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  29. package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
  30. package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
  31. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
  32. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
  33. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
  34. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
  35. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
  36. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
  37. package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
  38. package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
  39. package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
  40. package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
  41. package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
  42. package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
  43. package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
  44. package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
  45. package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
  46. package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
  47. package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
  48. package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
  49. package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
  50. package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
  51. package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
  52. package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
  53. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  54. package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
  55. package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
  56. package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
  57. package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
  58. package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
  59. package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
  60. package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
  61. package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
  62. package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
  63. package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
  64. package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
  65. package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
  66. package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
  67. package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
  68. package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
  69. package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
  70. package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
  71. package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
  72. package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
  73. package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +5 -5
  74. package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
  75. package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
  76. package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
  77. package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
  78. package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
  79. package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
  80. package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
  81. package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
  82. package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
  83. package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
  84. package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
  85. package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
  86. package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
  87. package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
  88. package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
  89. package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
  90. package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
  91. package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
  92. package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
  93. package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +2 -2
  94. package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
  95. package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
  96. package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
  97. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +2 -2
  98. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
  99. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +4 -4
  100. package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
  101. package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
  102. package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
  103. package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
  104. package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
  105. package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
  106. package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
  107. package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
  108. package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
  109. package/dist/web/standalone/.next/server/app/index.html +1 -1
  110. package/dist/web/standalone/.next/server/app/index.rsc +4 -4
  111. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  112. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +4 -4
  113. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  114. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +3 -3
  115. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  116. package/dist/web/standalone/.next/server/app/page.js +2 -2
  117. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  118. package/dist/web/standalone/.next/server/app-paths-manifest.json +14 -14
  119. package/dist/web/standalone/.next/server/chunks/229.js +1 -1
  120. package/dist/web/standalone/.next/server/chunks/471.js +3 -3
  121. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  122. package/dist/web/standalone/.next/server/middleware.js +2 -2
  123. package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
  124. package/dist/web/standalone/.next/server/next-font-manifest.json +1 -1
  125. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  126. package/dist/web/standalone/.next/server/pages/500.html +2 -2
  127. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  128. package/dist/web/standalone/.next/static/chunks/app/_not-found/{page-f2a7482d42a5614b.js → page-2f24283c162b6ab3.js} +1 -1
  129. package/dist/web/standalone/.next/static/chunks/app/{layout-a16c7a7ecdf0c2cf.js → layout-9ecfd95f343793f0.js} +1 -1
  130. package/dist/web/standalone/.next/static/chunks/app/page-7e9530a7122506c5.js +1 -0
  131. package/dist/web/standalone/.next/static/chunks/main-app-d3d4c336195465f9.js +1 -0
  132. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-ab5a8926e07ec673.js +1 -0
  133. package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
  134. package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
  135. package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
  136. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api.target.mk +14 -14
  137. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_except.target.mk +14 -14
  138. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_maybe.target.mk +14 -14
  139. package/dist/web/standalone/server.js +1 -1
  140. package/package.json +1 -1
  141. package/packages/pi-coding-agent/dist/core/auth-storage.test.js +8 -6
  142. package/packages/pi-coding-agent/dist/core/auth-storage.test.js.map +1 -1
  143. package/packages/pi-coding-agent/dist/core/extensions/runner.test.js +26 -24
  144. package/packages/pi-coding-agent/dist/core/extensions/runner.test.js.map +1 -1
  145. package/packages/pi-coding-agent/dist/core/fs-utils.test.js +48 -29
  146. package/packages/pi-coding-agent/dist/core/fs-utils.test.js.map +1 -1
  147. package/packages/pi-coding-agent/dist/core/resolve-config-value.test.js +44 -34
  148. package/packages/pi-coding-agent/dist/core/resolve-config-value.test.js.map +1 -1
  149. package/packages/pi-coding-agent/dist/core/session-manager.test.js +34 -30
  150. package/packages/pi-coding-agent/dist/core/session-manager.test.js.map +1 -1
  151. package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.js +12 -10
  152. package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.js.map +1 -1
  153. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.test.js +47 -43
  154. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.test.js.map +1 -1
  155. package/packages/pi-coding-agent/src/core/auth-storage.test.ts +7 -7
  156. package/packages/pi-coding-agent/src/core/extensions/runner.test.ts +26 -26
  157. package/packages/pi-coding-agent/src/core/fs-utils.test.ts +43 -31
  158. package/packages/pi-coding-agent/src/core/resolve-config-value.test.ts +45 -40
  159. package/packages/pi-coding-agent/src/core/session-manager.test.ts +33 -33
  160. package/packages/pi-coding-agent/src/core/tools/edit-diff.test.ts +17 -17
  161. package/packages/pi-coding-agent/src/resources/extensions/memory/storage.test.ts +74 -74
  162. package/src/resources/extensions/gsd/auto-start.ts +0 -14
  163. package/src/resources/extensions/gsd/commands/handlers/workflow.ts +0 -8
  164. package/src/resources/extensions/gsd/tests/all-milestones-complete-merge.test.ts +99 -99
  165. package/src/resources/extensions/gsd/tests/auto-lock-creation.test.ts +16 -14
  166. package/src/resources/extensions/gsd/tests/auto-paused-session-validation.test.ts +57 -43
  167. package/src/resources/extensions/gsd/tests/auto-preflight.test.ts +13 -11
  168. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +523 -465
  169. package/src/resources/extensions/gsd/tests/auto-secrets-gate.test.ts +75 -73
  170. package/src/resources/extensions/gsd/tests/auto-start-needs-discussion.test.ts +56 -34
  171. package/src/resources/extensions/gsd/tests/auto-worktree-milestone-merge.test.ts +656 -533
  172. package/src/resources/extensions/gsd/tests/auto-worktree.test.ts +143 -165
  173. package/src/resources/extensions/gsd/tests/cache-staleness-regression.test.ts +52 -29
  174. package/src/resources/extensions/gsd/tests/captures.test.ts +176 -148
  175. package/src/resources/extensions/gsd/tests/claude-import-tui.test.ts +33 -32
  176. package/src/resources/extensions/gsd/tests/collect-from-manifest.test.ts +143 -141
  177. package/src/resources/extensions/gsd/tests/commands-inspect-open-db.test.ts +25 -25
  178. package/src/resources/extensions/gsd/tests/commands-logs.test.ts +81 -81
  179. package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +59 -38
  180. package/src/resources/extensions/gsd/tests/complete-slice.test.ts +263 -228
  181. package/src/resources/extensions/gsd/tests/complete-task.test.ts +302 -250
  182. package/src/resources/extensions/gsd/tests/context-store.test.ts +367 -354
  183. package/src/resources/extensions/gsd/tests/continue-here.test.ts +72 -68
  184. package/src/resources/extensions/gsd/tests/cost-projection.test.ts +106 -92
  185. package/src/resources/extensions/gsd/tests/crash-recovery.test.ts +35 -27
  186. package/src/resources/extensions/gsd/tests/dashboard-budget.test.ts +237 -220
  187. package/src/resources/extensions/gsd/tests/db-writer.test.ts +420 -390
  188. package/src/resources/extensions/gsd/tests/definition-loader.test.ts +92 -76
  189. package/src/resources/extensions/gsd/tests/derive-state-crossval.test.ts +83 -68
  190. package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +183 -152
  191. package/src/resources/extensions/gsd/tests/derive-state-deps.test.ts +101 -78
  192. package/src/resources/extensions/gsd/tests/derive-state.test.ts +227 -192
  193. package/src/resources/extensions/gsd/tests/detection.test.ts +278 -232
  194. package/src/resources/extensions/gsd/tests/dev-engine-wrapper.test.ts +34 -30
  195. package/src/resources/extensions/gsd/tests/dispatch-guard.test.ts +180 -164
  196. package/src/resources/extensions/gsd/tests/dispatch-missing-task-plans.test.ts +49 -43
  197. package/src/resources/extensions/gsd/tests/dispatch-uat-last-completed.test.ts +32 -28
  198. package/src/resources/extensions/gsd/tests/doctor-completion-deferral.test.ts +29 -27
  199. package/src/resources/extensions/gsd/tests/doctor-delimiter-fix.test.ts +38 -34
  200. package/src/resources/extensions/gsd/tests/doctor-enhancements.test.ts +75 -54
  201. package/src/resources/extensions/gsd/tests/doctor-environment-worktree.test.ts +32 -21
  202. package/src/resources/extensions/gsd/tests/doctor-environment.test.ts +97 -72
  203. package/src/resources/extensions/gsd/tests/doctor-fixlevel.test.ts +44 -38
  204. package/src/resources/extensions/gsd/tests/doctor-git.test.ts +145 -104
  205. package/src/resources/extensions/gsd/tests/doctor-proactive.test.ts +106 -84
  206. package/src/resources/extensions/gsd/tests/doctor-roadmap-summary-atomicity.test.ts +60 -54
  207. package/src/resources/extensions/gsd/tests/doctor-runtime.test.ts +93 -72
  208. package/src/resources/extensions/gsd/tests/doctor.test.ts +134 -104
  209. package/src/resources/extensions/gsd/tests/ensure-db-open.test.ts +131 -123
  210. package/src/resources/extensions/gsd/tests/exit-command.test.ts +24 -20
  211. package/src/resources/extensions/gsd/tests/feature-branch-lifecycle-integration.test.ts +57 -48
  212. package/src/resources/extensions/gsd/tests/files-loadfile-eisdir.test.ts +7 -5
  213. package/src/resources/extensions/gsd/tests/flag-file-db.test.ts +42 -30
  214. package/src/resources/extensions/gsd/tests/freeform-decisions.test.ts +206 -198
  215. package/src/resources/extensions/gsd/tests/git-locale.test.ts +27 -13
  216. package/src/resources/extensions/gsd/tests/git-service.test.ts +388 -285
  217. package/src/resources/extensions/gsd/tests/gitignore-tracked-gsd.test.ts +39 -31
  218. package/src/resources/extensions/gsd/tests/graph-operations.test.ts +69 -63
  219. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +264 -255
  220. package/src/resources/extensions/gsd/tests/gsd-inspect.test.ts +119 -108
  221. package/src/resources/extensions/gsd/tests/gsd-recover.test.ts +103 -81
  222. package/src/resources/extensions/gsd/tests/gsd-tools.test.ts +262 -229
  223. package/src/resources/extensions/gsd/tests/headless-answers.test.ts +13 -13
  224. package/src/resources/extensions/gsd/tests/health-widget.test.ts +37 -29
  225. package/src/resources/extensions/gsd/tests/idle-recovery.test.ts +102 -81
  226. package/src/resources/extensions/gsd/tests/init-wizard.test.ts +18 -16
  227. package/src/resources/extensions/gsd/tests/integration-edge.test.ts +46 -41
  228. package/src/resources/extensions/gsd/tests/integration-lifecycle.test.ts +53 -42
  229. package/src/resources/extensions/gsd/tests/integration-mixed-milestones.test.ts +91 -75
  230. package/src/resources/extensions/gsd/tests/integration-proof.test.ts +18 -18
  231. package/src/resources/extensions/gsd/tests/markdown-renderer.test.ts +194 -150
  232. package/src/resources/extensions/gsd/tests/md-importer.test.ts +125 -101
  233. package/src/resources/extensions/gsd/tests/memory-extractor.test.ts +54 -45
  234. package/src/resources/extensions/gsd/tests/memory-store.test.ts +93 -80
  235. package/src/resources/extensions/gsd/tests/migrate-command.test.ts +66 -57
  236. package/src/resources/extensions/gsd/tests/migrate-hierarchy.test.ts +93 -83
  237. package/src/resources/extensions/gsd/tests/migrate-parser.test.ts +170 -161
  238. package/src/resources/extensions/gsd/tests/migrate-transformer.test.ts +141 -125
  239. package/src/resources/extensions/gsd/tests/migrate-validator-parsers.test.ts +131 -107
  240. package/src/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +96 -87
  241. package/src/resources/extensions/gsd/tests/migrate-writer.test.ts +164 -125
  242. package/src/resources/extensions/gsd/tests/must-have-parser.test.ts +94 -81
  243. package/src/resources/extensions/gsd/tests/none-mode-gates.test.ts +36 -35
  244. package/src/resources/extensions/gsd/tests/overrides.test.ts +106 -99
  245. package/src/resources/extensions/gsd/tests/parallel-crash-recovery.test.ts +47 -40
  246. package/src/resources/extensions/gsd/tests/parallel-worker-monitoring.test.ts +28 -25
  247. package/src/resources/extensions/gsd/tests/parallel-workers-multi-milestone-e2e.test.ts +83 -66
  248. package/src/resources/extensions/gsd/tests/park-edge-cases.test.ts +77 -54
  249. package/src/resources/extensions/gsd/tests/park-milestone.test.ts +115 -68
  250. package/src/resources/extensions/gsd/tests/parsers.test.ts +611 -546
  251. package/src/resources/extensions/gsd/tests/paths.test.ts +87 -72
  252. package/src/resources/extensions/gsd/tests/post-unit-hooks.test.ts +117 -77
  253. package/src/resources/extensions/gsd/tests/prompt-db.test.ts +56 -56
  254. package/src/resources/extensions/gsd/tests/queue-draft-detection.test.ts +119 -93
  255. package/src/resources/extensions/gsd/tests/queue-order.test.ts +82 -70
  256. package/src/resources/extensions/gsd/tests/queue-reorder-e2e.test.ts +55 -42
  257. package/src/resources/extensions/gsd/tests/quick-branch-lifecycle.test.ts +73 -45
  258. package/src/resources/extensions/gsd/tests/reassess-prompt.test.ts +38 -28
  259. package/src/resources/extensions/gsd/tests/replan-slice.test.ts +80 -73
  260. package/src/resources/extensions/gsd/tests/repo-identity-worktree.test.ts +74 -71
  261. package/src/resources/extensions/gsd/tests/requirements.test.ts +75 -70
  262. package/src/resources/extensions/gsd/tests/retry-state-reset.test.ts +66 -44
  263. package/src/resources/extensions/gsd/tests/roadmap-parse-regression.test.ts +181 -114
  264. package/src/resources/extensions/gsd/tests/rule-registry.test.ts +65 -63
  265. package/src/resources/extensions/gsd/tests/run-uat.test.ts +128 -66
  266. package/src/resources/extensions/gsd/tests/session-lock-multipath.test.ts +25 -18
  267. package/src/resources/extensions/gsd/tests/session-lock-regression.test.ts +44 -37
  268. package/src/resources/extensions/gsd/tests/shared-wal.test.ts +26 -19
  269. package/src/resources/extensions/gsd/tests/stalled-tool-recovery.test.ts +8 -6
  270. package/src/resources/extensions/gsd/tests/symlink-numbered-variants.test.ts +28 -22
  271. package/src/resources/extensions/gsd/tests/token-savings.test.ts +56 -54
  272. package/src/resources/extensions/gsd/tests/tool-call-loop-guard.test.ts +25 -23
  273. package/src/resources/extensions/gsd/tests/tool-naming.test.ts +11 -9
  274. package/src/resources/extensions/gsd/tests/unique-milestone-ids.test.ts +82 -66
  275. package/src/resources/extensions/gsd/tests/unit-runtime.test.ts +47 -46
  276. package/src/resources/extensions/gsd/tests/visualizer-critical-path.test.ts +22 -20
  277. package/src/resources/extensions/gsd/tests/visualizer-data.test.ts +86 -84
  278. package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +43 -41
  279. package/src/resources/extensions/gsd/tests/visualizer-views.test.ts +96 -94
  280. package/src/resources/extensions/gsd/tests/windows-path-normalization.test.ts +13 -11
  281. package/src/resources/extensions/gsd/tests/worker-registry.test.ts +29 -27
  282. package/src/resources/extensions/gsd/tests/workflow-templates.test.ts +52 -50
  283. package/src/resources/extensions/gsd/tests/worktree-bugfix.test.ts +13 -10
  284. package/src/resources/extensions/gsd/tests/worktree-db-integration.test.ts +18 -14
  285. package/src/resources/extensions/gsd/tests/worktree-db.test.ts +39 -38
  286. package/src/resources/extensions/gsd/tests/worktree-e2e.test.ts +21 -17
  287. package/src/resources/extensions/gsd/tests/worktree-health.test.ts +30 -25
  288. package/src/resources/extensions/gsd/tests/worktree-integration.test.ts +37 -30
  289. package/src/resources/extensions/gsd/tests/worktree-symlink-removal.test.ts +22 -15
  290. package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +66 -59
  291. package/src/resources/extensions/gsd/tests/worktree.test.ts +50 -44
  292. package/dist/web/standalone/.next/static/chunks/app/page-b9367c5ae13b99c6.js +0 -1
  293. package/dist/web/standalone/.next/static/chunks/main-app-fdab67f7802d7832.js +0 -1
  294. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-459824ffb8c323dd.js +0 -1
  295. package/src/resources/extensions/gsd/tests/quick-auto-guard.test.ts +0 -100
  296. package/src/resources/extensions/gsd/tests/sqlite-unavailable-gate.test.ts +0 -63
  297. /package/dist/web/standalone/.next/static/{-zps1Q9mQmioAKLcQiCr8 → mgkxN0mGP6gSUmGPEzbk_}/_buildManifest.js +0 -0
  298. /package/dist/web/standalone/.next/static/{-zps1Q9mQmioAKLcQiCr8 → mgkxN0mGP6gSUmGPEzbk_}/_ssgManifest.js +0 -0
@@ -1,10 +1,11 @@
1
- import { describe, test } from 'node:test';
2
- import assert from 'node:assert/strict';
3
1
  import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from 'node:fs';
4
2
  import { join } from 'node:path';
5
3
  import { tmpdir } from 'node:os';
6
4
 
7
5
  import { deriveState, isSliceComplete, isMilestoneComplete, isGhostMilestone } from '../state.ts';
6
+ import { createTestContext } from './test-helpers.ts';
7
+
8
+ const { assertEq, assertTrue, report } = createTestContext();
8
9
  // ─── Fixture Helpers ───────────────────────────────────────────────────────
9
10
 
10
11
  function createFixtureBase(): string {
@@ -64,28 +65,30 @@ function cleanup(base: string): void {
64
65
  // Test Groups
65
66
  // ═══════════════════════════════════════════════════════════════════════════
66
67
 
67
- describe('derive-state', async () => {
68
+ async function main(): Promise<void> {
68
69
 
69
70
  // ─── Test 1: empty milestones dir → pre-planning ───────────────────────
70
- test('empty milestones dir → pre-planning', async () => {
71
+ console.log('\n=== empty milestones dir → pre-planning ===');
72
+ {
71
73
  const base = createFixtureBase();
72
74
  try {
73
75
  const state = await deriveState(base);
74
76
 
75
- assert.deepStrictEqual(state.phase, 'pre-planning', 'phase is pre-planning');
76
- assert.deepStrictEqual(state.activeMilestone, null, 'activeMilestone is null');
77
- assert.deepStrictEqual(state.activeSlice, null, 'activeSlice is null');
78
- assert.deepStrictEqual(state.activeTask, null, 'activeTask is null');
79
- assert.deepStrictEqual(state.registry, [], 'registry is empty');
80
- assert.deepStrictEqual(state.progress?.milestones?.done, 0, 'milestones done = 0');
81
- assert.deepStrictEqual(state.progress?.milestones?.total, 0, 'milestones total = 0');
77
+ assertEq(state.phase, 'pre-planning', 'phase is pre-planning');
78
+ assertEq(state.activeMilestone, null, 'activeMilestone is null');
79
+ assertEq(state.activeSlice, null, 'activeSlice is null');
80
+ assertEq(state.activeTask, null, 'activeTask is null');
81
+ assertEq(state.registry, [], 'registry is empty');
82
+ assertEq(state.progress?.milestones?.done, 0, 'milestones done = 0');
83
+ assertEq(state.progress?.milestones?.total, 0, 'milestones total = 0');
82
84
  } finally {
83
85
  cleanup(base);
84
86
  }
85
- });
87
+ }
86
88
 
87
89
  // ─── Test 2: milestone dir exists but no roadmap → pre-planning ────────
88
- test('milestone dir exists but no roadmap → pre-planning', async () => {
90
+ console.log('\n=== milestone dir exists but no roadmap → pre-planning ===');
91
+ {
89
92
  const base = createFixtureBase();
90
93
  try {
91
94
  // Create M001 directory with CONTEXT but no roadmap file
@@ -94,20 +97,21 @@ describe('derive-state', async () => {
94
97
 
95
98
  const state = await deriveState(base);
96
99
 
97
- assert.deepStrictEqual(state.phase, 'pre-planning', 'phase is pre-planning');
98
- assert.ok(state.activeMilestone !== null, 'activeMilestone is not null');
99
- assert.deepStrictEqual(state.activeMilestone?.id, 'M001', 'activeMilestone id is M001');
100
- assert.deepStrictEqual(state.activeSlice, null, 'activeSlice is null');
101
- assert.deepStrictEqual(state.activeTask, null, 'activeTask is null');
102
- assert.deepStrictEqual(state.registry.length, 1, 'registry has 1 entry');
103
- assert.deepStrictEqual(state.registry[0]?.status, 'active', 'registry entry status is active');
100
+ assertEq(state.phase, 'pre-planning', 'phase is pre-planning');
101
+ assertTrue(state.activeMilestone !== null, 'activeMilestone is not null');
102
+ assertEq(state.activeMilestone?.id, 'M001', 'activeMilestone id is M001');
103
+ assertEq(state.activeSlice, null, 'activeSlice is null');
104
+ assertEq(state.activeTask, null, 'activeTask is null');
105
+ assertEq(state.registry.length, 1, 'registry has 1 entry');
106
+ assertEq(state.registry[0]?.status, 'active', 'registry entry status is active');
104
107
  } finally {
105
108
  cleanup(base);
106
109
  }
107
- });
110
+ }
108
111
 
109
112
  // ─── Test 3: roadmap with incomplete slice, no plan → planning ─────────
110
- test('roadmap with incomplete slice, no plan → planning', async () => {
113
+ console.log('\n=== roadmap with incomplete slice, no plan → planning ===');
114
+ {
111
115
  const base = createFixtureBase();
112
116
  try {
113
117
  writeRoadmap(base, 'M001', `# M001: Test Milestone
@@ -122,19 +126,20 @@ describe('derive-state', async () => {
122
126
 
123
127
  const state = await deriveState(base);
124
128
 
125
- assert.deepStrictEqual(state.phase, 'planning', 'phase is planning');
126
- assert.ok(state.activeSlice !== null, 'activeSlice is not null');
127
- assert.deepStrictEqual(state.activeSlice?.id, 'S01', 'activeSlice id is S01');
128
- assert.deepStrictEqual(state.activeTask, null, 'activeTask is null');
129
- assert.deepStrictEqual(state.progress?.slices?.done, 0, 'slices done = 0');
130
- assert.deepStrictEqual(state.progress?.slices?.total, 1, 'slices total = 1');
129
+ assertEq(state.phase, 'planning', 'phase is planning');
130
+ assertTrue(state.activeSlice !== null, 'activeSlice is not null');
131
+ assertEq(state.activeSlice?.id, 'S01', 'activeSlice id is S01');
132
+ assertEq(state.activeTask, null, 'activeTask is null');
133
+ assertEq(state.progress?.slices?.done, 0, 'slices done = 0');
134
+ assertEq(state.progress?.slices?.total, 1, 'slices total = 1');
131
135
  } finally {
132
136
  cleanup(base);
133
137
  }
134
- });
138
+ }
135
139
 
136
140
  // ─── Test 4: roadmap + plan with incomplete tasks → executing ──────────
137
- test('roadmap + plan with incomplete tasks → executing', async () => {
141
+ console.log('\n=== roadmap + plan with incomplete tasks → executing ===');
142
+ {
138
143
  const base = createFixtureBase();
139
144
  try {
140
145
  writeRoadmap(base, 'M001', `# M001: Test Milestone
@@ -163,18 +168,19 @@ describe('derive-state', async () => {
163
168
 
164
169
  const state = await deriveState(base);
165
170
 
166
- assert.deepStrictEqual(state.phase, 'executing', 'phase is executing');
167
- assert.ok(state.activeTask !== null, 'activeTask is not null');
168
- assert.deepStrictEqual(state.activeTask?.id, 'T01', 'activeTask id is T01');
169
- assert.deepStrictEqual(state.progress?.tasks?.done, 0, 'tasks done = 0');
170
- assert.deepStrictEqual(state.progress?.tasks?.total, 2, 'tasks total = 2');
171
+ assertEq(state.phase, 'executing', 'phase is executing');
172
+ assertTrue(state.activeTask !== null, 'activeTask is not null');
173
+ assertEq(state.activeTask?.id, 'T01', 'activeTask id is T01');
174
+ assertEq(state.progress?.tasks?.done, 0, 'tasks done = 0');
175
+ assertEq(state.progress?.tasks?.total, 2, 'tasks total = 2');
171
176
  } finally {
172
177
  cleanup(base);
173
178
  }
174
- });
179
+ }
175
180
 
176
181
  // ─── Test 5: executing + continue file → resume message ─────────────
177
- test('executing + continue file → resume message', async () => {
182
+ console.log('\n=== executing + continue file → resume message ===');
183
+ {
178
184
  const base = createFixtureBase();
179
185
  try {
180
186
  writeRoadmap(base, 'M001', `# M001: Test Milestone
@@ -222,20 +228,21 @@ Continue from step 2.
222
228
 
223
229
  const state = await deriveState(base);
224
230
 
225
- assert.deepStrictEqual(state.phase, 'executing', 'interrupted: phase is executing');
226
- assert.ok(state.activeTask !== null, 'interrupted: activeTask is not null');
227
- assert.deepStrictEqual(state.activeTask?.id, 'T01', 'interrupted: activeTask id is T01');
228
- assert.ok(
231
+ assertEq(state.phase, 'executing', 'interrupted: phase is executing');
232
+ assertTrue(state.activeTask !== null, 'interrupted: activeTask is not null');
233
+ assertEq(state.activeTask?.id, 'T01', 'interrupted: activeTask id is T01');
234
+ assertTrue(
229
235
  state.nextAction.includes('Resume') || state.nextAction.includes('resume') || state.nextAction.includes('continue.md'),
230
236
  'interrupted: nextAction mentions Resume/resume/continue.md'
231
237
  );
232
238
  } finally {
233
239
  cleanup(base);
234
240
  }
235
- });
241
+ }
236
242
 
237
243
  // ─── Test 6: all tasks done, slice not [x] → summarizing ──────────────
238
- test('all tasks done, slice not [x] → summarizing', async () => {
244
+ console.log('\n=== all tasks done, slice not [x] → summarizing ===');
245
+ {
239
246
  const base = createFixtureBase();
240
247
  try {
241
248
  writeRoadmap(base, 'M001', `# M001: Test Milestone
@@ -264,23 +271,24 @@ Continue from step 2.
264
271
 
265
272
  const state = await deriveState(base);
266
273
 
267
- assert.deepStrictEqual(state.phase, 'summarizing', 'summarizing: phase is summarizing');
268
- assert.ok(state.activeSlice !== null, 'summarizing: activeSlice is not null');
269
- assert.deepStrictEqual(state.activeSlice?.id, 'S01', 'summarizing: activeSlice id is S01');
270
- assert.deepStrictEqual(state.activeTask, null, 'summarizing: activeTask is null');
271
- assert.ok(
274
+ assertEq(state.phase, 'summarizing', 'summarizing: phase is summarizing');
275
+ assertTrue(state.activeSlice !== null, 'summarizing: activeSlice is not null');
276
+ assertEq(state.activeSlice?.id, 'S01', 'summarizing: activeSlice id is S01');
277
+ assertEq(state.activeTask, null, 'summarizing: activeTask is null');
278
+ assertTrue(
272
279
  state.nextAction.toLowerCase().includes('summary') || state.nextAction.toLowerCase().includes('complete'),
273
280
  'summarizing: nextAction mentions summary or complete'
274
281
  );
275
- assert.deepStrictEqual(state.progress?.tasks?.done, 2, 'summarizing: tasks done = 2');
276
- assert.deepStrictEqual(state.progress?.tasks?.total, 2, 'summarizing: tasks total = 2');
282
+ assertEq(state.progress?.tasks?.done, 2, 'summarizing: tasks done = 2');
283
+ assertEq(state.progress?.tasks?.total, 2, 'summarizing: tasks total = 2');
277
284
  } finally {
278
285
  cleanup(base);
279
286
  }
280
- });
287
+ }
281
288
 
282
289
  // ─── Test 7: all milestones complete → complete ────────────────────────
283
- test('all milestones complete → complete', async () => {
290
+ console.log('\n=== all milestones complete → complete ===');
291
+ {
284
292
  const base = createFixtureBase();
285
293
  try {
286
294
  writeRoadmap(base, 'M001', `# M001: Test Milestone
@@ -298,22 +306,23 @@ Continue from step 2.
298
306
 
299
307
  const state = await deriveState(base);
300
308
 
301
- assert.deepStrictEqual(state.phase, 'complete', 'complete: phase is complete');
302
- assert.deepStrictEqual(state.activeSlice, null, 'complete: activeSlice is null');
303
- assert.deepStrictEqual(state.activeTask, null, 'complete: activeTask is null');
304
- assert.ok(
309
+ assertEq(state.phase, 'complete', 'complete: phase is complete');
310
+ assertEq(state.activeSlice, null, 'complete: activeSlice is null');
311
+ assertEq(state.activeTask, null, 'complete: activeTask is null');
312
+ assertTrue(
305
313
  state.nextAction.toLowerCase().includes('complete'),
306
314
  'complete: nextAction mentions complete'
307
315
  );
308
- assert.deepStrictEqual(state.registry.length, 1, 'complete: registry has 1 entry');
309
- assert.deepStrictEqual(state.registry[0]?.status, 'complete', 'complete: registry[0] status is complete');
316
+ assertEq(state.registry.length, 1, 'complete: registry has 1 entry');
317
+ assertEq(state.registry[0]?.status, 'complete', 'complete: registry[0] status is complete');
310
318
  } finally {
311
319
  cleanup(base);
312
320
  }
313
- });
321
+ }
314
322
 
315
323
  // ─── Test 7b: complete with active requirements → surfaces unmapped reqs ──
316
- test('complete with active requirements → surfaces unmapped reqs', async () => {
324
+ console.log('\n=== complete with active requirements → surfaces unmapped reqs ===');
325
+ {
317
326
  const base = createFixtureBase();
318
327
  try {
319
328
  writeRoadmap(base, 'M001', `# M001: Test Milestone
@@ -346,22 +355,23 @@ Continue from step 2.
346
355
 
347
356
  const state = await deriveState(base);
348
357
 
349
- assert.deepStrictEqual(state.phase, 'complete', 'complete-with-reqs: phase is complete');
350
- assert.ok(
358
+ assertEq(state.phase, 'complete', 'complete-with-reqs: phase is complete');
359
+ assertTrue(
351
360
  state.nextAction.includes('2 active requirements'),
352
361
  'complete-with-reqs: nextAction mentions 2 active requirements'
353
362
  );
354
- assert.ok(
363
+ assertTrue(
355
364
  state.nextAction.includes('REQUIREMENTS.md'),
356
365
  'complete-with-reqs: nextAction mentions REQUIREMENTS.md'
357
366
  );
358
367
  } finally {
359
368
  cleanup(base);
360
369
  }
361
- });
370
+ }
362
371
 
363
372
  // ─── Test 7c: complete with no active requirements → standard message ──
364
- test('complete with no active requirements → standard message', async () => {
373
+ console.log('\n=== complete with no active requirements → standard message ===');
374
+ {
365
375
  const base = createFixtureBase();
366
376
  try {
367
377
  writeRoadmap(base, 'M001', `# M001: Test Milestone
@@ -386,15 +396,16 @@ Continue from step 2.
386
396
 
387
397
  const state = await deriveState(base);
388
398
 
389
- assert.deepStrictEqual(state.phase, 'complete', 'complete-no-active-reqs: phase is complete');
390
- assert.deepStrictEqual(state.nextAction, 'All milestones complete.', 'complete-no-active-reqs: standard completion message');
399
+ assertEq(state.phase, 'complete', 'complete-no-active-reqs: phase is complete');
400
+ assertEq(state.nextAction, 'All milestones complete.', 'complete-no-active-reqs: standard completion message');
391
401
  } finally {
392
402
  cleanup(base);
393
403
  }
394
- });
404
+ }
395
405
 
396
406
  // ─── Test 8: blocked dependencies ──────────────────────────────────────
397
- test('blocked dependencies', async () => {
407
+ console.log('\n=== blocked dependencies ===');
408
+ {
398
409
  // Case A: S01 active (deps satisfied), S02 blocked on S01
399
410
  const base1 = createFixtureBase();
400
411
  try {
@@ -425,8 +436,8 @@ Continue from step 2.
425
436
 
426
437
  const state1 = await deriveState(base1);
427
438
 
428
- assert.deepStrictEqual(state1.phase, 'executing', 'blocked-A: phase is executing (S01 active)');
429
- assert.deepStrictEqual(state1.activeSlice?.id, 'S01', 'blocked-A: activeSlice is S01');
439
+ assertEq(state1.phase, 'executing', 'blocked-A: phase is executing (S01 active)');
440
+ assertEq(state1.activeSlice?.id, 'S01', 'blocked-A: activeSlice is S01');
430
441
  } finally {
431
442
  cleanup(base1);
432
443
  }
@@ -446,16 +457,17 @@ Continue from step 2.
446
457
 
447
458
  const state2 = await deriveState(base2);
448
459
 
449
- assert.deepStrictEqual(state2.phase, 'blocked', 'blocked-B: phase is blocked');
450
- assert.deepStrictEqual(state2.activeSlice, null, 'blocked-B: activeSlice is null');
451
- assert.ok(state2.blockers.length > 0, 'blocked-B: blockers array is non-empty');
460
+ assertEq(state2.phase, 'blocked', 'blocked-B: phase is blocked');
461
+ assertEq(state2.activeSlice, null, 'blocked-B: activeSlice is null');
462
+ assertTrue(state2.blockers.length > 0, 'blocked-B: blockers array is non-empty');
452
463
  } finally {
453
464
  cleanup(base2);
454
465
  }
455
- });
466
+ }
456
467
 
457
468
  // ─── Test 9: multi-milestone registry ──────────────────────────────────
458
- test('multi-milestone registry', async () => {
469
+ console.log('\n=== multi-milestone registry ===');
470
+ {
459
471
  const base = createFixtureBase();
460
472
  try {
461
473
  // M001: complete (all slices done)
@@ -489,23 +501,24 @@ Continue from step 2.
489
501
 
490
502
  const state = await deriveState(base);
491
503
 
492
- assert.deepStrictEqual(state.registry.length, 3, 'multi-ms: registry has 3 entries');
493
- assert.deepStrictEqual(state.registry[0]?.id, 'M001', 'multi-ms: registry[0] is M001');
494
- assert.deepStrictEqual(state.registry[0]?.status, 'complete', 'multi-ms: M001 is complete');
495
- assert.deepStrictEqual(state.registry[1]?.id, 'M002', 'multi-ms: registry[1] is M002');
496
- assert.deepStrictEqual(state.registry[1]?.status, 'active', 'multi-ms: M002 is active');
497
- assert.deepStrictEqual(state.registry[2]?.id, 'M003', 'multi-ms: registry[2] is M003');
498
- assert.deepStrictEqual(state.registry[2]?.status, 'pending', 'multi-ms: M003 is pending');
499
- assert.deepStrictEqual(state.activeMilestone?.id, 'M002', 'multi-ms: activeMilestone is M002');
500
- assert.deepStrictEqual(state.progress?.milestones?.done, 1, 'multi-ms: milestones done = 1');
501
- assert.deepStrictEqual(state.progress?.milestones?.total, 3, 'multi-ms: milestones total = 3');
504
+ assertEq(state.registry.length, 3, 'multi-ms: registry has 3 entries');
505
+ assertEq(state.registry[0]?.id, 'M001', 'multi-ms: registry[0] is M001');
506
+ assertEq(state.registry[0]?.status, 'complete', 'multi-ms: M001 is complete');
507
+ assertEq(state.registry[1]?.id, 'M002', 'multi-ms: registry[1] is M002');
508
+ assertEq(state.registry[1]?.status, 'active', 'multi-ms: M002 is active');
509
+ assertEq(state.registry[2]?.id, 'M003', 'multi-ms: registry[2] is M003');
510
+ assertEq(state.registry[2]?.status, 'pending', 'multi-ms: M003 is pending');
511
+ assertEq(state.activeMilestone?.id, 'M002', 'multi-ms: activeMilestone is M002');
512
+ assertEq(state.progress?.milestones?.done, 1, 'multi-ms: milestones done = 1');
513
+ assertEq(state.progress?.milestones?.total, 3, 'multi-ms: milestones total = 3');
502
514
  } finally {
503
515
  cleanup(base);
504
516
  }
505
- });
517
+ }
506
518
 
507
519
  // ─── Test 10: requirements integration ─────────────────────────────────
508
- test('requirements integration', async () => {
520
+ console.log('\n=== requirements integration ===');
521
+ {
509
522
  const base = createFixtureBase();
510
523
  try {
511
524
  writeRequirements(base, `# Requirements
@@ -546,19 +559,20 @@ Continue from step 2.
546
559
  // Need at least an empty milestones dir for deriveState
547
560
  const state = await deriveState(base);
548
561
 
549
- assert.ok(state.requirements !== undefined, 'requirements: requirements object exists');
550
- assert.deepStrictEqual(state.requirements?.active, 2, 'requirements: active = 2');
551
- assert.deepStrictEqual(state.requirements?.validated, 1, 'requirements: validated = 1');
552
- assert.deepStrictEqual(state.requirements?.deferred, 2, 'requirements: deferred = 2');
553
- assert.deepStrictEqual(state.requirements?.outOfScope, 1, 'requirements: outOfScope = 1');
554
- assert.deepStrictEqual(state.requirements?.total, 6, 'requirements: total = 6 (sum of all)');
562
+ assertTrue(state.requirements !== undefined, 'requirements: requirements object exists');
563
+ assertEq(state.requirements?.active, 2, 'requirements: active = 2');
564
+ assertEq(state.requirements?.validated, 1, 'requirements: validated = 1');
565
+ assertEq(state.requirements?.deferred, 2, 'requirements: deferred = 2');
566
+ assertEq(state.requirements?.outOfScope, 1, 'requirements: outOfScope = 1');
567
+ assertEq(state.requirements?.total, 6, 'requirements: total = 6 (sum of all)');
555
568
  } finally {
556
569
  cleanup(base);
557
570
  }
558
- });
571
+ }
559
572
 
560
573
  // ─── Test 11: all slices [x], no summary → completing-milestone ────────
561
- test('all slices [x], no summary → completing-milestone', async () => {
574
+ console.log('\n=== all slices [x], no summary → completing-milestone ===');
575
+ {
562
576
  const base = createFixtureBase();
563
577
  try {
564
578
  writeRoadmap(base, 'M001', `# M001: Test Milestone
@@ -578,26 +592,27 @@ Continue from step 2.
578
592
 
579
593
  const state = await deriveState(base);
580
594
 
581
- assert.deepStrictEqual(state.phase, 'completing-milestone', 'completing-ms: phase is completing-milestone');
582
- assert.ok(state.activeMilestone !== null, 'completing-ms: activeMilestone is not null');
583
- assert.deepStrictEqual(state.activeMilestone?.id, 'M001', 'completing-ms: activeMilestone id is M001');
584
- assert.deepStrictEqual(state.activeSlice, null, 'completing-ms: activeSlice is null');
585
- assert.deepStrictEqual(state.activeTask, null, 'completing-ms: activeTask is null');
586
- assert.deepStrictEqual(state.registry.length, 1, 'completing-ms: registry has 1 entry');
587
- assert.deepStrictEqual(state.registry[0]?.status, 'active', 'completing-ms: registry[0] status is active (not complete)');
588
- assert.deepStrictEqual(state.progress?.slices?.done, 2, 'completing-ms: slices done = 2');
589
- assert.deepStrictEqual(state.progress?.slices?.total, 2, 'completing-ms: slices total = 2');
590
- assert.ok(
595
+ assertEq(state.phase, 'completing-milestone', 'completing-ms: phase is completing-milestone');
596
+ assertTrue(state.activeMilestone !== null, 'completing-ms: activeMilestone is not null');
597
+ assertEq(state.activeMilestone?.id, 'M001', 'completing-ms: activeMilestone id is M001');
598
+ assertEq(state.activeSlice, null, 'completing-ms: activeSlice is null');
599
+ assertEq(state.activeTask, null, 'completing-ms: activeTask is null');
600
+ assertEq(state.registry.length, 1, 'completing-ms: registry has 1 entry');
601
+ assertEq(state.registry[0]?.status, 'active', 'completing-ms: registry[0] status is active (not complete)');
602
+ assertEq(state.progress?.slices?.done, 2, 'completing-ms: slices done = 2');
603
+ assertEq(state.progress?.slices?.total, 2, 'completing-ms: slices total = 2');
604
+ assertTrue(
591
605
  state.nextAction.toLowerCase().includes('summary') || state.nextAction.toLowerCase().includes('complete'),
592
606
  'completing-ms: nextAction mentions summary or complete'
593
607
  );
594
608
  } finally {
595
609
  cleanup(base);
596
610
  }
597
- });
611
+ }
598
612
 
599
613
  // ─── Test 12: all slices [x], summary exists → complete ───────────────
600
- test('all slices [x], summary exists → complete', async () => {
614
+ console.log('\n=== all slices [x], summary exists → complete ===');
615
+ {
601
616
  const base = createFixtureBase();
602
617
  try {
603
618
  writeRoadmap(base, 'M001', `# M001: Test Milestone
@@ -615,18 +630,19 @@ Continue from step 2.
615
630
 
616
631
  const state = await deriveState(base);
617
632
 
618
- assert.deepStrictEqual(state.phase, 'complete', 'summary-exists: phase is complete');
619
- assert.deepStrictEqual(state.registry.length, 1, 'summary-exists: registry has 1 entry');
620
- assert.deepStrictEqual(state.registry[0]?.status, 'complete', 'summary-exists: registry[0] status is complete');
621
- assert.deepStrictEqual(state.activeSlice, null, 'summary-exists: activeSlice is null');
622
- assert.deepStrictEqual(state.activeTask, null, 'summary-exists: activeTask is null');
633
+ assertEq(state.phase, 'complete', 'summary-exists: phase is complete');
634
+ assertEq(state.registry.length, 1, 'summary-exists: registry has 1 entry');
635
+ assertEq(state.registry[0]?.status, 'complete', 'summary-exists: registry[0] status is complete');
636
+ assertEq(state.activeSlice, null, 'summary-exists: activeSlice is null');
637
+ assertEq(state.activeTask, null, 'summary-exists: activeTask is null');
623
638
  } finally {
624
639
  cleanup(base);
625
640
  }
626
- });
641
+ }
627
642
 
628
643
  // ─── Test 13: multi-milestone completing-milestone ─────────────────────
629
- test('multi-milestone completing-milestone', async () => {
644
+ console.log('\n=== multi-milestone completing-milestone ===');
645
+ {
630
646
  const base = createFixtureBase();
631
647
  try {
632
648
  // M001: all slices done + summary exists → complete
@@ -671,28 +687,29 @@ Continue from step 2.
671
687
 
672
688
  const state = await deriveState(base);
673
689
 
674
- assert.deepStrictEqual(state.phase, 'completing-milestone', 'multi-completing: phase is completing-milestone');
675
- assert.deepStrictEqual(state.activeMilestone?.id, 'M002', 'multi-completing: activeMilestone is M002');
676
- assert.deepStrictEqual(state.activeSlice, null, 'multi-completing: activeSlice is null');
677
- assert.deepStrictEqual(state.activeTask, null, 'multi-completing: activeTask is null');
678
- assert.deepStrictEqual(state.registry.length, 3, 'multi-completing: registry has 3 entries');
679
- assert.deepStrictEqual(state.registry[0]?.id, 'M001', 'multi-completing: registry[0] is M001');
680
- assert.deepStrictEqual(state.registry[0]?.status, 'complete', 'multi-completing: M001 is complete');
681
- assert.deepStrictEqual(state.registry[1]?.id, 'M002', 'multi-completing: registry[1] is M002');
682
- assert.deepStrictEqual(state.registry[1]?.status, 'active', 'multi-completing: M002 is active (completing-milestone)');
683
- assert.deepStrictEqual(state.registry[2]?.id, 'M003', 'multi-completing: registry[2] is M003');
684
- assert.deepStrictEqual(state.registry[2]?.status, 'pending', 'multi-completing: M003 is pending');
685
- assert.deepStrictEqual(state.progress?.milestones?.done, 1, 'multi-completing: milestones done = 1');
686
- assert.deepStrictEqual(state.progress?.milestones?.total, 3, 'multi-completing: milestones total = 3');
687
- assert.deepStrictEqual(state.progress?.slices?.done, 2, 'multi-completing: slices done = 2');
688
- assert.deepStrictEqual(state.progress?.slices?.total, 2, 'multi-completing: slices total = 2');
690
+ assertEq(state.phase, 'completing-milestone', 'multi-completing: phase is completing-milestone');
691
+ assertEq(state.activeMilestone?.id, 'M002', 'multi-completing: activeMilestone is M002');
692
+ assertEq(state.activeSlice, null, 'multi-completing: activeSlice is null');
693
+ assertEq(state.activeTask, null, 'multi-completing: activeTask is null');
694
+ assertEq(state.registry.length, 3, 'multi-completing: registry has 3 entries');
695
+ assertEq(state.registry[0]?.id, 'M001', 'multi-completing: registry[0] is M001');
696
+ assertEq(state.registry[0]?.status, 'complete', 'multi-completing: M001 is complete');
697
+ assertEq(state.registry[1]?.id, 'M002', 'multi-completing: registry[1] is M002');
698
+ assertEq(state.registry[1]?.status, 'active', 'multi-completing: M002 is active (completing-milestone)');
699
+ assertEq(state.registry[2]?.id, 'M003', 'multi-completing: registry[2] is M003');
700
+ assertEq(state.registry[2]?.status, 'pending', 'multi-completing: M003 is pending');
701
+ assertEq(state.progress?.milestones?.done, 1, 'multi-completing: milestones done = 1');
702
+ assertEq(state.progress?.milestones?.total, 3, 'multi-completing: milestones total = 3');
703
+ assertEq(state.progress?.slices?.done, 2, 'multi-completing: slices done = 2');
704
+ assertEq(state.progress?.slices?.total, 2, 'multi-completing: slices total = 2');
689
705
  } finally {
690
706
  cleanup(base);
691
707
  }
692
- });
708
+ }
693
709
 
694
710
  // ═══ Milestone with summary but no roadmap → complete ═══════════════════
695
711
  {
712
+ console.log('\n=== milestone with summary and no roadmap → complete ===');
696
713
  const base = createFixtureBase();
697
714
  try {
698
715
  // M001, M002: completed milestones with summaries but no roadmaps
@@ -709,17 +726,17 @@ Continue from step 2.
709
726
 
710
727
  const state = await deriveState(base);
711
728
 
712
- assert.deepStrictEqual(state.phase, 'planning', 'summary-no-roadmap: phase is planning (active is M003)');
713
- assert.deepStrictEqual(state.activeMilestone?.id, 'M003', 'summary-no-roadmap: active milestone is M003');
714
- assert.deepStrictEqual(state.activeMilestone?.title, 'Polish', 'summary-no-roadmap: active title is Polish');
715
- assert.deepStrictEqual(state.registry.length, 3, 'summary-no-roadmap: registry has 3 entries');
716
- assert.deepStrictEqual(state.registry[0]?.status, 'complete', 'summary-no-roadmap: M001 is complete');
717
- assert.deepStrictEqual(state.registry[0]?.title, 'Bootstrap', 'summary-no-roadmap: M001 title from summary');
718
- assert.deepStrictEqual(state.registry[1]?.status, 'complete', 'summary-no-roadmap: M002 is complete');
719
- assert.deepStrictEqual(state.registry[1]?.title, 'Core Features', 'summary-no-roadmap: M002 title from summary');
720
- assert.deepStrictEqual(state.registry[2]?.status, 'active', 'summary-no-roadmap: M003 is active');
721
- assert.deepStrictEqual(state.progress?.milestones?.done, 2, 'summary-no-roadmap: milestones done = 2');
722
- assert.deepStrictEqual(state.progress?.milestones?.total, 3, 'summary-no-roadmap: milestones total = 3');
729
+ assertEq(state.phase, 'planning', 'summary-no-roadmap: phase is planning (active is M003)');
730
+ assertEq(state.activeMilestone?.id, 'M003', 'summary-no-roadmap: active milestone is M003');
731
+ assertEq(state.activeMilestone?.title, 'Polish', 'summary-no-roadmap: active title is Polish');
732
+ assertEq(state.registry.length, 3, 'summary-no-roadmap: registry has 3 entries');
733
+ assertEq(state.registry[0]?.status, 'complete', 'summary-no-roadmap: M001 is complete');
734
+ assertEq(state.registry[0]?.title, 'Bootstrap', 'summary-no-roadmap: M001 title from summary');
735
+ assertEq(state.registry[1]?.status, 'complete', 'summary-no-roadmap: M002 is complete');
736
+ assertEq(state.registry[1]?.title, 'Core Features', 'summary-no-roadmap: M002 title from summary');
737
+ assertEq(state.registry[2]?.status, 'active', 'summary-no-roadmap: M003 is active');
738
+ assertEq(state.progress?.milestones?.done, 2, 'summary-no-roadmap: milestones done = 2');
739
+ assertEq(state.progress?.milestones?.total, 3, 'summary-no-roadmap: milestones total = 3');
723
740
  } finally {
724
741
  cleanup(base);
725
742
  }
@@ -727,6 +744,7 @@ Continue from step 2.
727
744
 
728
745
  // ═══ All milestones have summary but no roadmap → complete ═════════════
729
746
  {
747
+ console.log('\n=== all milestones summary-only → complete ===');
730
748
  const base = createFixtureBase();
731
749
  try {
732
750
  const m1dir = join(base, '.gsd', 'milestones', 'M001');
@@ -734,15 +752,16 @@ Continue from step 2.
734
752
  writeFileSync(join(m1dir, 'M001-SUMMARY.md'), '---\ntitle: Done\n---\nAll done.');
735
753
 
736
754
  const state = await deriveState(base);
737
- assert.deepStrictEqual(state.phase, 'complete', 'all-summary-only: phase is complete');
738
- assert.deepStrictEqual(state.registry[0]?.status, 'complete', 'all-summary-only: M001 is complete');
755
+ assertEq(state.phase, 'complete', 'all-summary-only: phase is complete');
756
+ assertEq(state.registry[0]?.status, 'complete', 'all-summary-only: M001 is complete');
739
757
  } finally {
740
758
  cleanup(base);
741
759
  }
742
760
  }
743
761
 
744
762
  // ─── Empty plan (zero tasks) stays in planning, not summarizing (#454) ──
745
- test('empty plan → planning (not summarizing)', async () => {
763
+ console.log('\n=== empty plan → planning (not summarizing) ===');
764
+ {
746
765
  const base = createFixtureBase();
747
766
  try {
748
767
  writeRoadmap(base, 'M001', `---
@@ -767,16 +786,17 @@ slice: S01
767
786
  ## Tasks
768
787
  `);
769
788
  const state = await deriveState(base);
770
- assert.deepStrictEqual(state.phase, 'planning', 'empty plan stays in planning');
771
- assert.deepStrictEqual(state.activeSlice?.id, 'S01', 'active slice is S01');
772
- assert.deepStrictEqual(state.activeTask, null, 'no active task');
789
+ assertEq(state.phase, 'planning', 'empty plan stays in planning');
790
+ assertEq(state.activeSlice?.id, 'S01', 'active slice is S01');
791
+ assertEq(state.activeTask, null, 'no active task');
773
792
  } finally {
774
793
  cleanup(base);
775
794
  }
776
- });
795
+ }
777
796
 
778
797
  // ─── Test: completed M001 (summary, no validation) skipped for active M003 (#864) ────
779
- test('completed milestone with summary but no validation is not active (#864)', async () => {
798
+ console.log('\n=== completed milestone with summary but no validation is not active (#864) ===');
799
+ {
780
800
  const base = createFixtureBase();
781
801
  try {
782
802
  // M001: all slices done, has summary, no validation
@@ -786,16 +806,17 @@ slice: S01
786
806
  writeRoadmap(base, 'M003', `# M003: Active Milestone\n\n**Vision:** Do stuff.\n\n## Slices\n\n- [ ] **S01: Work slice** \`risk:low\` \`depends:[]\`\n > Needs work.\n`);
787
807
 
788
808
  const state = await deriveState(base);
789
- assert.deepStrictEqual(state.activeMilestone?.id, 'M003', 'active milestone is M003, not completed M001');
809
+ assertEq(state.activeMilestone?.id, 'M003', 'active milestone is M003, not completed M001');
790
810
  const m001Entry = state.registry.find(e => e.id === 'M001');
791
- assert.deepStrictEqual(m001Entry?.status, 'complete', 'M001 is marked complete despite no validation');
811
+ assertEq(m001Entry?.status, 'complete', 'M001 is marked complete despite no validation');
792
812
  } finally {
793
813
  cleanup(base);
794
814
  }
795
- });
815
+ }
796
816
 
797
817
  // ─── Test: completed M001 with summary AND validation is complete (#864) ────
798
- test('completed milestone with summary and validation is complete', async () => {
818
+ console.log('\n=== completed milestone with summary and validation is complete ===');
819
+ {
799
820
  const base = createFixtureBase();
800
821
  try {
801
822
  writeRoadmap(base, 'M001', `# M001: First Milestone\n\n**Vision:** Done.\n\n## Slices\n\n- [x] **S01: Done slice** \`risk:low\` \`depends:[]\`\n > Completed.\n`);
@@ -804,30 +825,32 @@ slice: S01
804
825
  writeRoadmap(base, 'M003', `# M003: Active Milestone\n\n**Vision:** Do stuff.\n\n## Slices\n\n- [ ] **S01: Work slice** \`risk:low\` \`depends:[]\`\n > Needs work.\n`);
805
826
 
806
827
  const state = await deriveState(base);
807
- assert.deepStrictEqual(state.activeMilestone?.id, 'M003', 'active milestone is M003');
828
+ assertEq(state.activeMilestone?.id, 'M003', 'active milestone is M003');
808
829
  const m001Entry = state.registry.find(e => e.id === 'M001');
809
- assert.deepStrictEqual(m001Entry?.status, 'complete', 'M001 with both summary and validation is complete');
830
+ assertEq(m001Entry?.status, 'complete', 'M001 with both summary and validation is complete');
810
831
  } finally {
811
832
  cleanup(base);
812
833
  }
813
- });
834
+ }
814
835
 
815
836
  // ─── Test: all slices done, no summary, no validation → needs validation (#864) ────
816
- test('all slices done, no summary, no validation → validating-milestone', async () => {
837
+ console.log('\n=== all slices done, no summary, no validation → validating-milestone ===');
838
+ {
817
839
  const base = createFixtureBase();
818
840
  try {
819
841
  writeRoadmap(base, 'M001', `# M001: First Milestone\n\n**Vision:** Validate me.\n\n## Slices\n\n- [x] **S01: Done slice** \`risk:low\` \`depends:[]\`\n > Completed.\n`);
820
842
  // No summary, no validation — this should be active for validation
821
843
 
822
844
  const state = await deriveState(base);
823
- assert.deepStrictEqual(state.activeMilestone?.id, 'M001', 'M001 is active for validation');
845
+ assertEq(state.activeMilestone?.id, 'M001', 'M001 is active for validation');
824
846
  } finally {
825
847
  cleanup(base);
826
848
  }
827
- });
849
+ }
828
850
 
829
851
  // ─── Test: all slices done, validation pass, no summary → needs completion (#864) ────
830
- test('all slices done, validation pass, no summary → completing-milestone', async () => {
852
+ console.log('\n=== all slices done, validation pass, no summary → completing-milestone ===');
853
+ {
831
854
  const base = createFixtureBase();
832
855
  try {
833
856
  writeRoadmap(base, 'M001', `# M001: First Milestone\n\n**Vision:** Complete me.\n\n## Slices\n\n- [x] **S01: Done slice** \`risk:low\` \`depends:[]\`\n > Completed.\n`);
@@ -835,14 +858,15 @@ slice: S01
835
858
  // No summary — validated but not yet completed
836
859
 
837
860
  const state = await deriveState(base);
838
- assert.deepStrictEqual(state.activeMilestone?.id, 'M001', 'M001 is active for completion');
861
+ assertEq(state.activeMilestone?.id, 'M001', 'M001 is active for completion');
839
862
  } finally {
840
863
  cleanup(base);
841
864
  }
842
- });
865
+ }
843
866
 
844
867
  // ─── Test: unchecked roadmap slices + summary → complete (summary is terminal) ────
845
- test('unchecked roadmap slices + summary → complete (summary is terminal)', async () => {
868
+ console.log('\n=== unchecked roadmap slices + summary → complete (summary is terminal) ===');
869
+ {
846
870
  const base = createFixtureBase();
847
871
  try {
848
872
  // M001: roadmap has unchecked slices but a summary exists — should be complete
@@ -853,15 +877,16 @@ slice: S01
853
877
 
854
878
  const state = await deriveState(base);
855
879
  const m001Entry = state.registry.find(e => e.id === 'M001');
856
- assert.deepStrictEqual(m001Entry?.status, 'complete', 'M001 with unchecked roadmap + summary is complete');
857
- assert.deepStrictEqual(state.activeMilestone?.id, 'M002', 'active milestone is M002, not M001');
880
+ assertEq(m001Entry?.status, 'complete', 'M001 with unchecked roadmap + summary is complete');
881
+ assertEq(state.activeMilestone?.id, 'M002', 'active milestone is M002, not M001');
858
882
  } finally {
859
883
  cleanup(base);
860
884
  }
861
- });
885
+ }
862
886
 
863
887
  // ─── Test: unchecked roadmap + summary counts toward completeMilestoneIds (deps) ────
864
- test('unchecked roadmap + summary satisfies dependency', async () => {
888
+ console.log('\n=== unchecked roadmap + summary satisfies dependency ===');
889
+ {
865
890
  const base = createFixtureBase();
866
891
  try {
867
892
  // M001: unchecked roadmap + summary → complete
@@ -874,16 +899,17 @@ slice: S01
874
899
  writeFileSync(join(contextDir, 'M002-CONTEXT.md'), '---\ndepends_on:\n - M001\n---\n\n# M002 Context\n\nDepends on M001.');
875
900
 
876
901
  const state = await deriveState(base);
877
- assert.deepStrictEqual(state.activeMilestone?.id, 'M002', 'M002 is active — M001 dependency satisfied via summary');
902
+ assertEq(state.activeMilestone?.id, 'M002', 'M002 is active — M001 dependency satisfied via summary');
878
903
  const m002Entry = state.registry.find(e => e.id === 'M002');
879
- assert.deepStrictEqual(m002Entry?.status, 'active', 'M002 status is active, not pending');
904
+ assertEq(m002Entry?.status, 'active', 'M002 status is active, not pending');
880
905
  } finally {
881
906
  cleanup(base);
882
907
  }
883
- });
908
+ }
884
909
 
885
910
  // ─── Test: ghost milestone (only META.json) is skipped ───────────────
886
- test('ghost milestone (only META.json) is skipped', async () => {
911
+ console.log('\n=== ghost milestone (only META.json) is skipped ===');
912
+ {
887
913
  const base = createFixtureBase();
888
914
  try {
889
915
  // Create a ghost milestone directory with only META.json
@@ -892,20 +918,21 @@ slice: S01
892
918
  writeFileSync(join(ghostDir, 'META.json'), JSON.stringify({ id: 'M001' }));
893
919
 
894
920
  // isGhostMilestone should detect it
895
- assert.ok(isGhostMilestone(base, 'M001'), 'M001 is a ghost milestone');
921
+ assertTrue(isGhostMilestone(base, 'M001'), 'M001 is a ghost milestone');
896
922
 
897
923
  // deriveState should treat this as pre-planning (no real milestones)
898
924
  const state = await deriveState(base);
899
- assert.deepStrictEqual(state.phase, 'pre-planning', 'ghost-only: phase is pre-planning');
900
- assert.deepStrictEqual(state.activeMilestone, null, 'ghost-only: no active milestone');
901
- assert.deepStrictEqual(state.registry.length, 0, 'ghost-only: registry is empty');
925
+ assertEq(state.phase, 'pre-planning', 'ghost-only: phase is pre-planning');
926
+ assertEq(state.activeMilestone, null, 'ghost-only: no active milestone');
927
+ assertEq(state.registry.length, 0, 'ghost-only: registry is empty');
902
928
  } finally {
903
929
  cleanup(base);
904
930
  }
905
- });
931
+ }
906
932
 
907
933
  // ─── Test: ghost milestone skipped when real milestones exist ──────────
908
- test('ghost milestone skipped alongside real milestones', async () => {
934
+ console.log('\n=== ghost milestone skipped alongside real milestones ===');
935
+ {
909
936
  const base = createFixtureBase();
910
937
  try {
911
938
  // M001: ghost (only META.json)
@@ -919,19 +946,20 @@ slice: S01
919
946
  writeFileSync(join(realDir, 'M002-CONTEXT.md'), '# Real Milestone\n\nThis has content.');
920
947
 
921
948
  const state = await deriveState(base);
922
- assert.deepStrictEqual(state.activeMilestone?.id, 'M002', 'ghost+real: active milestone is M002');
949
+ assertEq(state.activeMilestone?.id, 'M002', 'ghost+real: active milestone is M002');
923
950
  // Ghost M001 should not appear in the registry
924
951
  const m001Entry = state.registry.find(e => e.id === 'M001');
925
- assert.deepStrictEqual(m001Entry, undefined, 'ghost+real: M001 not in registry');
926
- assert.deepStrictEqual(state.registry.length, 1, 'ghost+real: registry has 1 entry');
927
- assert.deepStrictEqual(state.registry[0]?.status, 'active', 'ghost+real: M002 is active');
952
+ assertEq(m001Entry, undefined, 'ghost+real: M001 not in registry');
953
+ assertEq(state.registry.length, 1, 'ghost+real: registry has 1 entry');
954
+ assertEq(state.registry[0]?.status, 'active', 'ghost+real: M002 is active');
928
955
  } finally {
929
956
  cleanup(base);
930
957
  }
931
- });
958
+ }
932
959
 
933
960
  // ─── Test: zero-slice roadmap → pre-planning, not blocked (#1785) ────
934
- test('zero-slice roadmap → pre-planning, not blocked (#1785)', async () => {
961
+ console.log('\n=== zero-slice roadmap → pre-planning, not blocked (#1785) ===');
962
+ {
935
963
  const base = createFixtureBase();
936
964
  try {
937
965
  // Write a stub roadmap with zero slices (placeholder text, no slice definitions)
@@ -939,15 +967,22 @@ slice: S01
939
967
 
940
968
  const state = await deriveState(base);
941
969
 
942
- assert.deepStrictEqual(state.phase, 'pre-planning', 'phase is pre-planning when roadmap has zero slices');
943
- assert.ok(state.activeMilestone !== null, 'activeMilestone is set');
944
- assert.deepStrictEqual(state.activeMilestone?.id, 'M001', 'activeMilestone is M001');
945
- assert.deepStrictEqual(state.activeSlice, null, 'activeSlice is null');
946
- assert.deepStrictEqual(state.activeTask, null, 'activeTask is null');
947
- assert.deepStrictEqual(state.blockers.length, 0, 'no blockers reported');
948
- assert.ok(state.nextAction.includes('M001'), 'nextAction references M001');
970
+ assertEq(state.phase, 'pre-planning', 'phase is pre-planning when roadmap has zero slices');
971
+ assertTrue(state.activeMilestone !== null, 'activeMilestone is set');
972
+ assertEq(state.activeMilestone?.id, 'M001', 'activeMilestone is M001');
973
+ assertEq(state.activeSlice, null, 'activeSlice is null');
974
+ assertEq(state.activeTask, null, 'activeTask is null');
975
+ assertEq(state.blockers.length, 0, 'no blockers reported');
976
+ assertTrue(state.nextAction.includes('M001'), 'nextAction references M001');
949
977
  } finally {
950
978
  cleanup(base);
951
979
  }
952
- });
980
+ }
981
+
982
+ report();
983
+ }
984
+
985
+ main().catch((error) => {
986
+ console.error(error);
987
+ process.exit(1);
953
988
  });