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,5 +1,3 @@
1
- import { describe, test } from 'node:test';
2
- import assert from 'node:assert/strict';
3
1
  // ensureDbOpen — Tests that the lazy DB opener creates + migrates the database
4
2
  // when .gsd/ exists with Markdown content but no gsd.db file.
5
3
  //
@@ -7,11 +5,14 @@ import assert from 'node:assert/strict';
7
5
  // "GSD database is not available" because ensureDbOpen only opened
8
6
  // existing DB files but never created them.
9
7
 
8
+ import { createTestContext } from './test-helpers.ts';
10
9
  import * as path from 'node:path';
11
10
  import * as os from 'node:os';
12
11
  import * as fs from 'node:fs';
13
12
  import { closeDatabase, isDbAvailable, getDecisionById } from '../gsd-db.ts';
14
13
 
14
+ const { assertEq, assertTrue, report } = createTestContext();
15
+
15
16
  function makeTmpDir(): string {
16
17
  const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-ensure-db-'));
17
18
  return dir;
@@ -27,134 +28,141 @@ function cleanupDir(dir: string): void {
27
28
  // ensureDbOpen creates DB + migrates when .gsd/ has Markdown
28
29
  // ═══════════════════════════════════════════════════════════════════════════
29
30
 
30
- describe('ensure-db-open', () => {
31
- test('ensureDbOpen: creates DB from Markdown', async () => {
32
- const tmpDir = makeTmpDir();
33
- const gsdDir = path.join(tmpDir, '.gsd');
34
- fs.mkdirSync(gsdDir, { recursive: true });
35
-
36
- // Write a minimal DECISIONS.md so migration has content
37
- const decisionsContent = `# Decisions
38
-
39
- | # | When | Scope | Decision | Choice | Rationale | Revisable |
40
- |---|------|-------|----------|--------|-----------|-----------|
41
- | D001 | M001 | architecture | Use SQLite | SQLite | Sync API | Yes |
42
- `;
43
- fs.writeFileSync(path.join(gsdDir, 'DECISIONS.md'), decisionsContent);
44
-
45
- // Verify no DB file exists yet
46
- const dbPath = path.join(gsdDir, 'gsd.db');
47
- assert.ok(!fs.existsSync(dbPath), 'DB file should not exist before ensureDbOpen');
48
-
49
- // Close any previously open DB
50
- try { closeDatabase(); } catch { /* ok */ }
51
-
52
- // Override process.cwd to point at tmpDir for ensureDbOpen
53
- const origCwd = process.cwd;
54
- process.cwd = () => tmpDir;
55
-
56
- try {
57
- // Dynamic import to get the freshest version
58
- const { ensureDbOpen } = await import('../bootstrap/dynamic-tools.ts');
59
-
60
- const result = await ensureDbOpen();
61
-
62
- assert.ok(result === true, 'ensureDbOpen should return true when .gsd/ has Markdown');
63
- assert.ok(fs.existsSync(dbPath), 'DB file should be created after ensureDbOpen');
64
- assert.ok(isDbAvailable(), 'DB should be available after ensureDbOpen');
65
-
66
- // Verify that Markdown migration actually ran
67
- const decision = getDecisionById('D001');
68
- assert.ok(decision !== null, 'D001 should be migrated from DECISIONS.md');
69
- if (decision) {
70
- assert.deepStrictEqual(decision.scope, 'architecture', 'Migrated decision scope should match');
71
- assert.deepStrictEqual(decision.choice, 'SQLite', 'Migrated decision choice should match');
72
- }
73
- } finally {
74
- process.cwd = origCwd;
75
- closeDatabase();
76
- cleanupDir(tmpDir);
77
- }
78
- });
79
-
80
- // ═══════════════════════════════════════════════════════════════════════════
81
- // ensureDbOpen returns false when no .gsd/ exists
82
- // ═══════════════════════════════════════════════════════════════════════════
83
-
84
- test('ensureDbOpen: no .gsd/ returns false', async () => {
85
- const tmpDir = makeTmpDir();
86
- // No .gsd/ directory at all
87
-
88
- try { closeDatabase(); } catch { /* ok */ }
89
- const origCwd = process.cwd;
90
- process.cwd = () => tmpDir;
91
-
92
- try {
93
- const { ensureDbOpen } = await import('../bootstrap/dynamic-tools.ts');
94
- const result = await ensureDbOpen();
95
- assert.ok(result === false, 'ensureDbOpen should return false when no .gsd/ exists');
96
- assert.ok(!isDbAvailable(), 'DB should not be available');
97
- } finally {
98
- process.cwd = origCwd;
99
- cleanupDir(tmpDir);
31
+ console.log('\n── ensureDbOpen: creates DB from Markdown ──');
32
+
33
+ {
34
+ const tmpDir = makeTmpDir();
35
+ const gsdDir = path.join(tmpDir, '.gsd');
36
+ fs.mkdirSync(gsdDir, { recursive: true });
37
+
38
+ // Write a minimal DECISIONS.md so migration has content
39
+ const decisionsContent = `# Decisions
40
+
41
+ | # | When | Scope | Decision | Choice | Rationale | Revisable |
42
+ |---|------|-------|----------|--------|-----------|-----------|
43
+ | D001 | M001 | architecture | Use SQLite | SQLite | Sync API | Yes |
44
+ `;
45
+ fs.writeFileSync(path.join(gsdDir, 'DECISIONS.md'), decisionsContent);
46
+
47
+ // Verify no DB file exists yet
48
+ const dbPath = path.join(gsdDir, 'gsd.db');
49
+ assertTrue(!fs.existsSync(dbPath), 'DB file should not exist before ensureDbOpen');
50
+
51
+ // Close any previously open DB
52
+ try { closeDatabase(); } catch { /* ok */ }
53
+
54
+ // Override process.cwd to point at tmpDir for ensureDbOpen
55
+ const origCwd = process.cwd;
56
+ process.cwd = () => tmpDir;
57
+
58
+ try {
59
+ // Dynamic import to get the freshest version
60
+ const { ensureDbOpen } = await import('../bootstrap/dynamic-tools.ts');
61
+
62
+ const result = await ensureDbOpen();
63
+
64
+ assertTrue(result === true, 'ensureDbOpen should return true when .gsd/ has Markdown');
65
+ assertTrue(fs.existsSync(dbPath), 'DB file should be created after ensureDbOpen');
66
+ assertTrue(isDbAvailable(), 'DB should be available after ensureDbOpen');
67
+
68
+ // Verify that Markdown migration actually ran
69
+ const decision = getDecisionById('D001');
70
+ assertTrue(decision !== null, 'D001 should be migrated from DECISIONS.md');
71
+ if (decision) {
72
+ assertEq(decision.scope, 'architecture', 'Migrated decision scope should match');
73
+ assertEq(decision.choice, 'SQLite', 'Migrated decision choice should match');
100
74
  }
101
- });
75
+ } finally {
76
+ process.cwd = origCwd;
77
+ closeDatabase();
78
+ cleanupDir(tmpDir);
79
+ }
80
+ }
81
+
82
+ // ═══════════════════════════════════════════════════════════════════════════
83
+ // ensureDbOpen returns false when no .gsd/ exists
84
+ // ═══════════════════════════════════════════════════════════════════════════
85
+
86
+ console.log('\n── ensureDbOpen: no .gsd/ returns false ──');
102
87
 
103
- // ═══════════════════════════════════════════════════════════════════════════
104
- // ensureDbOpen opens existing DB without re-migration
105
- // ═══════════════════════════════════════════════════════════════════════════
88
+ {
89
+ const tmpDir = makeTmpDir();
90
+ // No .gsd/ directory at all
106
91
 
107
- test('ensureDbOpen: opens existing DB', async () => {
108
- const tmpDir = makeTmpDir();
109
- const gsdDir = path.join(tmpDir, '.gsd');
110
- fs.mkdirSync(gsdDir, { recursive: true });
92
+ try { closeDatabase(); } catch { /* ok */ }
93
+ const origCwd = process.cwd;
94
+ process.cwd = () => tmpDir;
111
95
 
112
- // Create a DB file first
113
- const dbPath = path.join(gsdDir, 'gsd.db');
114
- const { openDatabase } = await import('../gsd-db.ts');
115
- openDatabase(dbPath);
96
+ try {
97
+ const { ensureDbOpen } = await import('../bootstrap/dynamic-tools.ts');
98
+ const result = await ensureDbOpen();
99
+ assertTrue(result === false, 'ensureDbOpen should return false when no .gsd/ exists');
100
+ assertTrue(!isDbAvailable(), 'DB should not be available');
101
+ } finally {
102
+ process.cwd = origCwd;
103
+ cleanupDir(tmpDir);
104
+ }
105
+ }
106
+
107
+ // ═══════════════════════════════════════════════════════════════════════════
108
+ // ensureDbOpen opens existing DB without re-migration
109
+ // ═══════════════════════════════════════════════════════════════════════════
110
+
111
+ console.log('\n── ensureDbOpen: opens existing DB ──');
112
+
113
+ {
114
+ const tmpDir = makeTmpDir();
115
+ const gsdDir = path.join(tmpDir, '.gsd');
116
+ fs.mkdirSync(gsdDir, { recursive: true });
117
+
118
+ // Create a DB file first
119
+ const dbPath = path.join(gsdDir, 'gsd.db');
120
+ const { openDatabase } = await import('../gsd-db.ts');
121
+ openDatabase(dbPath);
122
+ closeDatabase();
123
+
124
+ assertTrue(fs.existsSync(dbPath), 'DB file should exist from manual create');
125
+
126
+ const origCwd = process.cwd;
127
+ process.cwd = () => tmpDir;
128
+
129
+ try {
130
+ const { ensureDbOpen } = await import('../bootstrap/dynamic-tools.ts');
131
+ const result = await ensureDbOpen();
132
+ assertTrue(result === true, 'ensureDbOpen should open existing DB');
133
+ assertTrue(isDbAvailable(), 'DB should be available');
134
+ } finally {
135
+ process.cwd = origCwd;
116
136
  closeDatabase();
137
+ cleanupDir(tmpDir);
138
+ }
139
+ }
117
140
 
118
- assert.ok(fs.existsSync(dbPath), 'DB file should exist from manual create');
141
+ // ═══════════════════════════════════════════════════════════════════════════
142
+ // ensureDbOpen returns false for empty .gsd/ (no Markdown, no DB)
143
+ // ═══════════════════════════════════════════════════════════════════════════
119
144
 
120
- const origCwd = process.cwd;
121
- process.cwd = () => tmpDir;
145
+ console.log('\n── ensureDbOpen: empty .gsd/ returns false ──');
122
146
 
123
- try {
124
- const { ensureDbOpen } = await import('../bootstrap/dynamic-tools.ts');
125
- const result = await ensureDbOpen();
126
- assert.ok(result === true, 'ensureDbOpen should open existing DB');
127
- assert.ok(isDbAvailable(), 'DB should be available');
128
- } finally {
129
- process.cwd = origCwd;
130
- closeDatabase();
131
- cleanupDir(tmpDir);
132
- }
133
- });
134
-
135
- // ═══════════════════════════════════════════════════════════════════════════
136
- // ensureDbOpen returns false for empty .gsd/ (no Markdown, no DB)
137
- // ═══════════════════════════════════════════════════════════════════════════
138
-
139
- test('ensureDbOpen: empty .gsd/ returns false', async () => {
140
- const tmpDir = makeTmpDir();
141
- fs.mkdirSync(path.join(tmpDir, '.gsd'), { recursive: true });
142
- // .gsd/ exists but no DECISIONS.md, REQUIREMENTS.md, or milestones/
143
-
144
- try { closeDatabase(); } catch { /* ok */ }
145
- const origCwd = process.cwd;
146
- process.cwd = () => tmpDir;
147
-
148
- try {
149
- const { ensureDbOpen } = await import('../bootstrap/dynamic-tools.ts');
150
- const result = await ensureDbOpen();
151
- assert.ok(result === false, 'ensureDbOpen should return false for empty .gsd/');
152
- } finally {
153
- process.cwd = origCwd;
154
- cleanupDir(tmpDir);
155
- }
156
- });
147
+ {
148
+ const tmpDir = makeTmpDir();
149
+ fs.mkdirSync(path.join(tmpDir, '.gsd'), { recursive: true });
150
+ // .gsd/ exists but no DECISIONS.md, REQUIREMENTS.md, or milestones/
157
151
 
158
- // ═══════════════════════════════════════════════════════════════════════════
152
+ try { closeDatabase(); } catch { /* ok */ }
153
+ const origCwd = process.cwd;
154
+ process.cwd = () => tmpDir;
155
+
156
+ try {
157
+ const { ensureDbOpen } = await import('../bootstrap/dynamic-tools.ts');
158
+ const result = await ensureDbOpen();
159
+ assertTrue(result === false, 'ensureDbOpen should return false for empty .gsd/');
160
+ } finally {
161
+ process.cwd = origCwd;
162
+ cleanupDir(tmpDir);
163
+ }
164
+ }
165
+
166
+ // ═══════════════════════════════════════════════════════════════════════════
159
167
 
160
- });
168
+ report();
@@ -3,7 +3,7 @@ import assert from "node:assert/strict";
3
3
 
4
4
  import { registerExitCommand } from "../exit-command.ts";
5
5
 
6
- test("/exit requests graceful shutdown instead of process.exit", async (t) => {
6
+ test("/exit requests graceful shutdown instead of process.exit", async () => {
7
7
  const commands = new Map<
8
8
  string,
9
9
  {
@@ -35,13 +35,15 @@ test("/exit requests graceful shutdown instead of process.exit", async (t) => {
35
35
  throw new Error(`process.exit should not be called: ${code ?? "undefined"}`);
36
36
  }) as typeof process.exit;
37
37
 
38
- t.after(() => { process.exit = originalExit; });
39
-
40
- await exit.handler("", {
41
- async shutdown() {
42
- shutdownCalls += 1;
43
- },
44
- });
38
+ try {
39
+ await exit.handler("", {
40
+ async shutdown() {
41
+ shutdownCalls += 1;
42
+ },
43
+ });
44
+ } finally {
45
+ process.exit = originalExit;
46
+ }
45
47
 
46
48
  assert.equal(stopAutoCalls, 1, "handler should stop auto-mode exactly once before shutdown");
47
49
  assert.equal(shutdownCalls, 1, "handler should request graceful shutdown exactly once");
@@ -49,7 +51,7 @@ test("/exit requests graceful shutdown instead of process.exit", async (t) => {
49
51
 
50
52
  // ─── #1839 regression: ESM cache mismatch must not crash exit ────────────────
51
53
 
52
- test("/exit still shuts down gracefully when stopAuto throws (ESM module cache mismatch)", async (t) => {
54
+ test("/exit still shuts down gracefully when stopAuto throws (ESM module cache mismatch)", async () => {
53
55
  const commands = new Map<string, { description?: string; handler: (args: string, ctx: any) => Promise<void> }>();
54
56
 
55
57
  const pi = {
@@ -78,18 +80,20 @@ test("/exit still shuts down gracefully when stopAuto throws (ESM module cache m
78
80
  throw new Error(`process.exit should not be called: ${code ?? "undefined"}`);
79
81
  }) as typeof process.exit;
80
82
 
81
- t.after(() => { process.exit = originalExit; });
82
-
83
- await exit.handler("", {
84
- async shutdown() {
85
- shutdownCalls += 1;
86
- },
87
- ui: {
88
- notify(msg: string, level: string) {
89
- notifications.push({ msg, level });
83
+ try {
84
+ await exit.handler("", {
85
+ async shutdown() {
86
+ shutdownCalls += 1;
90
87
  },
91
- },
92
- });
88
+ ui: {
89
+ notify(msg: string, level: string) {
90
+ notifications.push({ msg, level });
91
+ },
92
+ },
93
+ });
94
+ } finally {
95
+ process.exit = originalExit;
96
+ }
93
97
 
94
98
  assert.equal(shutdownCalls, 1, "shutdown must still be called even when stopAuto throws");
95
99
  assert.equal(notifications.length, 1, "should emit exactly one warning notification");
@@ -1,5 +1,3 @@
1
- import { describe, test } from 'node:test';
2
- import assert from 'node:assert/strict';
3
1
  /**
4
2
  * feature-branch-lifecycle.test.ts — Integration tests for the feature-branch workflow.
5
3
  *
@@ -31,6 +29,10 @@ import { captureIntegrationBranch, getSliceBranchName } from "../worktree.ts";
31
29
  import { writeIntegrationBranch, readIntegrationBranch } from "../git-service.ts";
32
30
  import { nextMilestoneId, generateMilestoneSuffix } from "../guided-flow.ts";
33
31
 
32
+ import { createTestContext } from "./test-helpers.ts";
33
+
34
+ const { assertEq, assertTrue, assertMatch, report } = createTestContext();
35
+
34
36
  // ─── Helpers ────────────────────────────────────────────────────────────────
35
37
 
36
38
  function run(cmd: string, cwd: string): string {
@@ -135,7 +137,7 @@ function addSliceToMilestone(
135
137
 
136
138
  // ─── Tests ──────────────────────────────────────────────────────────────────
137
139
 
138
- describe('feature-branch-lifecycle-integration', async () => {
140
+ async function main(): Promise<void> {
139
141
  const savedCwd = process.cwd();
140
142
  const tempDirs: string[] = [];
141
143
 
@@ -152,13 +154,14 @@ describe('feature-branch-lifecycle-integration', async () => {
152
154
  // Start on f-new-shiny-thing with uncommitted changes, create
153
155
  // worktree, add slices, merge back. Assert main is untouched.
154
156
  // ================================================================
155
- test('Feature-branch lifecycle with unique milestone IDs', () => {
157
+ console.log("\n=== Feature-branch lifecycle with unique milestone IDs ===");
158
+ {
156
159
  const featureBranch = "f-new-shiny-thing";
157
160
  const repo = fresh(featureBranch);
158
161
 
159
162
  // Generate a unique milestone ID (M001-xxxxxx format)
160
163
  const milestoneId = nextMilestoneId([], true);
161
- assert.match(milestoneId, /^M001-[a-z0-9]{6}$/, "unique milestone ID format");
164
+ assertMatch(milestoneId, /^M001-[a-z0-9]{6}$/, "unique milestone ID format");
162
165
 
163
166
  // Snapshot main before anything happens
164
167
  const mainShaBefore = headSha(repo, "main");
@@ -171,8 +174,8 @@ describe('feature-branch-lifecycle-integration', async () => {
171
174
 
172
175
  // Verify files are uncommitted
173
176
  const statusBefore = run("git status --short", repo);
174
- assert.ok(statusBefore.includes("wip-config.ts"), "wip-config.ts is uncommitted");
175
- assert.ok(statusBefore.includes("wip-types.ts"), "wip-types.ts is uncommitted");
177
+ assertTrue(statusBefore.includes("wip-config.ts"), "wip-config.ts is uncommitted");
178
+ assertTrue(statusBefore.includes("wip-types.ts"), "wip-types.ts is uncommitted");
176
179
 
177
180
  // ── Simulate what startAuto does: commit dirty state, capture integration branch ──
178
181
  // startAuto bootstraps .gsd/ which commits .gsd/ files. It also calls
@@ -195,7 +198,7 @@ describe('feature-branch-lifecycle-integration', async () => {
195
198
 
196
199
  // Verify integration branch recorded
197
200
  const recorded = readIntegrationBranch(repo, milestoneId);
198
- assert.deepStrictEqual(recorded, featureBranch, "integration branch recorded as feature branch");
201
+ assertEq(recorded, featureBranch, "integration branch recorded as feature branch");
199
202
 
200
203
  // Snapshot feature branch SHA after metadata commit (HEAD may have advanced)
201
204
  const featureShaBeforeWorktree = headSha(repo, featureBranch);
@@ -203,28 +206,28 @@ describe('feature-branch-lifecycle-integration', async () => {
203
206
  // ── Create the auto-worktree ──
204
207
  const wtPath = createAutoWorktree(repo, milestoneId);
205
208
  tempDirs.push(wtPath);
206
- assert.ok(existsSync(wtPath), "worktree directory created");
209
+ assertTrue(existsSync(wtPath), "worktree directory created");
207
210
 
208
211
  // Worktree should be on milestone/<unique-id> branch
209
212
  const wtBranch = run("git branch --show-current", wtPath);
210
- assert.deepStrictEqual(wtBranch, `milestone/${milestoneId}`, "worktree is on milestone branch");
213
+ assertEq(wtBranch, `milestone/${milestoneId}`, "worktree is on milestone branch");
211
214
 
212
215
  // Milestone branch should be rooted at the feature branch, not main
213
216
  const milestoneBranchBase = headSha(repo, `milestone/${milestoneId}`);
214
- assert.deepStrictEqual(
217
+ assertEq(
215
218
  milestoneBranchBase,
216
219
  featureShaBeforeWorktree,
217
220
  "milestone branch starts from feature branch HEAD",
218
221
  );
219
222
 
220
223
  // Feature-branch-only file should be in the worktree
221
- assert.ok(
224
+ assertTrue(
222
225
  existsSync(join(wtPath, "feature-setup.ts")),
223
226
  "feature branch file (feature-setup.ts) exists in worktree",
224
227
  );
225
228
 
226
229
  // Main should be completely untouched at this point
227
- assert.deepStrictEqual(headSha(repo, "main"), mainShaBefore, "main SHA unchanged after worktree creation");
230
+ assertEq(headSha(repo, "main"), mainShaBefore, "main SHA unchanged after worktree creation");
228
231
 
229
232
  // ── Do work in slices ──
230
233
  addSliceToMilestone(wtPath, milestoneId, "S01", "Auth module", [
@@ -247,62 +250,62 @@ describe('feature-branch-lifecycle-integration', async () => {
247
250
 
248
251
  // ── Assert: feature branch received the merge ──
249
252
  const currentBranch = run("git branch --show-current", repo);
250
- assert.deepStrictEqual(currentBranch, featureBranch, "repo is on feature branch after merge");
253
+ assertEq(currentBranch, featureBranch, "repo is on feature branch after merge");
251
254
 
252
255
  // Exactly one new commit on feature branch (the squash merge)
253
256
  const featureLog = run(`git log --oneline ${featureBranch}`, repo);
254
- assert.ok(
257
+ assertTrue(
255
258
  featureLog.includes(`feat(${milestoneId})`),
256
259
  "feature branch has milestone merge commit",
257
260
  );
258
261
 
259
262
  // Slice files are on the feature branch
260
- assert.ok(existsSync(join(repo, "auth.ts")), "auth.ts on feature branch");
261
- assert.ok(existsSync(join(repo, "dashboard.ts")), "dashboard.ts on feature branch");
262
- assert.ok(existsSync(join(repo, "auth-utils.ts")), "auth-utils.ts on feature branch");
263
+ assertTrue(existsSync(join(repo, "auth.ts")), "auth.ts on feature branch");
264
+ assertTrue(existsSync(join(repo, "dashboard.ts")), "dashboard.ts on feature branch");
265
+ assertTrue(existsSync(join(repo, "auth-utils.ts")), "auth-utils.ts on feature branch");
263
266
 
264
267
  // Original feature branch file still present
265
- assert.ok(existsSync(join(repo, "feature-setup.ts")), "feature-setup.ts still on feature branch");
268
+ assertTrue(existsSync(join(repo, "feature-setup.ts")), "feature-setup.ts still on feature branch");
266
269
 
267
270
  // Commit message is well-formed
268
- assert.ok(result.commitMessage.includes("New shiny feature"), "commit message has milestone title");
269
- assert.ok(result.commitMessage.includes("S01: Auth module"), "commit message lists S01");
270
- assert.ok(result.commitMessage.includes("S02: Dashboard"), "commit message lists S02");
271
- assert.ok(
271
+ assertTrue(result.commitMessage.includes("New shiny feature"), "commit message has milestone title");
272
+ assertTrue(result.commitMessage.includes("S01: Auth module"), "commit message lists S01");
273
+ assertTrue(result.commitMessage.includes("S02: Dashboard"), "commit message lists S02");
274
+ assertTrue(
272
275
  result.commitMessage.includes(`milestone/${milestoneId}`),
273
276
  "commit message references milestone branch with unique ID",
274
277
  );
275
278
 
276
279
  // ── Assert: main is COMPLETELY untouched ──
277
- assert.deepStrictEqual(headSha(repo, "main"), mainShaBefore, "main SHA unchanged after merge");
278
- assert.deepStrictEqual(commitCount(repo, "main"), mainCommitsBefore, "main commit count unchanged");
280
+ assertEq(headSha(repo, "main"), mainShaBefore, "main SHA unchanged after merge");
281
+ assertEq(commitCount(repo, "main"), mainCommitsBefore, "main commit count unchanged");
279
282
 
280
283
  // Main should NOT have any of the milestone files
281
284
  run("git checkout main", repo);
282
- assert.ok(!existsSync(join(repo, "auth.ts")), "auth.ts NOT on main");
283
- assert.ok(!existsSync(join(repo, "dashboard.ts")), "dashboard.ts NOT on main");
284
- assert.ok(!existsSync(join(repo, "feature-setup.ts")), "feature-setup.ts NOT on main");
285
+ assertTrue(!existsSync(join(repo, "auth.ts")), "auth.ts NOT on main");
286
+ assertTrue(!existsSync(join(repo, "dashboard.ts")), "dashboard.ts NOT on main");
287
+ assertTrue(!existsSync(join(repo, "feature-setup.ts")), "feature-setup.ts NOT on main");
285
288
  run(`git checkout ${featureBranch}`, repo);
286
289
 
287
290
  // ── Assert: worktree cleaned up ──
288
291
  const worktreeDir = join(repo, ".gsd", "worktrees", milestoneId);
289
- assert.ok(!existsSync(worktreeDir), "worktree directory removed");
292
+ assertTrue(!existsSync(worktreeDir), "worktree directory removed");
290
293
 
291
294
  // Milestone branch deleted
292
- assert.ok(
295
+ assertTrue(
293
296
  !branchExists(repo, `milestone/${milestoneId}`),
294
297
  "milestone branch deleted after merge",
295
298
  );
296
299
 
297
300
  // Only expected branches remain
298
301
  const branches = allBranches(repo);
299
- assert.ok(branches.includes("main"), "main branch exists");
300
- assert.ok(branches.includes(featureBranch), "feature branch exists");
301
- assert.ok(
302
+ assertTrue(branches.includes("main"), "main branch exists");
303
+ assertTrue(branches.includes(featureBranch), "feature branch exists");
304
+ assertTrue(
302
305
  !branches.some(b => b.startsWith("milestone/")),
303
306
  "no milestone branches remain",
304
307
  );
305
- });
308
+ }
306
309
 
307
310
  // ================================================================
308
311
  // Test 2: Uncommitted .gsd/ planning files are available in worktree
@@ -311,7 +314,8 @@ describe('feature-branch-lifecycle-integration', async () => {
311
314
  // Planning artifacts should be carried into the worktree even if
312
315
  // they weren't committed on the feature branch.
313
316
  // ================================================================
314
- test('Untracked planning files copied to worktree', () => {
317
+ console.log("\n=== Untracked planning files copied to worktree ===");
318
+ {
315
319
  const featureBranch = "f-planning-test";
316
320
  const repo = fresh(featureBranch);
317
321
  const milestoneId = nextMilestoneId([], true);
@@ -330,7 +334,7 @@ describe('feature-branch-lifecycle-integration', async () => {
330
334
  writeFileSync(join(repo, ".gsd", "DECISIONS.md"), "# Decisions\n\n## D001\nTest decision.\n");
331
335
 
332
336
  // These files are untracked
333
- assert.ok(run("git status --short", repo).length > 0, "repo has untracked files");
337
+ assertTrue(run("git status --short", repo).length > 0, "repo has untracked files");
334
338
 
335
339
  // Record integration branch and create worktree
336
340
  writeIntegrationBranch(repo, milestoneId, featureBranch);
@@ -340,11 +344,11 @@ describe('feature-branch-lifecycle-integration', async () => {
340
344
  // With external state, worktree .gsd is a symlink to shared state.
341
345
  // Verify symlink was created (planning files are shared, not copied).
342
346
  const wtGsd = join(wtPath, ".gsd");
343
- assert.ok(existsSync(wtGsd), "worktree .gsd exists (symlink or dir)");
347
+ assertTrue(existsSync(wtGsd), "worktree .gsd exists (symlink or dir)");
344
348
 
345
349
  // Clean up: chdir back before teardown
346
350
  process.chdir(savedCwd);
347
- });
351
+ }
348
352
 
349
353
  // ================================================================
350
354
  // Test 3: Multiple milestones on the same feature branch
@@ -352,7 +356,8 @@ describe('feature-branch-lifecycle-integration', async () => {
352
356
  // Proves that unique IDs prevent collision when running successive
353
357
  // milestones, and each merge lands on the feature branch.
354
358
  // ================================================================
355
- test('Multiple unique milestones on same feature branch', () => {
359
+ console.log("\n=== Multiple unique milestones on same feature branch ===");
360
+ {
356
361
  const featureBranch = "f-multi-milestone";
357
362
  const repo = fresh(featureBranch);
358
363
 
@@ -372,12 +377,12 @@ describe('feature-branch-lifecycle-integration', async () => {
372
377
  mergeMilestoneToMain(repo, mid1, makeRoadmap(mid1, "First", [{ id: "S01", title: "First milestone work" }]));
373
378
  process.chdir(savedCwd);
374
379
 
375
- assert.ok(existsSync(join(repo, "m1-feature.ts")), "m1 file on feature branch");
380
+ assertTrue(existsSync(join(repo, "m1-feature.ts")), "m1 file on feature branch");
376
381
 
377
382
  // Second milestone — different unique ID
378
383
  const mid2 = nextMilestoneId([mid1], true);
379
- assert.ok(mid1 !== mid2, "second milestone has different ID");
380
- assert.match(mid2, /^M002-[a-z0-9]{6}$/, "second milestone is M002-xxxxxx");
384
+ assertTrue(mid1 !== mid2, "second milestone has different ID");
385
+ assertMatch(mid2, /^M002-[a-z0-9]{6}$/, "second milestone is M002-xxxxxx");
381
386
 
382
387
  mkdirSync(join(repo, ".gsd", "milestones", mid2), { recursive: true });
383
388
  writeIntegrationBranch(repo, mid2, featureBranch);
@@ -392,19 +397,19 @@ describe('feature-branch-lifecycle-integration', async () => {
392
397
  process.chdir(savedCwd);
393
398
 
394
399
  // Both milestone files on feature branch
395
- assert.ok(existsSync(join(repo, "m1-feature.ts")), "m1 file still on feature branch");
396
- assert.ok(existsSync(join(repo, "m2-feature.ts")), "m2 file on feature branch");
400
+ assertTrue(existsSync(join(repo, "m1-feature.ts")), "m1 file still on feature branch");
401
+ assertTrue(existsSync(join(repo, "m2-feature.ts")), "m2 file on feature branch");
397
402
 
398
403
  // Main completely untouched
399
- assert.deepStrictEqual(headSha(repo, "main"), mainShaBefore, "main unchanged after two milestones");
404
+ assertEq(headSha(repo, "main"), mainShaBefore, "main unchanged after two milestones");
400
405
 
401
406
  // No milestone branches remain
402
407
  const branches = allBranches(repo);
403
- assert.ok(
408
+ assertTrue(
404
409
  !branches.some(b => b.startsWith("milestone/")),
405
410
  "no milestone branches remain after two milestones",
406
411
  );
407
- });
412
+ }
408
413
 
409
414
  } finally {
410
415
  process.chdir(savedCwd);
@@ -412,4 +417,8 @@ describe('feature-branch-lifecycle-integration', async () => {
412
417
  try { rmSync(d, { recursive: true, force: true }); } catch { /* ignore */ }
413
418
  }
414
419
  }
415
- });
420
+
421
+ report();
422
+ }
423
+
424
+ main();
@@ -6,13 +6,15 @@ import fs from "node:fs";
6
6
 
7
7
  import { loadFile } from "../files.ts";
8
8
 
9
- test("loadFile returns null for directory paths instead of throwing EISDIR", async (t) => {
9
+ test("loadFile returns null for directory paths instead of throwing EISDIR", async () => {
10
10
  const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "gsd-loadfile-eisdir-"));
11
11
  const dirPath = path.join(tmp, "tasks");
12
12
  fs.mkdirSync(dirPath);
13
13
 
14
- t.after(() => { fs.rmSync(tmp, { recursive: true, force: true }); });
15
-
16
- const result = await loadFile(dirPath);
17
- assert.equal(result, null);
14
+ try {
15
+ const result = await loadFile(dirPath);
16
+ assert.equal(result, null);
17
+ } finally {
18
+ fs.rmSync(tmp, { recursive: true, force: true });
19
+ }
18
20
  });