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
  import { mkdtempSync, mkdirSync, writeFileSync, rmSync, existsSync, symlinkSync } from "node:fs";
4
2
  import { join, dirname } from "node:path";
5
3
  import { tmpdir } from "node:os";
@@ -22,170 +20,174 @@ import {
22
20
  type TaskCommitContext,
23
21
  } from "../git-service.ts";
24
22
  import { nativeAddAllWithExclusions } from "../native-git-bridge.ts";
23
+ import { createTestContext } from './test-helpers.ts';
24
+
25
+ const { assertEq, assertTrue, report } = createTestContext();
25
26
  function run(command: string, cwd: string): string {
26
27
  return execSync(command, { cwd, stdio: ["ignore", "pipe", "pipe"], encoding: "utf-8" }).trim();
27
28
  }
28
29
 
29
- describe('git-service', async () => {
30
+ async function main(): Promise<void> {
30
31
  // ─── inferCommitType ───────────────────────────────────────────────────
31
32
 
33
+ console.log("\n=== inferCommitType ===");
32
34
 
33
- assert.deepStrictEqual(
35
+ assertEq(
34
36
  inferCommitType("Implement user authentication"),
35
37
  "feat",
36
38
  "generic feature title → feat"
37
39
  );
38
40
 
39
- assert.deepStrictEqual(
41
+ assertEq(
40
42
  inferCommitType("Add dashboard page"),
41
43
  "feat",
42
44
  "add-style title → feat"
43
45
  );
44
46
 
45
- assert.deepStrictEqual(
47
+ assertEq(
46
48
  inferCommitType("Fix login redirect bug"),
47
49
  "fix",
48
50
  "title with 'fix' → fix"
49
51
  );
50
52
 
51
- assert.deepStrictEqual(
53
+ assertEq(
52
54
  inferCommitType("Bug in session handling"),
53
55
  "fix",
54
56
  "title with 'bug' → fix"
55
57
  );
56
58
 
57
- assert.deepStrictEqual(
59
+ assertEq(
58
60
  inferCommitType("Hotfix for production crash"),
59
61
  "fix",
60
62
  "title with 'hotfix' → fix"
61
63
  );
62
64
 
63
- assert.deepStrictEqual(
65
+ assertEq(
64
66
  inferCommitType("Patch memory leak"),
65
67
  "fix",
66
68
  "title with 'patch' → fix"
67
69
  );
68
70
 
69
- assert.deepStrictEqual(
71
+ assertEq(
70
72
  inferCommitType("Refactor state management"),
71
73
  "refactor",
72
74
  "title with 'refactor' → refactor"
73
75
  );
74
76
 
75
- assert.deepStrictEqual(
77
+ assertEq(
76
78
  inferCommitType("Restructure project layout"),
77
79
  "refactor",
78
80
  "title with 'restructure' → refactor"
79
81
  );
80
82
 
81
- assert.deepStrictEqual(
83
+ assertEq(
82
84
  inferCommitType("Reorganize module imports"),
83
85
  "refactor",
84
86
  "title with 'reorganize' → refactor"
85
87
  );
86
88
 
87
- assert.deepStrictEqual(
89
+ assertEq(
88
90
  inferCommitType("Update API documentation"),
89
91
  "docs",
90
92
  "title with 'documentation' → docs"
91
93
  );
92
94
 
93
- assert.deepStrictEqual(
95
+ assertEq(
94
96
  inferCommitType("Add doc for setup guide"),
95
97
  "docs",
96
98
  "title with 'doc' → docs"
97
99
  );
98
100
 
99
- assert.deepStrictEqual(
101
+ assertEq(
100
102
  inferCommitType("Add unit tests for auth"),
101
103
  "test",
102
104
  "title with 'tests' → test"
103
105
  );
104
106
 
105
- assert.deepStrictEqual(
107
+ assertEq(
106
108
  inferCommitType("Testing infrastructure setup"),
107
109
  "test",
108
110
  "title with 'testing' → test"
109
111
  );
110
112
 
111
- assert.deepStrictEqual(
113
+ assertEq(
112
114
  inferCommitType("Chore: update dependencies"),
113
115
  "chore",
114
116
  "title with 'chore' → chore"
115
117
  );
116
118
 
117
- assert.deepStrictEqual(
119
+ assertEq(
118
120
  inferCommitType("Cleanup unused imports"),
119
121
  "chore",
120
122
  "title with 'cleanup' → chore"
121
123
  );
122
124
 
123
- assert.deepStrictEqual(
125
+ assertEq(
124
126
  inferCommitType("Clean up stale branches"),
125
127
  "chore",
126
128
  "title with 'clean up' → chore"
127
129
  );
128
130
 
129
- assert.deepStrictEqual(
131
+ assertEq(
130
132
  inferCommitType("Archive old milestones"),
131
133
  "chore",
132
134
  "title with 'archive' → chore"
133
135
  );
134
136
 
135
- assert.deepStrictEqual(
137
+ assertEq(
136
138
  inferCommitType("Remove deprecated endpoints"),
137
139
  "chore",
138
140
  "title with 'remove' → chore"
139
141
  );
140
142
 
141
- assert.deepStrictEqual(
143
+ assertEq(
142
144
  inferCommitType("Delete temp files"),
143
145
  "chore",
144
146
  "title with 'delete' → chore"
145
147
  );
146
148
 
147
149
  // Mixed keywords — first match wins
148
- assert.deepStrictEqual(
150
+ assertEq(
149
151
  inferCommitType("Fix and refactor the login module"),
150
152
  "fix",
151
153
  "mixed keywords → first match wins (fix before refactor)"
152
154
  );
153
155
 
154
- assert.deepStrictEqual(
156
+ assertEq(
155
157
  inferCommitType("Refactor test utilities"),
156
158
  "refactor",
157
159
  "mixed keywords → first match wins (refactor before test)"
158
160
  );
159
161
 
160
162
  // Unknown / unrecognized title → feat
161
- assert.deepStrictEqual(
163
+ assertEq(
162
164
  inferCommitType("Build the new pipeline"),
163
165
  "feat",
164
166
  "unrecognized title → feat"
165
167
  );
166
168
 
167
- assert.deepStrictEqual(
169
+ assertEq(
168
170
  inferCommitType(""),
169
171
  "feat",
170
172
  "empty title → feat"
171
173
  );
172
174
 
173
175
  // Word boundary: "testify" should NOT match "test"
174
- assert.deepStrictEqual(
176
+ assertEq(
175
177
  inferCommitType("Testify integration"),
176
178
  "feat",
177
179
  "'testify' does not match 'test' — word boundary prevents partial match"
178
180
  );
179
181
 
180
182
  // "documentary" should NOT match "doc" (word boundary)
181
- assert.deepStrictEqual(
183
+ assertEq(
182
184
  inferCommitType("Documentary style UI"),
183
185
  "feat",
184
186
  "'documentary' does not match 'doc' — word boundary prevents partial match"
185
187
  );
186
188
 
187
189
  // "prefix" should NOT match "fix" (word boundary)
188
- assert.deepStrictEqual(
190
+ assertEq(
189
191
  inferCommitType("Add prefix to all IDs"),
190
192
  "feat",
191
193
  "'prefix' does not match 'fix' — word boundary prevents partial match"
@@ -193,14 +195,15 @@ describe('git-service', async () => {
193
195
 
194
196
  // ─── inferCommitType with oneLiner ──────────────────────────────────────
195
197
 
198
+ console.log("\n=== inferCommitType with oneLiner ===");
196
199
 
197
- assert.deepStrictEqual(
200
+ assertEq(
198
201
  inferCommitType("implement dashboard", "Fixed rendering bug in sidebar"),
199
202
  "fix",
200
203
  "one-liner with 'fixed' overrides generic title → fix"
201
204
  );
202
205
 
203
- assert.deepStrictEqual(
206
+ assertEq(
204
207
  inferCommitType("add search", "Optimized query performance with caching"),
205
208
  "perf",
206
209
  "one-liner with 'performance' and 'caching' → perf"
@@ -208,27 +211,29 @@ describe('git-service', async () => {
208
211
 
209
212
  // ─── buildTaskCommitMessage ─────────────────────────────────────────────
210
213
 
211
- test('buildTaskCommitMessage', () => {
214
+ console.log("\n=== buildTaskCommitMessage ===");
215
+
216
+ {
212
217
  const msg = buildTaskCommitMessage({
213
218
  taskId: "S01/T02",
214
219
  taskTitle: "implement user authentication",
215
220
  oneLiner: "Added JWT-based auth with refresh token rotation",
216
221
  keyFiles: ["src/auth.ts", "src/middleware/jwt.ts"],
217
222
  });
218
- assert.ok(msg.startsWith("feat(S01/T02):"), "message starts with type(scope)");
219
- assert.ok(msg.includes("JWT-based auth"), "message includes one-liner content");
220
- assert.ok(msg.includes("- src/auth.ts"), "message body includes key files");
221
- assert.ok(msg.includes("- src/middleware/jwt.ts"), "message body includes second key file");
222
- });
223
+ assertTrue(msg.startsWith("feat(S01/T02):"), "message starts with type(scope)");
224
+ assertTrue(msg.includes("JWT-based auth"), "message includes one-liner content");
225
+ assertTrue(msg.includes("- src/auth.ts"), "message body includes key files");
226
+ assertTrue(msg.includes("- src/middleware/jwt.ts"), "message body includes second key file");
227
+ }
223
228
 
224
229
  {
225
230
  const msg = buildTaskCommitMessage({
226
231
  taskId: "S02/T01",
227
232
  taskTitle: "fix login redirect bug",
228
233
  });
229
- assert.ok(msg.startsWith("fix(S02/T01):"), "infers fix type from title");
230
- assert.ok(msg.includes("fix login redirect bug"), "uses task title when no one-liner");
231
- assert.ok(!msg.includes("\n"), "no body when no key files");
234
+ assertTrue(msg.startsWith("fix(S02/T01):"), "infers fix type from title");
235
+ assertTrue(msg.includes("fix login redirect bug"), "uses task title when no one-liner");
236
+ assertTrue(!msg.includes("\n"), "no body when no key files");
232
237
  }
233
238
 
234
239
  {
@@ -237,13 +242,14 @@ describe('git-service', async () => {
237
242
  taskTitle: "add tests",
238
243
  oneLiner: "Unit tests for auth module with coverage",
239
244
  });
240
- assert.ok(msg.startsWith("test(S01/T03):"), "infers test type");
245
+ assertTrue(msg.startsWith("test(S01/T03):"), "infers test type");
241
246
  }
242
247
 
243
248
  // ─── RUNTIME_EXCLUSION_PATHS ───────────────────────────────────────────
244
249
 
250
+ console.log("\n=== RUNTIME_EXCLUSION_PATHS ===");
245
251
 
246
- assert.deepStrictEqual(
252
+ assertEq(
247
253
  RUNTIME_EXCLUSION_PATHS.length,
248
254
  13,
249
255
  "exactly 13 runtime exclusion paths"
@@ -265,23 +271,24 @@ describe('git-service', async () => {
265
271
  ".gsd/DISCUSSION-MANIFEST.json",
266
272
  ];
267
273
 
268
- assert.deepStrictEqual(
274
+ assertEq(
269
275
  [...RUNTIME_EXCLUSION_PATHS],
270
276
  expectedPaths,
271
277
  "paths match expected set in order"
272
278
  );
273
279
 
274
- assert.ok(
280
+ assertTrue(
275
281
  RUNTIME_EXCLUSION_PATHS.includes(".gsd/activity/"),
276
282
  "includes .gsd/activity/"
277
283
  );
278
- assert.ok(
284
+ assertTrue(
279
285
  RUNTIME_EXCLUSION_PATHS.includes(".gsd/STATE.md"),
280
286
  "includes .gsd/STATE.md"
281
287
  );
282
288
 
283
289
  // ─── runGit ────────────────────────────────────────────────────────────
284
290
 
291
+ console.log("\n=== runGit ===");
285
292
 
286
293
  const tempDir = mkdtempSync(join(tmpdir(), "gsd-git-service-test-"));
287
294
  run("git init -b main", tempDir);
@@ -290,11 +297,11 @@ describe('git-service', async () => {
290
297
 
291
298
  // runGit should work on a valid repo
292
299
  const branch = runGit(tempDir, ["branch", "--show-current"]);
293
- assert.deepStrictEqual(branch, "main", "runGit returns current branch");
300
+ assertEq(branch, "main", "runGit returns current branch");
294
301
 
295
302
  // runGit allowFailure returns empty string on failure
296
303
  const result = runGit(tempDir, ["log", "--oneline"], { allowFailure: true });
297
- assert.deepStrictEqual(result, "", "runGit allowFailure returns empty on error (no commits yet)");
304
+ assertEq(result, "", "runGit allowFailure returns empty on error (no commits yet)");
298
305
 
299
306
  // runGit throws on failure without allowFailure
300
307
  let threw = false;
@@ -302,21 +309,22 @@ describe('git-service', async () => {
302
309
  runGit(tempDir, ["log", "--oneline"]);
303
310
  } catch (e) {
304
311
  threw = true;
305
- assert.ok(
312
+ assertTrue(
306
313
  (e as Error).message.includes("git log --oneline failed"),
307
314
  "error message includes command and path"
308
315
  );
309
316
  }
310
- assert.ok(threw, "runGit throws without allowFailure on error");
317
+ assertTrue(threw, "runGit throws without allowFailure on error");
311
318
 
312
319
  // ─── Type exports compile check ────────────────────────────────────────
313
320
 
321
+ console.log("\n=== Type exports ===");
314
322
 
315
323
  // These are compile-time checks — if we got here, the types import fine
316
324
  const _prefs: GitPreferences = { auto_push: true, remote: "origin" };
317
325
  const _opts: CommitOptions = { message: "test" };
318
- assert.ok(true, "GitPreferences type exported and usable");
319
- assert.ok(true, "CommitOptions type exported and usable");
326
+ assertTrue(true, "GitPreferences type exported and usable");
327
+ assertTrue(true, "CommitOptions type exported and usable");
320
328
 
321
329
  // Cleanup T01 temp dir
322
330
  rmSync(tempDir, { recursive: true, force: true });
@@ -343,7 +351,9 @@ describe('git-service', async () => {
343
351
 
344
352
  // ─── GitServiceImpl: smart staging ─────────────────────────────────────
345
353
 
346
- test('GitServiceImpl: smart staging', () => {
354
+ console.log("\n=== GitServiceImpl: smart staging ===");
355
+
356
+ {
347
357
  const repo = initTempRepo();
348
358
  const svc = new GitServiceImpl(repo);
349
359
 
@@ -360,32 +370,34 @@ describe('git-service', async () => {
360
370
 
361
371
  const result = svc.commit({ message: "test: smart staging" });
362
372
 
363
- assert.deepStrictEqual(result, "test: smart staging", "commit returns the commit message");
373
+ assertEq(result, "test: smart staging", "commit returns the commit message");
364
374
 
365
375
  // Verify only src/code.ts is in the commit
366
376
  const showStat = run("git show --stat --format= HEAD", repo);
367
- assert.ok(showStat.includes("src/code.ts"), "src/code.ts is in the commit");
368
- assert.ok(!showStat.includes(".gsd/activity"), ".gsd/activity/ excluded from commit");
369
- assert.ok(!showStat.includes(".gsd/runtime"), ".gsd/runtime/ excluded from commit");
370
- assert.ok(!showStat.includes("STATE.md"), ".gsd/STATE.md excluded from commit");
371
- assert.ok(!showStat.includes("auto.lock"), ".gsd/auto.lock excluded from commit");
372
- assert.ok(!showStat.includes("metrics.json"), ".gsd/metrics.json excluded from commit");
373
- assert.ok(!showStat.includes(".gsd/worktrees"), ".gsd/worktrees/ excluded from commit");
377
+ assertTrue(showStat.includes("src/code.ts"), "src/code.ts is in the commit");
378
+ assertTrue(!showStat.includes(".gsd/activity"), ".gsd/activity/ excluded from commit");
379
+ assertTrue(!showStat.includes(".gsd/runtime"), ".gsd/runtime/ excluded from commit");
380
+ assertTrue(!showStat.includes("STATE.md"), ".gsd/STATE.md excluded from commit");
381
+ assertTrue(!showStat.includes("auto.lock"), ".gsd/auto.lock excluded from commit");
382
+ assertTrue(!showStat.includes("metrics.json"), ".gsd/metrics.json excluded from commit");
383
+ assertTrue(!showStat.includes(".gsd/worktrees"), ".gsd/worktrees/ excluded from commit");
374
384
 
375
385
  // Verify runtime files are still untracked
376
386
  // git status --short may collapse to "?? .gsd/" or show individual files
377
387
  // Use --untracked-files=all to force individual listing
378
388
  const statusOut = run("git status --short --untracked-files=all", repo);
379
- assert.ok(statusOut.includes(".gsd/activity/"), "activity still untracked after commit");
380
- assert.ok(statusOut.includes(".gsd/runtime/"), "runtime still untracked after commit");
381
- assert.ok(statusOut.includes(".gsd/STATE.md"), "STATE.md still untracked after commit");
389
+ assertTrue(statusOut.includes(".gsd/activity/"), "activity still untracked after commit");
390
+ assertTrue(statusOut.includes(".gsd/runtime/"), "runtime still untracked after commit");
391
+ assertTrue(statusOut.includes(".gsd/STATE.md"), "STATE.md still untracked after commit");
382
392
 
383
393
  rmSync(repo, { recursive: true, force: true });
384
- });
394
+ }
385
395
 
386
396
  // ─── GitServiceImpl: smart staging excludes tracked runtime files ──────
387
397
 
388
- test('GitServiceImpl: smart staging excludes tracked runtime files', () => {
398
+ console.log("\n=== GitServiceImpl: smart staging excludes tracked runtime files ===");
399
+
400
+ {
389
401
  // Reproduces the real bug: .gsd/ runtime files that are already tracked
390
402
  // (in the git index) must be excluded from staging even when .gsd/ is
391
403
  // in .gitignore. The old pathspec-exclude approach failed silently in
@@ -415,9 +427,9 @@ describe('git-service', async () => {
415
427
 
416
428
  // Verify runtime files are tracked (precondition)
417
429
  const tracked = run("git ls-files .gsd/", repo);
418
- assert.ok(tracked.includes("metrics.json"), "precondition: metrics.json tracked");
419
- assert.ok(tracked.includes("completed-units.json"), "precondition: completed-units.json tracked");
420
- assert.ok(tracked.includes("activity/log.jsonl"), "precondition: activity log tracked");
430
+ assertTrue(tracked.includes("metrics.json"), "precondition: metrics.json tracked");
431
+ assertTrue(tracked.includes("completed-units.json"), "precondition: completed-units.json tracked");
432
+ assertTrue(tracked.includes("activity/log.jsonl"), "precondition: activity log tracked");
421
433
 
422
434
  // Now modify both runtime and real files
423
435
  createFile(repo, ".gsd/metrics.json", '{"version":2}');
@@ -428,15 +440,15 @@ describe('git-service', async () => {
428
440
  // autoCommit should commit real.ts. The first call also runs auto-cleanup
429
441
  // which removes runtime files from the index via a dedicated commit.
430
442
  const msg = svc.autoCommit("execute-task", "M001/S01/T01");
431
- assert.ok(msg !== null, "autoCommit produces a commit");
443
+ assertTrue(msg !== null, "autoCommit produces a commit");
432
444
 
433
445
  const show = run("git show --stat HEAD", repo);
434
- assert.ok(show.includes("src/real.ts"), "real files are committed");
446
+ assertTrue(show.includes("src/real.ts"), "real files are committed");
435
447
 
436
448
  // After the commit, runtime files must no longer be in the git index.
437
449
  // They remain on disk but are untracked (protected by .gitignore).
438
450
  const trackedAfter = run("git ls-files .gsd/", repo);
439
- assert.deepStrictEqual(trackedAfter, "", "no .gsd/ runtime files remain in the index");
451
+ assertEq(trackedAfter, "", "no .gsd/ runtime files remain in the index");
440
452
 
441
453
  // Verify a second autoCommit with changed runtime files does NOT stage them
442
454
  createFile(repo, ".gsd/metrics.json", '{"version":3}');
@@ -444,33 +456,37 @@ describe('git-service', async () => {
444
456
  createFile(repo, "src/real.ts", "third version");
445
457
 
446
458
  const msg2 = svc.autoCommit("execute-task", "M001/S01/T02");
447
- assert.ok(msg2 !== null, "second autoCommit produces a commit");
459
+ assertTrue(msg2 !== null, "second autoCommit produces a commit");
448
460
 
449
461
  const show2 = run("git show --stat HEAD", repo);
450
- assert.ok(show2.includes("src/real.ts"), "real files committed in second commit");
451
- assert.ok(!show2.includes("metrics"), "metrics.json not in second commit");
452
- assert.ok(!show2.includes("completed-units"), "completed-units.json not in second commit");
453
- assert.ok(!show2.includes("activity"), "activity not in second commit");
462
+ assertTrue(show2.includes("src/real.ts"), "real files committed in second commit");
463
+ assertTrue(!show2.includes("metrics"), "metrics.json not in second commit");
464
+ assertTrue(!show2.includes("completed-units"), "completed-units.json not in second commit");
465
+ assertTrue(!show2.includes("activity"), "activity not in second commit");
454
466
 
455
467
  rmSync(repo, { recursive: true, force: true });
456
- });
468
+ }
457
469
 
458
470
  // ─── GitServiceImpl: autoCommit on clean repo ──────────────────────────
459
471
 
460
- test('GitServiceImpl: autoCommit', () => {
472
+ console.log("\n=== GitServiceImpl: autoCommit ===");
473
+
474
+ {
461
475
  const repo = initTempRepo();
462
476
  const svc = new GitServiceImpl(repo);
463
477
 
464
478
  // Clean repo — autoCommit should return null
465
479
  const cleanResult = svc.autoCommit("task", "T01");
466
- assert.deepStrictEqual(cleanResult, null, "autoCommit on clean repo returns null");
480
+ assertEq(cleanResult, null, "autoCommit on clean repo returns null");
467
481
 
468
482
  rmSync(repo, { recursive: true, force: true });
469
- });
483
+ }
470
484
 
471
485
  // ─── GitServiceImpl: autoCommit on dirty repo ──────────────────────────
472
486
 
473
- test('GitServiceImpl: autoCommit on dirty repo', () => {
487
+ console.log("\n=== GitServiceImpl: autoCommit on dirty repo ===");
488
+
489
+ {
474
490
  const repo = initTempRepo();
475
491
  const svc = new GitServiceImpl(repo);
476
492
 
@@ -478,10 +494,10 @@ describe('git-service', async () => {
478
494
 
479
495
  // Without task context, autoCommit uses generic chore message
480
496
  const msg = svc.autoCommit("task", "T01");
481
- assert.deepStrictEqual(msg, "chore(T01): auto-commit after task", "autoCommit returns generic format without task context");
497
+ assertEq(msg, "chore(T01): auto-commit after task", "autoCommit returns generic format without task context");
482
498
 
483
499
  const log = run("git log --oneline -1", repo);
484
- assert.ok(log.includes("chore(T01): auto-commit after task"), "generic commit message is in git log");
500
+ assertTrue(log.includes("chore(T01): auto-commit after task"), "generic commit message is in git log");
485
501
 
486
502
  // With task context, autoCommit uses meaningful message
487
503
  createFile(repo, "src/auth.ts", "export function login() {}");
@@ -491,16 +507,18 @@ describe('git-service', async () => {
491
507
  oneLiner: "Added JWT-based auth with refresh token rotation",
492
508
  keyFiles: ["src/auth.ts"],
493
509
  });
494
- assert.ok(msg2 !== null, "autoCommit with task context returns a message");
495
- assert.ok(msg2!.startsWith("feat(S01/T02):"), "meaningful commit uses feat type and scope");
496
- assert.ok(msg2!.includes("JWT-based auth"), "meaningful commit includes one-liner content");
510
+ assertTrue(msg2 !== null, "autoCommit with task context returns a message");
511
+ assertTrue(msg2!.startsWith("feat(S01/T02):"), "meaningful commit uses feat type and scope");
512
+ assertTrue(msg2!.includes("JWT-based auth"), "meaningful commit includes one-liner content");
497
513
 
498
514
  rmSync(repo, { recursive: true, force: true });
499
- });
515
+ }
500
516
 
501
517
  // ─── GitServiceImpl: empty-after-staging guard ─────────────────────────
502
518
 
503
- test('GitServiceImpl: empty-after-staging guard', () => {
519
+ console.log("\n=== GitServiceImpl: empty-after-staging guard ===");
520
+
521
+ {
504
522
  const repo = initTempRepo();
505
523
  const svc = new GitServiceImpl(repo);
506
524
 
@@ -508,18 +526,20 @@ describe('git-service', async () => {
508
526
  createFile(repo, ".gsd/activity/x.jsonl", "data");
509
527
 
510
528
  const result = svc.autoCommit("task", "T02");
511
- assert.deepStrictEqual(result, null, "autoCommit returns null when only runtime files are dirty");
529
+ assertEq(result, null, "autoCommit returns null when only runtime files are dirty");
512
530
 
513
531
  // Verify no new commit was created (should still be at init commit)
514
532
  const logCount = run("git rev-list --count HEAD", repo);
515
- assert.deepStrictEqual(logCount, "1", "no new commit created when only runtime files changed");
533
+ assertEq(logCount, "1", "no new commit created when only runtime files changed");
516
534
 
517
535
  rmSync(repo, { recursive: true, force: true });
518
- });
536
+ }
519
537
 
520
538
  // ─── GitServiceImpl: autoCommit with extraExclusions ───────────────────
521
539
 
522
- test('GitServiceImpl: autoCommit with extraExclusions', () => {
540
+ console.log("\n=== GitServiceImpl: autoCommit with extraExclusions ===");
541
+
542
+ {
523
543
  const repo = initTempRepo();
524
544
  const svc = new GitServiceImpl(repo);
525
545
 
@@ -529,19 +549,21 @@ describe('git-service', async () => {
529
549
 
530
550
  // Auto-commit with .gsd/ excluded (simulates pre-switch)
531
551
  const msg = svc.autoCommit("pre-switch", "main", [".gsd/"]);
532
- assert.deepStrictEqual(msg, "chore(main): auto-commit after pre-switch", "pre-switch autoCommit with .gsd/ exclusion commits");
552
+ assertEq(msg, "chore(main): auto-commit after pre-switch", "pre-switch autoCommit with .gsd/ exclusion commits");
533
553
 
534
554
  // Verify .gsd/ file was NOT committed
535
555
  const show = run("git show --stat HEAD", repo);
536
- assert.ok(!show.includes("ROADMAP"), ".gsd/ files excluded from pre-switch auto-commit");
537
- assert.ok(show.includes("feature.ts"), "non-.gsd/ files included in pre-switch auto-commit");
556
+ assertTrue(!show.includes("ROADMAP"), ".gsd/ files excluded from pre-switch auto-commit");
557
+ assertTrue(show.includes("feature.ts"), "non-.gsd/ files included in pre-switch auto-commit");
538
558
 
539
559
  rmSync(repo, { recursive: true, force: true });
540
- });
560
+ }
541
561
 
542
562
  // ─── GitServiceImpl: autoCommit extraExclusions — only .gsd/ dirty ────
543
563
 
544
- test('GitServiceImpl: autoCommit extraExclusions — only .gsd/ dirty', () => {
564
+ console.log("\n=== GitServiceImpl: autoCommit extraExclusions — only .gsd/ dirty ===");
565
+
566
+ {
545
567
  const repo = initTempRepo();
546
568
  const svc = new GitServiceImpl(repo);
547
569
 
@@ -551,23 +573,25 @@ describe('git-service', async () => {
551
573
 
552
574
  // Auto-commit with .gsd/ excluded — nothing else to commit
553
575
  const result = svc.autoCommit("pre-switch", "main", [".gsd/"]);
554
- assert.deepStrictEqual(result, null, "autoCommit returns null when only .gsd/ files are dirty and excluded");
576
+ assertEq(result, null, "autoCommit returns null when only .gsd/ files are dirty and excluded");
555
577
 
556
578
  rmSync(repo, { recursive: true, force: true });
557
- });
579
+ }
558
580
 
559
581
  // ─── GitServiceImpl: commit returns null when nothing staged ───────────
560
582
 
561
- test('GitServiceImpl: commit empty', () => {
583
+ console.log("\n=== GitServiceImpl: commit empty ===");
584
+
585
+ {
562
586
  const repo = initTempRepo();
563
587
  const svc = new GitServiceImpl(repo);
564
588
 
565
589
  // Nothing dirty, commit should return null
566
590
  const result = svc.commit({ message: "should not commit" });
567
- assert.deepStrictEqual(result, null, "commit returns null when nothing to stage");
591
+ assertEq(result, null, "commit returns null when nothing to stage");
568
592
 
569
593
  rmSync(repo, { recursive: true, force: true });
570
- });
594
+ }
571
595
 
572
596
  // ─── Helper: create repo for branch tests ────────────────────────────
573
597
 
@@ -584,32 +608,36 @@ describe('git-service', async () => {
584
608
 
585
609
  // ─── getCurrentBranch ────────────────────────────────────────────────
586
610
 
587
- test('Branch queries', () => {
611
+ console.log("\n=== Branch queries ===");
612
+
613
+ {
588
614
  const repo = initBranchTestRepo();
589
615
  const svc = new GitServiceImpl(repo);
590
616
 
591
- assert.deepStrictEqual(svc.getCurrentBranch(), "main", "getCurrentBranch returns main on main branch");
617
+ assertEq(svc.getCurrentBranch(), "main", "getCurrentBranch returns main on main branch");
592
618
 
593
619
  run("git checkout -b gsd/M001/S01", repo);
594
- assert.deepStrictEqual(svc.getCurrentBranch(), "gsd/M001/S01", "getCurrentBranch returns slice branch name");
620
+ assertEq(svc.getCurrentBranch(), "gsd/M001/S01", "getCurrentBranch returns slice branch name");
595
621
 
596
622
  run("git checkout -b feature/foo", repo);
597
- assert.deepStrictEqual(svc.getCurrentBranch(), "feature/foo", "getCurrentBranch returns feature branch name");
623
+ assertEq(svc.getCurrentBranch(), "feature/foo", "getCurrentBranch returns feature branch name");
598
624
 
599
625
  rmSync(repo, { recursive: true, force: true });
600
- });
626
+ }
601
627
 
602
628
  // ─── getMainBranch ────────────────────────────────────────────────────
603
629
 
604
- test('getMainBranch', () => {
630
+ console.log("\n=== getMainBranch ===");
631
+
632
+ {
605
633
  const repo = initBranchTestRepo();
606
634
  const svc = new GitServiceImpl(repo);
607
635
 
608
636
  // Basic case: repo has "main" branch
609
- assert.deepStrictEqual(svc.getMainBranch(), "main", "getMainBranch returns main when main exists");
637
+ assertEq(svc.getMainBranch(), "main", "getMainBranch returns main when main exists");
610
638
 
611
639
  rmSync(repo, { recursive: true, force: true });
612
- });
640
+ }
613
641
 
614
642
  {
615
643
  // master-only repo
@@ -622,7 +650,7 @@ describe('git-service', async () => {
622
650
  run('git commit -m "init"', repo);
623
651
 
624
652
  const svc = new GitServiceImpl(repo);
625
- assert.deepStrictEqual(svc.getMainBranch(), "master", "getMainBranch returns master when only master exists");
653
+ assertEq(svc.getMainBranch(), "master", "getMainBranch returns master when only master exists");
626
654
 
627
655
  rmSync(repo, { recursive: true, force: true });
628
656
  }
@@ -633,7 +661,9 @@ describe('git-service', async () => {
633
661
 
634
662
  // ─── createSnapshot: prefs enabled ─────────────────────────────────────
635
663
 
636
- test('createSnapshot: enabled', () => {
664
+ console.log("\n=== createSnapshot: enabled ===");
665
+
666
+ {
637
667
  const repo = initBranchTestRepo();
638
668
  const svc = new GitServiceImpl(repo, { snapshots: true });
639
669
 
@@ -647,14 +677,16 @@ describe('git-service', async () => {
647
677
 
648
678
  // Verify ref exists under refs/gsd/snapshots/
649
679
  const refs = run("git for-each-ref refs/gsd/snapshots/", repo);
650
- assert.ok(refs.includes("refs/gsd/snapshots/gsd/M001/S01/"), "snapshot ref created under refs/gsd/snapshots/");
680
+ assertTrue(refs.includes("refs/gsd/snapshots/gsd/M001/S01/"), "snapshot ref created under refs/gsd/snapshots/");
651
681
 
652
682
  rmSync(repo, { recursive: true, force: true });
653
- });
683
+ }
654
684
 
655
685
  // ─── createSnapshot: prefs disabled ────────────────────────────────────
656
686
 
657
- test('createSnapshot: disabled', () => {
687
+ console.log("\n=== createSnapshot: disabled ===");
688
+
689
+ {
658
690
  const repo = initBranchTestRepo();
659
691
  const svc = new GitServiceImpl(repo, { snapshots: false });
660
692
 
@@ -666,14 +698,16 @@ describe('git-service', async () => {
666
698
  svc.createSnapshot("gsd/M001/S01");
667
699
 
668
700
  const refs = run("git for-each-ref refs/gsd/snapshots/", repo);
669
- assert.deepStrictEqual(refs, "", "no snapshot ref created when prefs.snapshots is false");
701
+ assertEq(refs, "", "no snapshot ref created when prefs.snapshots is false");
670
702
 
671
703
  rmSync(repo, { recursive: true, force: true });
672
- });
704
+ }
673
705
 
674
706
  // ─── runPreMergeCheck: pass ────────────────────────────────────────────
675
707
 
676
- test('runPreMergeCheck: pass', () => {
708
+ console.log("\n=== runPreMergeCheck: pass ===");
709
+
710
+ {
677
711
  const repo = initBranchTestRepo();
678
712
  // Create package.json with passing test script
679
713
  createFile(repo, "package.json", JSON.stringify({
@@ -686,15 +720,17 @@ describe('git-service', async () => {
686
720
  const svc = new GitServiceImpl(repo, { pre_merge_check: true });
687
721
  const result: PreMergeCheckResult = svc.runPreMergeCheck();
688
722
 
689
- assert.deepStrictEqual(result.passed, true, "runPreMergeCheck returns passed:true when tests pass");
690
- assert.ok(!result.skipped, "runPreMergeCheck is not skipped when enabled");
723
+ assertEq(result.passed, true, "runPreMergeCheck returns passed:true when tests pass");
724
+ assertTrue(!result.skipped, "runPreMergeCheck is not skipped when enabled");
691
725
 
692
726
  rmSync(repo, { recursive: true, force: true });
693
- });
727
+ }
694
728
 
695
729
  // ─── runPreMergeCheck: fail ────────────────────────────────────────────
696
730
 
697
- test('runPreMergeCheck: fail', () => {
731
+ console.log("\n=== runPreMergeCheck: fail ===");
732
+
733
+ {
698
734
  const repo = initBranchTestRepo();
699
735
  // Create package.json with failing test script
700
736
  createFile(repo, "package.json", JSON.stringify({
@@ -707,15 +743,17 @@ describe('git-service', async () => {
707
743
  const svc = new GitServiceImpl(repo, { pre_merge_check: true });
708
744
  const result: PreMergeCheckResult = svc.runPreMergeCheck();
709
745
 
710
- assert.deepStrictEqual(result.passed, false, "runPreMergeCheck returns passed:false when tests fail");
711
- assert.ok(!result.skipped, "runPreMergeCheck is not skipped when enabled");
746
+ assertEq(result.passed, false, "runPreMergeCheck returns passed:false when tests fail");
747
+ assertTrue(!result.skipped, "runPreMergeCheck is not skipped when enabled");
712
748
 
713
749
  rmSync(repo, { recursive: true, force: true });
714
- });
750
+ }
715
751
 
716
752
  // ─── runPreMergeCheck: disabled ────────────────────────────────────────
717
753
 
718
- test('runPreMergeCheck: disabled', () => {
754
+ console.log("\n=== runPreMergeCheck: disabled ===");
755
+
756
+ {
719
757
  const repo = initBranchTestRepo();
720
758
  createFile(repo, "package.json", JSON.stringify({
721
759
  name: "test-disabled",
@@ -727,86 +765,98 @@ describe('git-service', async () => {
727
765
  const svc = new GitServiceImpl(repo, { pre_merge_check: false });
728
766
  const result: PreMergeCheckResult = svc.runPreMergeCheck();
729
767
 
730
- assert.deepStrictEqual(result.skipped, true, "runPreMergeCheck skipped when pre_merge_check is false");
731
- assert.deepStrictEqual(result.passed, true, "runPreMergeCheck returns passed:true when skipped (no block)");
768
+ assertEq(result.skipped, true, "runPreMergeCheck skipped when pre_merge_check is false");
769
+ assertEq(result.passed, true, "runPreMergeCheck returns passed:true when skipped (no block)");
732
770
 
733
771
  rmSync(repo, { recursive: true, force: true });
734
- });
772
+ }
735
773
 
736
774
  // ─── runPreMergeCheck: custom command ──────────────────────────────────
737
775
 
738
- test('runPreMergeCheck: custom command', () => {
776
+ console.log("\n=== runPreMergeCheck: custom command ===");
777
+
778
+ {
739
779
  const repo = initBranchTestRepo();
740
780
  // Custom command string overrides auto-detection
741
781
  const svc = new GitServiceImpl(repo, { pre_merge_check: 'node -e "process.exit(0)"' });
742
782
  const result: PreMergeCheckResult = svc.runPreMergeCheck();
743
783
 
744
- assert.deepStrictEqual(result.passed, true, "runPreMergeCheck passes with custom command that exits 0");
745
- assert.ok(!result.skipped, "custom command is not skipped");
784
+ assertEq(result.passed, true, "runPreMergeCheck passes with custom command that exits 0");
785
+ assertTrue(!result.skipped, "custom command is not skipped");
746
786
 
747
787
  rmSync(repo, { recursive: true, force: true });
748
- });
788
+ }
749
789
 
750
790
  // ─── VALID_BRANCH_NAME regex ──────────────────────────────────────────
751
791
 
752
- test('VALID_BRANCH_NAME regex', () => {
792
+ console.log("\n=== VALID_BRANCH_NAME regex ===");
793
+
794
+ {
753
795
  // Valid branch names
754
- assert.ok(VALID_BRANCH_NAME.test("main"), "VALID_BRANCH_NAME accepts 'main'");
755
- assert.ok(VALID_BRANCH_NAME.test("master"), "VALID_BRANCH_NAME accepts 'master'");
756
- assert.ok(VALID_BRANCH_NAME.test("develop"), "VALID_BRANCH_NAME accepts 'develop'");
757
- assert.ok(VALID_BRANCH_NAME.test("feature/foo"), "VALID_BRANCH_NAME accepts 'feature/foo'");
758
- assert.ok(VALID_BRANCH_NAME.test("release-1.0"), "VALID_BRANCH_NAME accepts 'release-1.0'");
759
- assert.ok(VALID_BRANCH_NAME.test("my_branch"), "VALID_BRANCH_NAME accepts 'my_branch'");
760
- assert.ok(VALID_BRANCH_NAME.test("v2.0.1"), "VALID_BRANCH_NAME accepts 'v2.0.1'");
796
+ assertTrue(VALID_BRANCH_NAME.test("main"), "VALID_BRANCH_NAME accepts 'main'");
797
+ assertTrue(VALID_BRANCH_NAME.test("master"), "VALID_BRANCH_NAME accepts 'master'");
798
+ assertTrue(VALID_BRANCH_NAME.test("develop"), "VALID_BRANCH_NAME accepts 'develop'");
799
+ assertTrue(VALID_BRANCH_NAME.test("feature/foo"), "VALID_BRANCH_NAME accepts 'feature/foo'");
800
+ assertTrue(VALID_BRANCH_NAME.test("release-1.0"), "VALID_BRANCH_NAME accepts 'release-1.0'");
801
+ assertTrue(VALID_BRANCH_NAME.test("my_branch"), "VALID_BRANCH_NAME accepts 'my_branch'");
802
+ assertTrue(VALID_BRANCH_NAME.test("v2.0.1"), "VALID_BRANCH_NAME accepts 'v2.0.1'");
761
803
 
762
804
  // Invalid / injection attempts
763
- assert.ok(!VALID_BRANCH_NAME.test("main; rm -rf /"), "VALID_BRANCH_NAME rejects shell injection");
764
- assert.ok(!VALID_BRANCH_NAME.test("main && echo pwned"), "VALID_BRANCH_NAME rejects && injection");
765
- assert.ok(!VALID_BRANCH_NAME.test(""), "VALID_BRANCH_NAME rejects empty string");
766
- assert.ok(!VALID_BRANCH_NAME.test("branch name"), "VALID_BRANCH_NAME rejects spaces");
767
- assert.ok(!VALID_BRANCH_NAME.test("branch`cmd`"), "VALID_BRANCH_NAME rejects backticks");
768
- assert.ok(!VALID_BRANCH_NAME.test("branch$(cmd)"), "VALID_BRANCH_NAME rejects $() subshell");
769
- });
805
+ assertTrue(!VALID_BRANCH_NAME.test("main; rm -rf /"), "VALID_BRANCH_NAME rejects shell injection");
806
+ assertTrue(!VALID_BRANCH_NAME.test("main && echo pwned"), "VALID_BRANCH_NAME rejects && injection");
807
+ assertTrue(!VALID_BRANCH_NAME.test(""), "VALID_BRANCH_NAME rejects empty string");
808
+ assertTrue(!VALID_BRANCH_NAME.test("branch name"), "VALID_BRANCH_NAME rejects spaces");
809
+ assertTrue(!VALID_BRANCH_NAME.test("branch`cmd`"), "VALID_BRANCH_NAME rejects backticks");
810
+ assertTrue(!VALID_BRANCH_NAME.test("branch$(cmd)"), "VALID_BRANCH_NAME rejects $() subshell");
811
+ }
770
812
 
771
813
  // ─── getMainBranch: configured main_branch preference ──────────────────
772
814
 
773
- test('getMainBranch: configured main_branch', () => {
815
+ console.log("\n=== getMainBranch: configured main_branch ===");
816
+
817
+ {
774
818
  const repo = initBranchTestRepo();
775
819
  const svc = new GitServiceImpl(repo, { main_branch: "trunk" });
776
820
 
777
- assert.deepStrictEqual(svc.getMainBranch(), "trunk", "getMainBranch returns configured main_branch preference");
821
+ assertEq(svc.getMainBranch(), "trunk", "getMainBranch returns configured main_branch preference");
778
822
 
779
823
  rmSync(repo, { recursive: true, force: true });
780
- });
824
+ }
781
825
 
782
826
  // ─── getMainBranch: falls back to auto-detection when not set ──────────
783
827
 
784
- test('getMainBranch: fallback to auto-detection', () => {
828
+ console.log("\n=== getMainBranch: fallback to auto-detection ===");
829
+
830
+ {
785
831
  const repo = initBranchTestRepo();
786
832
  const svc = new GitServiceImpl(repo, {});
787
833
 
788
- assert.deepStrictEqual(svc.getMainBranch(), "main", "getMainBranch falls back to auto-detection when main_branch not set");
834
+ assertEq(svc.getMainBranch(), "main", "getMainBranch falls back to auto-detection when main_branch not set");
789
835
 
790
836
  rmSync(repo, { recursive: true, force: true });
791
- });
837
+ }
792
838
 
793
839
  // ─── getMainBranch: ignores invalid branch names ───────────────────────
794
840
 
795
- test('getMainBranch: ignores invalid branch name', () => {
841
+ console.log("\n=== getMainBranch: ignores invalid branch name ===");
842
+
843
+ {
796
844
  const repo = initBranchTestRepo();
797
845
  const svc = new GitServiceImpl(repo, { main_branch: "main; rm -rf /" });
798
846
 
799
- assert.deepStrictEqual(svc.getMainBranch(), "main", "getMainBranch ignores invalid branch name and falls back to auto-detection");
847
+ assertEq(svc.getMainBranch(), "main", "getMainBranch ignores invalid branch name and falls back to auto-detection");
800
848
 
801
849
  rmSync(repo, { recursive: true, force: true });
802
- });
850
+ }
803
851
 
804
852
  // ─── PreMergeCheckResult type export compile check ─────────────────────
805
853
 
806
- test('PreMergeCheckResult type export', () => {
854
+ console.log("\n=== PreMergeCheckResult type export ===");
855
+
856
+ {
807
857
  const _checkResult: PreMergeCheckResult = { passed: true, skipped: false };
808
- assert.ok(true, "PreMergeCheckResult type exported and usable");
809
- });
858
+ assertTrue(true, "PreMergeCheckResult type exported and usable");
859
+ }
810
860
 
811
861
  // ═══════════════════════════════════════════════════════════════════════
812
862
  // Integration branch — feature-branch workflow support
@@ -814,70 +864,82 @@ describe('git-service', async () => {
814
864
 
815
865
  // ─── writeIntegrationBranch / readIntegrationBranch: round-trip ────────
816
866
 
817
- test('Integration branch: write and read', () => {
867
+ console.log("\n=== Integration branch: write and read ===");
868
+
869
+ {
818
870
  const repo = initBranchTestRepo();
819
871
 
820
872
  // Initially no integration branch
821
- assert.deepStrictEqual(readIntegrationBranch(repo, "M001"), null, "readIntegrationBranch returns null when no metadata");
873
+ assertEq(readIntegrationBranch(repo, "M001"), null, "readIntegrationBranch returns null when no metadata");
822
874
 
823
875
  // Write integration branch
824
876
  writeIntegrationBranch(repo, "M001", "f-123-new-thing");
825
- assert.deepStrictEqual(readIntegrationBranch(repo, "M001"), "f-123-new-thing", "readIntegrationBranch returns written branch");
877
+ assertEq(readIntegrationBranch(repo, "M001"), "f-123-new-thing", "readIntegrationBranch returns written branch");
826
878
 
827
879
  rmSync(repo, { recursive: true, force: true });
828
- });
880
+ }
829
881
 
830
882
  // ─── writeIntegrationBranch: updates when branch changes (#300) ──────
831
883
 
832
- test('Integration branch: updates on branch change', () => {
884
+ console.log("\n=== Integration branch: updates on branch change ===");
885
+
886
+ {
833
887
  const repo = initBranchTestRepo();
834
888
 
835
889
  writeIntegrationBranch(repo, "M001", "f-123-first");
836
890
  writeIntegrationBranch(repo, "M001", "f-456-second"); // updates to new branch (#300)
837
891
 
838
- assert.deepStrictEqual(readIntegrationBranch(repo, "M001"), "f-456-second", "second write updates integration branch to new value");
892
+ assertEq(readIntegrationBranch(repo, "M001"), "f-456-second", "second write updates integration branch to new value");
839
893
 
840
894
  rmSync(repo, { recursive: true, force: true });
841
- });
895
+ }
842
896
 
843
897
  // ─── writeIntegrationBranch: same branch is idempotent ─────────────────
844
898
 
845
- test('Integration branch: same branch is idempotent', () => {
899
+ console.log("\n=== Integration branch: same branch is idempotent ===");
900
+
901
+ {
846
902
  const repo = initBranchTestRepo();
847
903
 
848
904
  writeIntegrationBranch(repo, "M001", "f-123-first");
849
905
  writeIntegrationBranch(repo, "M001", "f-123-first"); // same branch — no-op
850
906
 
851
- assert.deepStrictEqual(readIntegrationBranch(repo, "M001"), "f-123-first", "same branch write is idempotent");
907
+ assertEq(readIntegrationBranch(repo, "M001"), "f-123-first", "same branch write is idempotent");
852
908
 
853
909
  rmSync(repo, { recursive: true, force: true });
854
- });
910
+ }
855
911
 
856
912
  // ─── writeIntegrationBranch: rejects slice branches ───────────────────
857
913
 
858
- test('Integration branch: rejects slice branches', () => {
914
+ console.log("\n=== Integration branch: rejects slice branches ===");
915
+
916
+ {
859
917
  const repo = initBranchTestRepo();
860
918
 
861
919
  writeIntegrationBranch(repo, "M001", "gsd/M001/S01");
862
- assert.deepStrictEqual(readIntegrationBranch(repo, "M001"), null, "slice branches are not recorded as integration branch");
920
+ assertEq(readIntegrationBranch(repo, "M001"), null, "slice branches are not recorded as integration branch");
863
921
 
864
922
  rmSync(repo, { recursive: true, force: true });
865
- });
923
+ }
866
924
 
867
925
  // ─── writeIntegrationBranch: rejects invalid branch names ─────────────
868
926
 
869
- test('Integration branch: rejects invalid names', () => {
927
+ console.log("\n=== Integration branch: rejects invalid names ===");
928
+
929
+ {
870
930
  const repo = initBranchTestRepo();
871
931
 
872
932
  writeIntegrationBranch(repo, "M001", "bad; rm -rf /");
873
- assert.deepStrictEqual(readIntegrationBranch(repo, "M001"), null, "invalid branch name is not recorded");
933
+ assertEq(readIntegrationBranch(repo, "M001"), null, "invalid branch name is not recorded");
874
934
 
875
935
  rmSync(repo, { recursive: true, force: true });
876
- });
936
+ }
877
937
 
878
938
  // ─── getMainBranch: uses integration branch when milestone set ────────
879
939
 
880
- test('getMainBranch: integration branch from milestone metadata', () => {
940
+ console.log("\n=== getMainBranch: integration branch from milestone metadata ===");
941
+
942
+ {
881
943
  const repo = initBranchTestRepo();
882
944
 
883
945
  // Create a feature branch
@@ -889,18 +951,20 @@ describe('git-service', async () => {
889
951
 
890
952
  // Without milestone set, getMainBranch returns "main"
891
953
  const svc = new GitServiceImpl(repo);
892
- assert.deepStrictEqual(svc.getMainBranch(), "main", "getMainBranch returns main when no milestone set");
954
+ assertEq(svc.getMainBranch(), "main", "getMainBranch returns main when no milestone set");
893
955
 
894
956
  // With milestone set, getMainBranch returns the integration branch
895
957
  svc.setMilestoneId("M001");
896
- assert.deepStrictEqual(svc.getMainBranch(), "f-123-feature", "getMainBranch returns integration branch when milestone set");
958
+ assertEq(svc.getMainBranch(), "f-123-feature", "getMainBranch returns integration branch when milestone set");
897
959
 
898
960
  rmSync(repo, { recursive: true, force: true });
899
- });
961
+ }
900
962
 
901
963
  // ─── getMainBranch: main_branch pref still takes priority ─────────────
902
964
 
903
- test('getMainBranch: main_branch pref overrides integration branch', () => {
965
+ console.log("\n=== getMainBranch: main_branch pref overrides integration branch ===");
966
+
967
+ {
904
968
  const repo = initBranchTestRepo();
905
969
 
906
970
  run("git checkout -b f-123-feature", repo);
@@ -912,14 +976,16 @@ describe('git-service', async () => {
912
976
  // Explicit preference still wins
913
977
  const svc = new GitServiceImpl(repo, { main_branch: "trunk" });
914
978
  svc.setMilestoneId("M001");
915
- assert.deepStrictEqual(svc.getMainBranch(), "trunk", "main_branch preference overrides integration branch");
979
+ assertEq(svc.getMainBranch(), "trunk", "main_branch preference overrides integration branch");
916
980
 
917
981
  rmSync(repo, { recursive: true, force: true });
918
- });
982
+ }
919
983
 
920
984
  // ─── getMainBranch: falls back when integration branch deleted ────────
921
985
 
922
- test('getMainBranch: fallback when integration branch deleted', () => {
986
+ console.log("\n=== getMainBranch: fallback when integration branch deleted ===");
987
+
988
+ {
923
989
  const repo = initBranchTestRepo();
924
990
 
925
991
  // Write metadata pointing to a branch that doesn't exist
@@ -927,67 +993,75 @@ describe('git-service', async () => {
927
993
 
928
994
  const svc = new GitServiceImpl(repo);
929
995
  svc.setMilestoneId("M001");
930
- assert.deepStrictEqual(svc.getMainBranch(), "main", "getMainBranch falls back to main when integration branch no longer exists");
996
+ assertEq(svc.getMainBranch(), "main", "getMainBranch falls back to main when integration branch no longer exists");
931
997
 
932
998
  rmSync(repo, { recursive: true, force: true });
933
- });
999
+ }
934
1000
 
935
1001
  // ─── resolveMilestoneIntegrationBranch: recorded branch wins when it exists ───
936
1002
 
937
- test('Integration branch: resolver prefers recorded branch', () => {
1003
+ console.log("\n=== Integration branch: resolver prefers recorded branch ===");
1004
+
1005
+ {
938
1006
  const repo = initBranchTestRepo();
939
1007
  run("git checkout -b feature/live", repo);
940
1008
  run("git checkout main", repo);
941
1009
  writeIntegrationBranch(repo, "M001", "feature/live");
942
1010
 
943
1011
  const resolved = resolveMilestoneIntegrationBranch(repo, "M001");
944
- assert.deepStrictEqual(resolved.status, "recorded", "resolver reports recorded branch when metadata branch exists");
945
- assert.deepStrictEqual(resolved.recordedBranch, "feature/live", "resolver includes recorded branch");
946
- assert.deepStrictEqual(resolved.effectiveBranch, "feature/live", "resolver uses recorded branch as effective branch");
1012
+ assertEq(resolved.status, "recorded", "resolver reports recorded branch when metadata branch exists");
1013
+ assertEq(resolved.recordedBranch, "feature/live", "resolver includes recorded branch");
1014
+ assertEq(resolved.effectiveBranch, "feature/live", "resolver uses recorded branch as effective branch");
947
1015
 
948
1016
  rmSync(repo, { recursive: true, force: true });
949
- });
1017
+ }
950
1018
 
951
1019
  // ─── resolveMilestoneIntegrationBranch: falls back to detected default ────────
952
1020
 
953
- test('Integration branch: resolver falls back to detected default', () => {
1021
+ console.log("\n=== Integration branch: resolver falls back to detected default ===");
1022
+
1023
+ {
954
1024
  const repo = initBranchTestRepo();
955
1025
  writeIntegrationBranch(repo, "M001", "deleted-branch");
956
1026
 
957
1027
  const resolved = resolveMilestoneIntegrationBranch(repo, "M001");
958
- assert.deepStrictEqual(resolved.status, "fallback", "resolver reports fallback when recorded branch is stale");
959
- assert.deepStrictEqual(resolved.recordedBranch, "deleted-branch", "resolver preserves stale recorded branch for diagnostics");
960
- assert.deepStrictEqual(resolved.effectiveBranch, "main", "resolver falls back to detected default branch");
961
- assert.ok(
1028
+ assertEq(resolved.status, "fallback", "resolver reports fallback when recorded branch is stale");
1029
+ assertEq(resolved.recordedBranch, "deleted-branch", "resolver preserves stale recorded branch for diagnostics");
1030
+ assertEq(resolved.effectiveBranch, "main", "resolver falls back to detected default branch");
1031
+ assertTrue(
962
1032
  resolved.reason.includes("deleted-branch") && resolved.reason.includes("main"),
963
1033
  "resolver reason mentions stale recorded branch and fallback branch",
964
1034
  );
965
1035
 
966
1036
  rmSync(repo, { recursive: true, force: true });
967
- });
1037
+ }
968
1038
 
969
1039
  // ─── resolveMilestoneIntegrationBranch: configured main_branch is fallback ─────
970
1040
 
971
- test('Integration branch: resolver uses configured fallback branch', () => {
1041
+ console.log("\n=== Integration branch: resolver uses configured fallback branch ===");
1042
+
1043
+ {
972
1044
  const repo = initBranchTestRepo();
973
1045
  run("git checkout -b trunk", repo);
974
1046
  run("git checkout main", repo);
975
1047
  writeIntegrationBranch(repo, "M001", "deleted-branch");
976
1048
 
977
1049
  const resolved = resolveMilestoneIntegrationBranch(repo, "M001", { main_branch: "trunk" });
978
- assert.deepStrictEqual(resolved.status, "fallback", "resolver reports fallback when using configured main_branch");
979
- assert.deepStrictEqual(resolved.effectiveBranch, "trunk", "resolver prefers configured main_branch as fallback");
980
- assert.ok(
1050
+ assertEq(resolved.status, "fallback", "resolver reports fallback when using configured main_branch");
1051
+ assertEq(resolved.effectiveBranch, "trunk", "resolver prefers configured main_branch as fallback");
1052
+ assertTrue(
981
1053
  resolved.reason.includes("deleted-branch") && resolved.reason.includes("trunk"),
982
1054
  "configured fallback reason mentions stale branch and configured branch",
983
1055
  );
984
1056
 
985
1057
  rmSync(repo, { recursive: true, force: true });
986
- });
1058
+ }
987
1059
 
988
1060
  // ─── Per-milestone isolation: different milestones, different targets ──
989
1061
 
990
- test('Integration branch: per-milestone isolation', () => {
1062
+ console.log("\n=== Integration branch: per-milestone isolation ===");
1063
+
1064
+ {
991
1065
  const repo = initBranchTestRepo();
992
1066
 
993
1067
  run("git checkout -b feature-a", repo);
@@ -1000,33 +1074,37 @@ describe('git-service', async () => {
1000
1074
  const svc = new GitServiceImpl(repo);
1001
1075
 
1002
1076
  svc.setMilestoneId("M001");
1003
- assert.deepStrictEqual(svc.getMainBranch(), "feature-a", "M001 integration branch is feature-a");
1077
+ assertEq(svc.getMainBranch(), "feature-a", "M001 integration branch is feature-a");
1004
1078
 
1005
1079
  svc.setMilestoneId("M002");
1006
- assert.deepStrictEqual(svc.getMainBranch(), "feature-b", "M002 integration branch is feature-b");
1080
+ assertEq(svc.getMainBranch(), "feature-b", "M002 integration branch is feature-b");
1007
1081
 
1008
1082
  svc.setMilestoneId(null);
1009
- assert.deepStrictEqual(svc.getMainBranch(), "main", "no milestone set → falls back to main");
1083
+ assertEq(svc.getMainBranch(), "main", "no milestone set → falls back to main");
1010
1084
 
1011
1085
  rmSync(repo, { recursive: true, force: true });
1012
- });
1086
+ }
1013
1087
 
1014
1088
  // ─── Backward compatibility: no metadata → existing behavior ──────────
1015
1089
 
1016
- test('Integration branch: backward compat', () => {
1090
+ console.log("\n=== Integration branch: backward compat ===");
1091
+
1092
+ {
1017
1093
  const repo = initBranchTestRepo();
1018
1094
  const svc = new GitServiceImpl(repo);
1019
1095
 
1020
1096
  // Set milestone but no metadata file exists
1021
1097
  svc.setMilestoneId("M001");
1022
- assert.deepStrictEqual(svc.getMainBranch(), "main", "backward compat: no metadata file → falls back to main");
1098
+ assertEq(svc.getMainBranch(), "main", "backward compat: no metadata file → falls back to main");
1023
1099
 
1024
1100
  rmSync(repo, { recursive: true, force: true });
1025
- });
1101
+ }
1026
1102
 
1027
1103
  // ─── untrackRuntimeFiles: removes tracked runtime files from index ───
1028
1104
 
1029
- test('untrackRuntimeFiles', async () => {
1105
+ console.log("\n=== untrackRuntimeFiles ===");
1106
+
1107
+ {
1030
1108
  const { untrackRuntimeFiles } = await import("../gitignore.ts");
1031
1109
  const repo = mkdtempSync(join(tmpdir(), "gsd-untrack-"));
1032
1110
  run("git init -b main", repo);
@@ -1047,36 +1125,38 @@ describe('git-service', async () => {
1047
1125
 
1048
1126
  // Precondition: runtime files are tracked
1049
1127
  const trackedBefore = run("git ls-files .gsd/", repo);
1050
- assert.ok(trackedBefore.includes("completed-units.json"), "untrack: precondition — completed-units tracked");
1051
- assert.ok(trackedBefore.includes("metrics.json"), "untrack: precondition — metrics tracked");
1128
+ assertTrue(trackedBefore.includes("completed-units.json"), "untrack: precondition — completed-units tracked");
1129
+ assertTrue(trackedBefore.includes("metrics.json"), "untrack: precondition — metrics tracked");
1052
1130
 
1053
1131
  // Run untrackRuntimeFiles
1054
1132
  untrackRuntimeFiles(repo);
1055
1133
 
1056
1134
  // Runtime files should be removed from the index
1057
1135
  const trackedAfter = run("git ls-files .gsd/", repo);
1058
- assert.deepStrictEqual(trackedAfter, "", "untrack: all runtime files removed from index");
1136
+ assertEq(trackedAfter, "", "untrack: all runtime files removed from index");
1059
1137
 
1060
1138
  // Non-runtime files remain tracked
1061
1139
  const srcTracked = run("git ls-files src.ts", repo);
1062
- assert.ok(srcTracked.includes("src.ts"), "untrack: non-runtime files remain tracked");
1140
+ assertTrue(srcTracked.includes("src.ts"), "untrack: non-runtime files remain tracked");
1063
1141
 
1064
1142
  // Files still exist on disk
1065
- assert.ok(existsSync(join(repo, ".gsd", "completed-units.json")),
1143
+ assertTrue(existsSync(join(repo, ".gsd", "completed-units.json")),
1066
1144
  "untrack: completed-units.json still on disk");
1067
- assert.ok(existsSync(join(repo, ".gsd", "metrics.json")),
1145
+ assertTrue(existsSync(join(repo, ".gsd", "metrics.json")),
1068
1146
  "untrack: metrics.json still on disk");
1069
1147
 
1070
1148
  // Idempotent — running again doesn't error
1071
1149
  untrackRuntimeFiles(repo);
1072
- assert.ok(true, "untrack: second call is idempotent (no error)");
1150
+ assertTrue(true, "untrack: second call is idempotent (no error)");
1073
1151
 
1074
1152
  rmSync(repo, { recursive: true, force: true });
1075
- });
1153
+ }
1076
1154
 
1077
1155
  // ─── smartStage excludes runtime files but allows milestone artifacts ──
1078
1156
 
1079
- test('smartStage excludes runtime files, allows milestone artifacts', () => {
1157
+ console.log("\n=== smartStage excludes runtime files, allows milestone artifacts ===");
1158
+
1159
+ {
1080
1160
  const repo = mkdtempSync(join(tmpdir(), "gsd-smart-stage-excludes-"));
1081
1161
  run("git init -b main", repo);
1082
1162
  run("git config user.email test@test.com", repo);
@@ -1098,65 +1178,71 @@ describe('git-service', async () => {
1098
1178
  // smartStage excludes only runtime paths, not all of .gsd/ (#1326)
1099
1179
  const svc = new GitServiceImpl(repo);
1100
1180
  const msg = svc.commit({ message: "test commit" });
1101
- assert.ok(msg !== null, "smartStage: commit succeeds");
1181
+ assertTrue(msg !== null, "smartStage: commit succeeds");
1102
1182
 
1103
1183
  const committed = run("git show --name-only HEAD", repo);
1104
- assert.ok(committed.includes("src.ts"), "smartStage: source files ARE in commit");
1184
+ assertTrue(committed.includes("src.ts"), "smartStage: source files ARE in commit");
1105
1185
  // Runtime files should NOT be committed
1106
- assert.ok(!committed.includes(".gsd/STATE.md"), "smartStage: STATE.md excluded (runtime)");
1107
- assert.ok(!committed.includes(".gsd/runtime/"), "smartStage: runtime/ excluded");
1108
- assert.ok(!committed.includes(".gsd/activity/"), "smartStage: activity/ excluded");
1186
+ assertTrue(!committed.includes(".gsd/STATE.md"), "smartStage: STATE.md excluded (runtime)");
1187
+ assertTrue(!committed.includes(".gsd/runtime/"), "smartStage: runtime/ excluded");
1188
+ assertTrue(!committed.includes(".gsd/activity/"), "smartStage: activity/ excluded");
1109
1189
  // Milestone artifacts SHOULD be committed when not gitignored (#1326)
1110
- assert.ok(committed.includes(".gsd/milestones/"), "smartStage: milestone artifacts ARE committed");
1190
+ assertTrue(committed.includes(".gsd/milestones/"), "smartStage: milestone artifacts ARE committed");
1111
1191
 
1112
1192
  rmSync(repo, { recursive: true, force: true });
1113
- });
1193
+ }
1114
1194
 
1115
1195
  // ─── writeIntegrationBranch: no commit (metadata in external storage) ──
1116
1196
 
1117
- test('writeIntegrationBranch: no commit', () => {
1197
+ console.log("\n=== writeIntegrationBranch: no commit ===");
1198
+
1199
+ {
1118
1200
  const repo = initBranchTestRepo();
1119
1201
  const commitsBefore = run("git rev-list --count HEAD", repo);
1120
1202
 
1121
1203
  writeIntegrationBranch(repo, "M001", "f-123-new-thing");
1122
1204
 
1123
1205
  // File should still be written to disk
1124
- assert.deepStrictEqual(readIntegrationBranch(repo, "M001"), "f-123-new-thing",
1206
+ assertEq(readIntegrationBranch(repo, "M001"), "f-123-new-thing",
1125
1207
  "writeIntegrationBranch: metadata file exists on disk");
1126
1208
 
1127
1209
  // No commit — .gsd/ is managed externally
1128
1210
  const commitsAfter = run("git rev-list --count HEAD", repo);
1129
- assert.deepStrictEqual(commitsBefore, commitsAfter,
1211
+ assertEq(commitsBefore, commitsAfter,
1130
1212
  "writeIntegrationBranch: no git commit created for integration branch");
1131
1213
 
1132
1214
  rmSync(repo, { recursive: true, force: true });
1133
- });
1215
+ }
1134
1216
 
1135
1217
  // ─── ensureGitignore: always adds .gsd to gitignore ──────────────────
1136
1218
 
1137
- test('ensureGitignore: adds .gsd entry', async () => {
1219
+ console.log("\n=== ensureGitignore: adds .gsd entry ===");
1220
+
1221
+ {
1138
1222
  const { ensureGitignore } = await import("../gitignore.ts");
1139
1223
  const repo = mkdtempSync(join(tmpdir(), "gsd-gitignore-external-state-"));
1140
1224
 
1141
1225
  // Should add .gsd to gitignore (external state dir is a symlink)
1142
1226
  const modified = ensureGitignore(repo);
1143
- assert.ok(modified, "ensureGitignore: gitignore was modified");
1227
+ assertTrue(modified, "ensureGitignore: gitignore was modified");
1144
1228
 
1145
1229
  const { readFileSync } = await import("node:fs");
1146
1230
  const content = readFileSync(join(repo, ".gitignore"), "utf-8");
1147
1231
  const lines = content.split("\n").map(l => l.trim()).filter(l => l && !l.startsWith("#"));
1148
- assert.ok(lines.includes(".gsd"), "ensureGitignore: .gitignore contains .gsd");
1232
+ assertTrue(lines.includes(".gsd"), "ensureGitignore: .gitignore contains .gsd");
1149
1233
 
1150
1234
  // Idempotent — calling again doesn't add duplicates
1151
1235
  const modified2 = ensureGitignore(repo);
1152
- assert.ok(!modified2, "ensureGitignore: second call is idempotent");
1236
+ assertTrue(!modified2, "ensureGitignore: second call is idempotent");
1153
1237
 
1154
1238
  rmSync(repo, { recursive: true, force: true });
1155
- });
1239
+ }
1156
1240
 
1157
1241
  // ─── nativeAddAllWithExclusions: symlinked .gsd fallback ───────────────
1158
1242
 
1159
- test('nativeAddAllWithExclusions: symlinked .gsd fallback', () => {
1243
+ console.log("\n=== nativeAddAllWithExclusions: symlinked .gsd fallback ===");
1244
+
1245
+ {
1160
1246
  // When .gsd is a symlink, git rejects `:!.gsd/...` pathspecs with
1161
1247
  // "fatal: pathspec '...' is beyond a symbolic link". The fix falls
1162
1248
  // back to plain `git add -A`, which respects .gitignore.
@@ -1185,20 +1271,22 @@ describe('git-service', async () => {
1185
1271
  threw = true;
1186
1272
  console.error(" unexpected error:", e);
1187
1273
  }
1188
- assert.ok(!threw, "nativeAddAllWithExclusions does not throw with symlinked .gsd");
1274
+ assertTrue(!threw, "nativeAddAllWithExclusions does not throw with symlinked .gsd");
1189
1275
 
1190
1276
  // Verify the real file was staged
1191
1277
  const staged = run("git diff --cached --name-only", repo);
1192
- assert.ok(staged.includes("src/app.ts"), "real file staged despite symlinked .gsd");
1193
- assert.ok(!staged.includes(".gsd"), ".gsd content not staged");
1278
+ assertTrue(staged.includes("src/app.ts"), "real file staged despite symlinked .gsd");
1279
+ assertTrue(!staged.includes(".gsd"), ".gsd content not staged");
1194
1280
 
1195
1281
  rmSync(repo, { recursive: true, force: true });
1196
1282
  rmSync(externalGsd, { recursive: true, force: true });
1197
- });
1283
+ }
1198
1284
 
1199
1285
  // ─── nativeAddAllWithExclusions: non-symlinked .gsd still works ───────
1200
1286
 
1201
- test('nativeAddAllWithExclusions: non-symlinked .gsd still works', () => {
1287
+ console.log("\n=== nativeAddAllWithExclusions: non-symlinked .gsd still works ===");
1288
+
1289
+ {
1202
1290
  // Verify the normal (non-symlink) case still works with pathspec exclusions
1203
1291
  const repo = initTempRepo();
1204
1292
 
@@ -1212,91 +1300,96 @@ describe('git-service', async () => {
1212
1300
  } catch {
1213
1301
  threw = true;
1214
1302
  }
1215
- assert.ok(!threw, "nativeAddAllWithExclusions works with normal .gsd directory");
1303
+ assertTrue(!threw, "nativeAddAllWithExclusions works with normal .gsd directory");
1216
1304
 
1217
1305
  const staged = run("git diff --cached --name-only", repo);
1218
- assert.ok(staged.includes("src/code.ts"), "real file staged with normal .gsd");
1306
+ assertTrue(staged.includes("src/code.ts"), "real file staged with normal .gsd");
1219
1307
 
1220
1308
  rmSync(repo, { recursive: true, force: true });
1221
- });
1309
+ }
1222
1310
 
1223
1311
  // ─── MergeConflictError: constructor fields ───────────────────────────────
1224
1312
 
1225
- test('MergeConflictError: constructor fields', () => {
1313
+ console.log("\n=== MergeConflictError: constructor fields ===");
1314
+ {
1226
1315
  const err = new MergeConflictError(
1227
1316
  ["src/foo.ts", "src/bar.ts"],
1228
1317
  "squash",
1229
1318
  "gsd/M001/S01",
1230
1319
  "main",
1231
1320
  );
1232
- assert.deepStrictEqual(err.conflictedFiles, ["src/foo.ts", "src/bar.ts"], "MergeConflictError.conflictedFiles populated");
1233
- assert.deepStrictEqual(err.strategy, "squash", "MergeConflictError.strategy set");
1234
- assert.deepStrictEqual(err.branch, "gsd/M001/S01", "MergeConflictError.branch set");
1235
- assert.deepStrictEqual(err.mainBranch, "main", "MergeConflictError.mainBranch set");
1236
- assert.deepStrictEqual(err.name, "MergeConflictError", "MergeConflictError.name is MergeConflictError");
1237
- assert.ok(err.message.includes("src/foo.ts"), "MergeConflictError message lists conflicted files");
1238
- assert.ok(err.message.toLowerCase().includes("squash"), "MergeConflictError message mentions strategy");
1239
- assert.ok(err instanceof MergeConflictError, "MergeConflictError is an instanceof MergeConflictError");
1240
- assert.ok(err instanceof Error, "MergeConflictError is an Error instance");
1241
- });
1321
+ assertEq(err.conflictedFiles, ["src/foo.ts", "src/bar.ts"], "MergeConflictError.conflictedFiles populated");
1322
+ assertEq(err.strategy, "squash", "MergeConflictError.strategy set");
1323
+ assertEq(err.branch, "gsd/M001/S01", "MergeConflictError.branch set");
1324
+ assertEq(err.mainBranch, "main", "MergeConflictError.mainBranch set");
1325
+ assertEq(err.name, "MergeConflictError", "MergeConflictError.name is MergeConflictError");
1326
+ assertTrue(err.message.includes("src/foo.ts"), "MergeConflictError message lists conflicted files");
1327
+ assertTrue(err.message.toLowerCase().includes("squash"), "MergeConflictError message mentions strategy");
1328
+ assertTrue(err instanceof MergeConflictError, "MergeConflictError is an instanceof MergeConflictError");
1329
+ assertTrue(err instanceof Error, "MergeConflictError is an Error instance");
1330
+ }
1242
1331
 
1243
1332
  // ─── Integration branch: rejects gsd/quick/* branches ────────────────────
1244
1333
 
1245
- test('Integration branch: rejects gsd/quick/* branches', () => {
1334
+ console.log("\n=== Integration branch: rejects gsd/quick/* branches ===");
1335
+ {
1246
1336
  const repo = initBranchTestRepo();
1247
1337
 
1248
1338
  writeIntegrationBranch(repo, "M001", "gsd/quick/1234-some-task");
1249
- assert.deepStrictEqual(readIntegrationBranch(repo, "M001"), null, "gsd/quick/* branches are not recorded as integration branch");
1339
+ assertEq(readIntegrationBranch(repo, "M001"), null, "gsd/quick/* branches are not recorded as integration branch");
1250
1340
 
1251
1341
  rmSync(repo, { recursive: true, force: true });
1252
- });
1342
+ }
1253
1343
 
1254
1344
  // ─── Integration branch: resolver returns missing when no metadata ────────
1255
1345
 
1256
- test('Integration branch: resolver returns missing when no metadata', () => {
1346
+ console.log("\n=== Integration branch: resolver returns missing when no metadata ===");
1347
+ {
1257
1348
  const repo = initBranchTestRepo();
1258
1349
 
1259
1350
  // No writeIntegrationBranch call — no metadata file exists
1260
1351
  const resolved = resolveMilestoneIntegrationBranch(repo, "M999");
1261
- assert.deepStrictEqual(resolved.status, "missing", "resolver reports missing when no metadata file");
1262
- assert.deepStrictEqual(resolved.recordedBranch, null, "resolver recordedBranch is null when no metadata");
1263
- assert.deepStrictEqual(resolved.effectiveBranch, null, "resolver effectiveBranch is null when no metadata");
1264
- assert.ok(resolved.reason.includes("M999"), "resolver reason mentions the milestone ID");
1352
+ assertEq(resolved.status, "missing", "resolver reports missing when no metadata file");
1353
+ assertEq(resolved.recordedBranch, null, "resolver recordedBranch is null when no metadata");
1354
+ assertEq(resolved.effectiveBranch, null, "resolver effectiveBranch is null when no metadata");
1355
+ assertTrue(resolved.reason.includes("M999"), "resolver reason mentions the milestone ID");
1265
1356
 
1266
1357
  rmSync(repo, { recursive: true, force: true });
1267
- });
1358
+ }
1268
1359
 
1269
1360
  // ─── Integration branch: resolver missing when both recorded and configured branches gone ───
1270
1361
 
1271
- test('Integration branch: resolver missing when both recorded and configured branches gone', () => {
1362
+ console.log("\n=== Integration branch: resolver missing when both recorded and configured branches gone ===");
1363
+ {
1272
1364
  const repo = initBranchTestRepo();
1273
1365
 
1274
1366
  // Record a branch that doesn't exist
1275
1367
  writeIntegrationBranch(repo, "M001", "deleted-feature");
1276
1368
  // configured main_branch also doesn't exist
1277
1369
  const resolved = resolveMilestoneIntegrationBranch(repo, "M001", { main_branch: "nonexistent-branch" });
1278
- assert.deepStrictEqual(resolved.status, "missing", "resolver reports missing when recorded branch and configured main_branch both absent");
1279
- assert.deepStrictEqual(resolved.recordedBranch, "deleted-feature", "resolver preserves stale recorded branch");
1280
- assert.deepStrictEqual(resolved.effectiveBranch, null, "resolver effectiveBranch is null when no safe fallback");
1281
- assert.ok(
1370
+ assertEq(resolved.status, "missing", "resolver reports missing when recorded branch and configured main_branch both absent");
1371
+ assertEq(resolved.recordedBranch, "deleted-feature", "resolver preserves stale recorded branch");
1372
+ assertEq(resolved.effectiveBranch, null, "resolver effectiveBranch is null when no safe fallback");
1373
+ assertTrue(
1282
1374
  resolved.reason.includes("deleted-feature") && resolved.reason.includes("nonexistent-branch"),
1283
1375
  "reason mentions both stale branch and unavailable configured branch",
1284
1376
  );
1285
1377
 
1286
1378
  rmSync(repo, { recursive: true, force: true });
1287
- });
1379
+ }
1288
1380
 
1289
1381
  // ─── buildTaskCommitMessage: issueNumber appends Resolves trailer ─────────
1290
1382
 
1291
- test('buildTaskCommitMessage: issueNumber appends Resolves trailer', () => {
1383
+ console.log("\n=== buildTaskCommitMessage: issueNumber appends Resolves trailer ===");
1384
+ {
1292
1385
  const msg = buildTaskCommitMessage({
1293
1386
  taskId: "S01/T03",
1294
1387
  taskTitle: "fix login redirect",
1295
1388
  issueNumber: 42,
1296
1389
  });
1297
- assert.ok(msg.includes("Resolves #42"), "buildTaskCommitMessage includes Resolves #N trailer when issueNumber is set");
1298
- assert.ok(msg.startsWith("fix(S01/T03):"), "buildTaskCommitMessage infers fix type");
1299
- });
1390
+ assertTrue(msg.includes("Resolves #42"), "buildTaskCommitMessage includes Resolves #N trailer when issueNumber is set");
1391
+ assertTrue(msg.startsWith("fix(S01/T03):"), "buildTaskCommitMessage infers fix type");
1392
+ }
1300
1393
 
1301
1394
  {
1302
1395
  // No issueNumber — no Resolves trailer
@@ -1304,26 +1397,29 @@ describe('git-service', async () => {
1304
1397
  taskId: "S01/T04",
1305
1398
  taskTitle: "add dashboard widget",
1306
1399
  });
1307
- assert.ok(!msg.includes("Resolves"), "buildTaskCommitMessage omits Resolves trailer when issueNumber is absent");
1400
+ assertTrue(!msg.includes("Resolves"), "buildTaskCommitMessage omits Resolves trailer when issueNumber is absent");
1308
1401
  }
1309
1402
 
1310
1403
  // ─── runPreMergeCheck: skips when no package.json ────────────────────────
1311
1404
 
1312
- test('runPreMergeCheck: skips when no package.json', () => {
1405
+ console.log("\n=== runPreMergeCheck: skips when no package.json ===");
1406
+ {
1313
1407
  const repo = initBranchTestRepo();
1314
1408
  // No package.json created — auto-detect should skip gracefully
1315
1409
  const svc = new GitServiceImpl(repo, { pre_merge_check: true });
1316
1410
  const result: PreMergeCheckResult = svc.runPreMergeCheck();
1317
1411
 
1318
- assert.deepStrictEqual(result.passed, true, "runPreMergeCheck passes when no package.json (skip)");
1319
- assert.deepStrictEqual(result.skipped, true, "runPreMergeCheck skips when no package.json found");
1412
+ assertEq(result.passed, true, "runPreMergeCheck passes when no package.json (skip)");
1413
+ assertEq(result.skipped, true, "runPreMergeCheck skips when no package.json found");
1320
1414
 
1321
1415
  rmSync(repo, { recursive: true, force: true });
1322
- });
1416
+ }
1323
1417
 
1324
1418
  // ─── autoCommit: symlinked .gsd does NOT stage milestone artifacts (#2247) ──
1325
1419
 
1326
- test('autoCommit: symlinked .gsd does NOT stage milestone artifacts (#2247)', () => {
1420
+ console.log("\n=== autoCommit: symlinked .gsd does NOT stage milestone artifacts (#2247) ===");
1421
+
1422
+ {
1327
1423
  // When .gsd is a symlink (external state project), .gsd/ files live outside
1328
1424
  // the repo by design. smartStage() must NOT force-stage them into git — the
1329
1425
  // .gitignore exclusion is correct and intentional.
@@ -1352,14 +1448,21 @@ describe('git-service', async () => {
1352
1448
 
1353
1449
  const svc = new GitServiceImpl(repo);
1354
1450
  const msg = svc.autoCommit("complete-milestone", "M009");
1355
- assert.ok(msg !== null, "symlink autoCommit: commit succeeds");
1451
+ assertTrue(msg !== null, "symlink autoCommit: commit succeeds");
1356
1452
 
1357
1453
  const committed = run("git show --name-only HEAD", repo);
1358
- assert.ok(committed.includes("src/feature.ts"), "symlink autoCommit: source file committed");
1359
- assert.ok(!committed.includes(".gsd/milestones/"),
1454
+ assertTrue(committed.includes("src/feature.ts"), "symlink autoCommit: source file committed");
1455
+ assertTrue(!committed.includes(".gsd/milestones/"),
1360
1456
  "symlink autoCommit: .gsd/milestones/ files are NOT staged (external state stays external)");
1361
1457
 
1362
1458
  try { rmSync(repo, { recursive: true, force: true }); } catch {}
1363
1459
  try { rmSync(externalGsd, { recursive: true, force: true }); } catch {}
1364
- });
1460
+ }
1461
+
1462
+ report();
1463
+ }
1464
+
1465
+ main().catch((error) => {
1466
+ console.error(error);
1467
+ process.exit(1);
1365
1468
  });