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
@@ -13,12 +13,11 @@
13
13
  * - Cost projection with budget ceiling awareness
14
14
  */
15
15
 
16
- import { describe, test } from 'node:test';
17
- import assert from 'node:assert/strict';
18
16
  import { mkdtempSync, mkdirSync, rmSync, writeFileSync, readFileSync } from 'node:fs';
19
17
  import { join } from 'node:path';
20
18
  import { tmpdir } from 'node:os';
21
19
 
20
+ import { createTestContext } from './test-helpers.ts';
22
21
  import {
23
22
  registerWorker,
24
23
  updateWorker,
@@ -44,6 +43,8 @@ import {
44
43
  predictRemainingCost,
45
44
  } from '../metrics.ts';
46
45
 
46
+ const { assertEq, assertTrue, assertMatch, report } = createTestContext();
47
+
47
48
  // ─── Fixture helpers ──────────────────────────────────────────────────────────
48
49
 
49
50
  function createFixtureBase(): string {
@@ -82,9 +83,9 @@ function cleanup(base: string): void {
82
83
 
83
84
  // ─── E2E: Parallel workers across M001 and M002 ──────────────────────────────
84
85
 
86
+ console.log("\n=== E2E: Parallel workers across milestones ===");
85
87
 
86
- describe('parallel-workers-multi-milestone-e2e', () => {
87
- test('E2E: Parallel workers across milestones', () => {
88
+ {
88
89
  resetWorkerRegistry();
89
90
  const base = createFixtureBase();
90
91
 
@@ -98,49 +99,52 @@ test('E2E: Parallel workers across milestones', () => {
98
99
  const w2 = registerWorker("researcher", "Research M001 APIs", 1, 3, batch1Id);
99
100
  const w3 = registerWorker("worker", "Implement M001 feature", 2, 3, batch1Id);
100
101
 
101
- assert.deepStrictEqual(getActiveWorkers().length, 3, "M001: 3 parallel workers registered");
102
- assert.ok(hasActiveWorkers(), "M001: has active workers");
102
+ assertEq(getActiveWorkers().length, 3, "M001: 3 parallel workers registered");
103
+ assertTrue(hasActiveWorkers(), "M001: has active workers");
103
104
 
104
105
  const batches1 = getWorkerBatches();
105
- assert.deepStrictEqual(batches1.size, 1, "M001: single batch");
106
- assert.deepStrictEqual(batches1.get(batch1Id)!.length, 3, "M001: batch has 3 workers");
106
+ assertEq(batches1.size, 1, "M001: single batch");
107
+ assertEq(batches1.get(batch1Id)!.length, 3, "M001: batch has 3 workers");
107
108
 
108
109
  // Complete M001 workers
109
110
  updateWorker(w1, "completed");
110
111
  updateWorker(w2, "completed");
111
112
  updateWorker(w3, "completed");
112
- assert.ok(!hasActiveWorkers(), "M001: no active workers after completion");
113
+ assertTrue(!hasActiveWorkers(), "M001: no active workers after completion");
113
114
 
114
115
  // Simulate M002 parallel workers (batch 2) — overlapping with M001 cleanup
115
116
  const batch2Id = "batch-m002";
116
117
  const w4 = registerWorker("scout", "Explore M002 codebase", 0, 2, batch2Id);
117
118
  const w5 = registerWorker("worker", "Implement M002 feature", 1, 2, batch2Id);
118
119
 
119
- assert.ok(hasActiveWorkers(), "M002: has active workers");
120
+ assertTrue(hasActiveWorkers(), "M002: has active workers");
120
121
  const batches2 = getWorkerBatches();
121
122
  // M001 workers may still be in cleanup window (5s timeout), M002 workers are active
122
- assert.ok(batches2.has(batch2Id), "M002: batch exists");
123
- assert.deepStrictEqual(batches2.get(batch2Id)!.length, 2, "M002: batch has 2 workers");
123
+ assertTrue(batches2.has(batch2Id), "M002: batch exists");
124
+ assertEq(batches2.get(batch2Id)!.length, 2, "M002: batch has 2 workers");
124
125
 
125
126
  // One worker fails in M002
126
127
  updateWorker(w4, "completed");
127
128
  updateWorker(w5, "failed");
128
- assert.ok(!hasActiveWorkers(), "M002: no active workers after all finish");
129
+ assertTrue(!hasActiveWorkers(), "M002: no active workers after all finish");
129
130
 
130
131
  // Verify worker statuses reflect correctly
131
132
  const allWorkers = getActiveWorkers();
132
133
  const m002Workers = allWorkers.filter(w => w.batchId === batch2Id);
133
134
  if (m002Workers.length > 0) {
134
135
  const failedWorker = m002Workers.find(w => w.status === "failed");
135
- assert.ok(failedWorker !== undefined, "M002: failed worker tracked");
136
- assert.deepStrictEqual(failedWorker?.agent, "worker", "M002: failed worker is 'worker'");
136
+ assertTrue(failedWorker !== undefined, "M002: failed worker tracked");
137
+ assertEq(failedWorker?.agent, "worker", "M002: failed worker is 'worker'");
137
138
  }
138
139
 
139
140
  cleanup(base);
140
- });
141
+ }
141
142
 
142
143
  // ─── E2E: Metrics accumulation across milestones ──────────────────────────────
143
- test('E2E: Metrics across milestones', () => {
144
+
145
+ console.log("\n=== E2E: Metrics across milestones ===");
146
+
147
+ {
144
148
  const base = createFixtureBase();
145
149
 
146
150
  // Build a ledger spanning two milestones
@@ -171,84 +175,90 @@ test('E2E: Metrics across milestones', () => {
171
175
 
172
176
  // Verify totals
173
177
  const totals = getProjectTotals(loaded.units);
174
- assert.deepStrictEqual(totals.units, 13, "metrics: 13 total units across M001+M002");
178
+ assertEq(totals.units, 13, "metrics: 13 total units across M001+M002");
175
179
  const totalCost = loaded.units.reduce((sum, u) => sum + u.cost, 0);
176
- assert.ok(Math.abs(totals.cost - totalCost) < 0.001, "metrics: total cost matches sum");
180
+ assertTrue(Math.abs(totals.cost - totalCost) < 0.001, "metrics: total cost matches sum");
177
181
 
178
182
  // Verify phase aggregation
179
183
  const phases = aggregateByPhase(loaded.units);
180
184
  const research = phases.find(p => p.phase === "research");
181
- assert.ok(research !== undefined, "metrics: research phase exists");
182
- assert.deepStrictEqual(research!.units, 2, "metrics: 2 research units (M001 + M002)");
185
+ assertTrue(research !== undefined, "metrics: research phase exists");
186
+ assertEq(research!.units, 2, "metrics: 2 research units (M001 + M002)");
183
187
 
184
188
  const execution = phases.find(p => p.phase === "execution");
185
- assert.ok(execution !== undefined, "metrics: execution phase exists");
186
- assert.deepStrictEqual(execution!.units, 4, "metrics: 4 execution units across both milestones");
189
+ assertTrue(execution !== undefined, "metrics: execution phase exists");
190
+ assertEq(execution!.units, 4, "metrics: 4 execution units across both milestones");
187
191
 
188
192
  // Verify slice aggregation
189
193
  const slices = aggregateBySlice(loaded.units);
190
- assert.ok(slices.length >= 4, "metrics: at least 4 slice aggregates (M001/S01, M001/S02, M002/S01, milestone-level)");
194
+ assertTrue(slices.length >= 4, "metrics: at least 4 slice aggregates (M001/S01, M001/S02, M002/S01, milestone-level)");
191
195
 
192
196
  const m001s01 = slices.find(s => s.sliceId === "M001/S01");
193
- assert.ok(m001s01 !== undefined, "metrics: M001/S01 slice aggregate exists");
197
+ assertTrue(m001s01 !== undefined, "metrics: M001/S01 slice aggregate exists");
194
198
  // M001/S01 has: plan-slice + T01 + T02 + complete-slice = 4 units
195
- assert.deepStrictEqual(m001s01!.units, 4, "metrics: M001/S01 has 4 units");
199
+ assertEq(m001s01!.units, 4, "metrics: M001/S01 has 4 units");
196
200
 
197
201
  // Cost projection
198
202
  const projLines = formatCostProjection(slices, 3, 2.0);
199
- assert.ok(projLines.length >= 1, "metrics: cost projection generated");
200
- assert.match(projLines[0], /Projected remaining/, "metrics: projection line text");
203
+ assertTrue(projLines.length >= 1, "metrics: cost projection generated");
204
+ assertMatch(projLines[0], /Projected remaining/, "metrics: projection line text");
201
205
 
202
206
  cleanup(base);
203
- });
207
+ }
204
208
 
205
209
  // ─── E2E: Budget alert progression through all thresholds ─────────────────────
206
- test('E2E: Budget alert progression 0→75→80→90→100', () => {
210
+
211
+ console.log("\n=== E2E: Budget alert progression 0→75→80→90→100 ===");
212
+
213
+ {
207
214
  // Simulate spending progression against a $10 budget ceiling
208
215
  const ceiling = 10.0;
209
216
 
210
217
  // Start: 50% spent
211
218
  let lastLevel = getBudgetAlertLevel(5.0 / ceiling);
212
- assert.deepStrictEqual(lastLevel, 0, "budget: 50% → level 0");
213
- assert.deepStrictEqual(getNewBudgetAlertLevel(0, 5.0 / ceiling), null, "budget: no alert at 50%");
219
+ assertEq(lastLevel, 0, "budget: 50% → level 0");
220
+ assertEq(getNewBudgetAlertLevel(0, 5.0 / ceiling), null, "budget: no alert at 50%");
214
221
 
215
222
  // Spend to 75%
216
223
  let newLevel = getNewBudgetAlertLevel(lastLevel, 7.5 / ceiling);
217
- assert.deepStrictEqual(newLevel, 75, "budget: alert fires at 75%");
224
+ assertEq(newLevel, 75, "budget: alert fires at 75%");
218
225
  lastLevel = newLevel!;
219
226
 
220
227
  // Spend to 78% — no alert (between 75 and 80)
221
- assert.deepStrictEqual(getNewBudgetAlertLevel(lastLevel, 7.8 / ceiling), null, "budget: no alert at 78%");
228
+ assertEq(getNewBudgetAlertLevel(lastLevel, 7.8 / ceiling), null, "budget: no alert at 78%");
222
229
 
223
230
  // Spend to 80% — 80% approach alert
224
231
  newLevel = getNewBudgetAlertLevel(lastLevel, 8.0 / ceiling);
225
- assert.deepStrictEqual(newLevel, 80, "budget: approach alert fires at 80%");
232
+ assertEq(newLevel, 80, "budget: approach alert fires at 80%");
226
233
  lastLevel = newLevel!;
227
234
 
228
235
  // Spend to 85% — no alert (still at 80 level)
229
- assert.deepStrictEqual(getNewBudgetAlertLevel(lastLevel, 8.5 / ceiling), null, "budget: no alert at 85%");
236
+ assertEq(getNewBudgetAlertLevel(lastLevel, 8.5 / ceiling), null, "budget: no alert at 85%");
230
237
 
231
238
  // Spend to 90%
232
239
  newLevel = getNewBudgetAlertLevel(lastLevel, 9.0 / ceiling);
233
- assert.deepStrictEqual(newLevel, 90, "budget: alert fires at 90%");
240
+ assertEq(newLevel, 90, "budget: alert fires at 90%");
234
241
  lastLevel = newLevel!;
235
242
 
236
243
  // Spend to 100%
237
244
  newLevel = getNewBudgetAlertLevel(lastLevel, 10.0 / ceiling);
238
- assert.deepStrictEqual(newLevel, 100, "budget: alert fires at 100%");
245
+ assertEq(newLevel, 100, "budget: alert fires at 100%");
239
246
  lastLevel = newLevel!;
240
247
 
241
248
  // Over budget — no re-emission
242
- assert.deepStrictEqual(getNewBudgetAlertLevel(lastLevel, 12.0 / ceiling), null, "budget: no re-alert over 100%");
249
+ assertEq(getNewBudgetAlertLevel(lastLevel, 12.0 / ceiling), null, "budget: no re-alert over 100%");
243
250
 
244
251
  // Enforcement at 80% — still "none" (enforcement only at 100%)
245
- assert.deepStrictEqual(getBudgetEnforcementAction("pause", 0.80), "none", "budget: no enforcement at 80%");
246
- assert.deepStrictEqual(getBudgetEnforcementAction("halt", 0.80), "none", "budget: no enforcement at 80%");
247
- assert.deepStrictEqual(getBudgetEnforcementAction("warn", 0.80), "none", "budget: no enforcement at 80%");
248
- });
252
+ assertEq(getBudgetEnforcementAction("pause", 0.80), "none", "budget: no enforcement at 80%");
253
+ assertEq(getBudgetEnforcementAction("halt", 0.80), "none", "budget: no enforcement at 80%");
254
+ assertEq(getBudgetEnforcementAction("warn", 0.80), "none", "budget: no enforcement at 80%");
255
+ }
249
256
 
250
257
  // ─── E2E: Budget prediction with multi-milestone cost data ────────────────────
251
- test('E2E: Budget prediction across milestones', () => {
258
+
259
+ console.log("\n=== E2E: Budget prediction across milestones ===");
260
+
261
+ {
252
262
  const units: UnitMetrics[] = [
253
263
  makeUnit({ type: "execute-task", id: "M001/S01/T01", cost: 0.10 }),
254
264
  makeUnit({ type: "execute-task", id: "M001/S01/T02", cost: 0.15 }),
@@ -258,27 +268,30 @@ test('E2E: Budget prediction across milestones', () => {
258
268
  ];
259
269
 
260
270
  const avgCosts = getAverageCostPerUnitType(units);
261
- assert.ok(avgCosts.has("execute-task"), "prediction: has execute-task average");
262
- assert.ok(avgCosts.has("plan-slice"), "prediction: has plan-slice average");
271
+ assertTrue(avgCosts.has("execute-task"), "prediction: has execute-task average");
272
+ assertTrue(avgCosts.has("plan-slice"), "prediction: has plan-slice average");
263
273
 
264
274
  // Average execute-task cost: (0.10 + 0.15 + 0.20) / 3 = 0.15
265
275
  const execAvg = avgCosts.get("execute-task")!;
266
- assert.ok(Math.abs(execAvg - 0.15) < 0.001, `prediction: execute-task avg is $0.15 (got ${execAvg})`);
276
+ assertTrue(Math.abs(execAvg - 0.15) < 0.001, `prediction: execute-task avg is $0.15 (got ${execAvg})`);
267
277
 
268
278
  // Average plan-slice cost: (0.05 + 0.08) / 2 = 0.065
269
279
  const planAvg = avgCosts.get("plan-slice")!;
270
- assert.ok(Math.abs(planAvg - 0.065) < 0.001, `prediction: plan-slice avg is $0.065 (got ${planAvg})`);
280
+ assertTrue(Math.abs(planAvg - 0.065) < 0.001, `prediction: plan-slice avg is $0.065 (got ${planAvg})`);
271
281
 
272
282
  // Predict remaining cost for 3 more execute-tasks and 1 plan-slice
273
283
  const remaining = predictRemainingCost(avgCosts, [
274
284
  "execute-task", "execute-task", "execute-task", "plan-slice",
275
285
  ]);
276
286
  // Expected: 3 * 0.15 + 1 * 0.065 = 0.515
277
- assert.ok(Math.abs(remaining - 0.515) < 0.001, `prediction: remaining cost ~$0.515 (got ${remaining})`);
278
- });
287
+ assertTrue(Math.abs(remaining - 0.515) < 0.001, `prediction: remaining cost ~$0.515 (got ${remaining})`);
288
+ }
279
289
 
280
290
  // ─── E2E: Parallel workers + budget alerts combined scenario ──────────────────
281
- test('E2E: Combined parallel workers + budget monitoring', () => {
291
+
292
+ console.log("\n=== E2E: Combined parallel workers + budget monitoring ===");
293
+
294
+ {
282
295
  resetWorkerRegistry();
283
296
 
284
297
  // Simulate a scenario: 3 parallel workers running while budget is at 78%
@@ -290,31 +303,34 @@ test('E2E: Combined parallel workers + budget monitoring', () => {
290
303
  // Budget is at 78% — no alert yet (between 75 and 80)
291
304
  const ceiling = 10.0;
292
305
  let lastLevel: ReturnType<typeof getBudgetAlertLevel> = 75; // already got 75% alert
293
- assert.deepStrictEqual(getNewBudgetAlertLevel(lastLevel, 7.8 / ceiling), null, "combined: no alert at 78% with workers running");
294
- assert.ok(hasActiveWorkers(), "combined: workers running during budget check");
306
+ assertEq(getNewBudgetAlertLevel(lastLevel, 7.8 / ceiling), null, "combined: no alert at 78% with workers running");
307
+ assertTrue(hasActiveWorkers(), "combined: workers running during budget check");
295
308
 
296
309
  // First worker completes, cost rises to 80%
297
310
  updateWorker(w1, "completed");
298
311
  const level80 = getNewBudgetAlertLevel(lastLevel, 8.0 / ceiling);
299
- assert.deepStrictEqual(level80, 80, "combined: 80% approach alert fires after worker completes");
312
+ assertEq(level80, 80, "combined: 80% approach alert fires after worker completes");
300
313
  lastLevel = level80!;
301
314
 
302
315
  // Second worker completes, cost rises to 88%
303
316
  updateWorker(w2, "completed");
304
- assert.deepStrictEqual(getNewBudgetAlertLevel(lastLevel, 8.8 / ceiling), null, "combined: no alert at 88%");
317
+ assertEq(getNewBudgetAlertLevel(lastLevel, 8.8 / ceiling), null, "combined: no alert at 88%");
305
318
 
306
319
  // Third worker completes, cost reaches 90%
307
320
  updateWorker(w3, "completed");
308
321
  const level90 = getNewBudgetAlertLevel(lastLevel, 9.0 / ceiling);
309
- assert.deepStrictEqual(level90, 90, "combined: 90% alert fires after all workers complete");
322
+ assertEq(level90, 90, "combined: 90% alert fires after all workers complete");
310
323
 
311
- assert.ok(!hasActiveWorkers(), "combined: no active workers at end");
324
+ assertTrue(!hasActiveWorkers(), "combined: no active workers at end");
312
325
 
313
326
  resetWorkerRegistry();
314
- });
327
+ }
315
328
 
316
329
  // ─── E2E: formatCostProjection with budget ceiling warnings ───────────────────
317
- test('E2E: Cost projection ceiling warnings', () => {
330
+
331
+ console.log("\n=== E2E: Cost projection ceiling warnings ===");
332
+
333
+ {
318
334
  const slices = [
319
335
  { sliceId: "M001/S01", units: 4, tokens: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 }, cost: 3.0, duration: 10000 },
320
336
  { sliceId: "M001/S02", units: 3, tokens: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 }, cost: 4.0, duration: 8000 },
@@ -323,15 +339,16 @@ test('E2E: Cost projection ceiling warnings', () => {
323
339
 
324
340
  // With ceiling NOT yet reached
325
341
  const proj1 = formatCostProjection(slices, 2, 20.0);
326
- assert.ok(proj1.length >= 1, "projection: has projection line");
327
- assert.match(proj1[0], /Projected remaining/, "projection: shows projection");
328
- assert.ok(proj1.length === 1, "projection: no ceiling warning when under budget");
342
+ assertTrue(proj1.length >= 1, "projection: has projection line");
343
+ assertMatch(proj1[0], /Projected remaining/, "projection: shows projection");
344
+ assertTrue(proj1.length === 1, "projection: no ceiling warning when under budget");
329
345
 
330
346
  // With ceiling reached (spent 12.0 >= ceiling 10.0)
331
347
  const proj2 = formatCostProjection(slices, 2, 10.0);
332
- assert.ok(proj2.length >= 2, "projection: has ceiling warning when over budget");
333
- assert.match(proj2[1], /ceiling/, "projection: ceiling warning text");
334
- });
348
+ assertTrue(proj2.length >= 2, "projection: has ceiling warning when over budget");
349
+ assertMatch(proj2[1], /ceiling/, "projection: ceiling warning text");
350
+ }
335
351
 
336
352
  // ─── Summary ──────────────────────────────────────────────────────────────────
337
- });
353
+
354
+ report();
@@ -12,8 +12,6 @@
12
12
  * 8. Discard milestone that has depends_on on others
13
13
  */
14
14
 
15
- import { describe, test } from 'node:test';
16
- import assert from 'node:assert/strict';
17
15
  import { mkdtempSync, mkdirSync, rmSync, writeFileSync, readFileSync } from 'node:fs';
18
16
  import { join } from 'node:path';
19
17
  import { tmpdir } from 'node:os';
@@ -22,6 +20,16 @@ import { deriveState, invalidateStateCache } from '../state.ts';
22
20
  import { clearPathCache } from '../paths.ts';
23
21
  import { parkMilestone, unparkMilestone, discardMilestone } from '../milestone-actions.ts';
24
22
 
23
+ let passed = 0;
24
+ let failed = 0;
25
+
26
+ function assert(condition: boolean, message: string): void {
27
+ if (condition) { passed++; } else { failed++; console.error(` FAIL: ${message}`); }
28
+ }
29
+ function assertEq<T>(actual: T, expected: T, message: string): void {
30
+ if (JSON.stringify(actual) === JSON.stringify(expected)) { passed++; }
31
+ else { failed++; console.error(` FAIL: ${message} — expected ${JSON.stringify(expected)}, got ${JSON.stringify(actual)}`); }
32
+ }
25
33
 
26
34
  function createFixture(): string {
27
35
  const b = mkdtempSync(join(tmpdir(), 'gsd-edge-'));
@@ -53,10 +61,11 @@ function createM(b: string, mid: string, opts?: { roadmap?: boolean; summary?: b
53
61
  function clear(): void { clearPathCache(); invalidateStateCache(); }
54
62
  function cleanup(b: string): void { rmSync(b, { recursive: true, force: true }); }
55
63
 
56
- // ─── EDGE 1: Discard breaks depends_on → downstream is BLOCKED ────────
64
+ async function main(): Promise<void> {
57
65
 
58
- describe('park-edge-cases', () => {
59
- test('EDGE 1: Discard breaks depends_on chain', async () => {
66
+ // ─── EDGE 1: Discard breaks depends_on → downstream is BLOCKED ────────
67
+ console.log('\n=== EDGE 1: Discard breaks depends_on chain ===');
68
+ {
60
69
  const b = createFixture();
61
70
  try {
62
71
  createM(b, 'M001', { roadmap: true, summary: true }); // complete
@@ -69,16 +78,17 @@ test('EDGE 1: Discard breaks depends_on chain', async () => {
69
78
 
70
79
  // M003 depends on M002 which no longer exists.
71
80
  // M002 is not in completeMilestoneIds → dep is unmet → M003 stays pending
72
- assert.deepStrictEqual(s.registry.find(e => e.id === 'M003')?.status, 'pending', 'M003 stays pending after dep discarded');
73
- assert.deepStrictEqual(s.phase, 'blocked', 'system is blocked (unmet dep on deleted milestone)');
74
- assert.ok(s.blockers.length > 0, 'blockers list is not empty');
81
+ assertEq(s.registry.find(e => e.id === 'M003')?.status, 'pending', 'M003 stays pending after dep discarded');
82
+ assertEq(s.phase, 'blocked', 'system is blocked (unmet dep on deleted milestone)');
83
+ assert(s.blockers.length > 0, 'blockers list is not empty');
75
84
  } finally {
76
85
  cleanup(b);
77
86
  }
78
- });
87
+ }
79
88
 
80
89
  // ─── EDGE 2: Park blocks depends_on chain ────────────────────────────
81
- test('EDGE 2: Park blocks depends_on chain', async () => {
90
+ console.log('\n=== EDGE 2: Park blocks depends_on chain ===');
91
+ {
82
92
  const b = createFixture();
83
93
  try {
84
94
  createM(b, 'M001', { roadmap: true, summary: true });
@@ -88,16 +98,17 @@ test('EDGE 2: Park blocks depends_on chain', async () => {
88
98
 
89
99
  parkMilestone(b, 'M002', 'testing');
90
100
  const s = await deriveState(b);
91
- assert.deepStrictEqual(s.registry.find(e => e.id === 'M003')?.status, 'pending', 'M003 pending when M002 parked');
101
+ assertEq(s.registry.find(e => e.id === 'M003')?.status, 'pending', 'M003 pending when M002 parked');
92
102
  // System should be blocked since M003 deps unmet and M002 is parked
93
- assert.ok(s.activeMilestone === null, 'no active milestone (M002 parked, M003 dep-blocked)');
103
+ assert(s.activeMilestone === null, 'no active milestone (M002 parked, M003 dep-blocked)');
94
104
  } finally {
95
105
  cleanup(b);
96
106
  }
97
- });
107
+ }
98
108
 
99
109
  // ─── EDGE 3: Discard active, next (no deps) activates ────────────────
100
- test('EDGE 3: Discard active → next activates', async () => {
110
+ console.log('\n=== EDGE 3: Discard active → next activates ===');
111
+ {
101
112
  const b = createFixture();
102
113
  try {
103
114
  createM(b, 'M001', { roadmap: true });
@@ -106,15 +117,16 @@ test('EDGE 3: Discard active → next activates', async () => {
106
117
 
107
118
  discardMilestone(b, 'M001');
108
119
  const s = await deriveState(b);
109
- assert.deepStrictEqual(s.activeMilestone?.id, 'M002', 'M002 becomes active');
110
- assert.ok(s.phase !== 'blocked', 'not blocked');
120
+ assertEq(s.activeMilestone?.id, 'M002', 'M002 becomes active');
121
+ assert(s.phase !== 'blocked', 'not blocked');
111
122
  } finally {
112
123
  cleanup(b);
113
124
  }
114
- });
125
+ }
115
126
 
116
127
  // ─── EDGE 4: Park all + discard all → clean pre-planning ─────────────
117
- test('EDGE 4: Park all → discard all → clean state', async () => {
128
+ console.log('\n=== EDGE 4: Park all → discard all → clean state ===');
129
+ {
118
130
  const b = createFixture();
119
131
  try {
120
132
  createM(b, 'M001', { roadmap: true });
@@ -126,28 +138,30 @@ test('EDGE 4: Park all → discard all → clean state', async () => {
126
138
  discardMilestone(b, 'M001');
127
139
  discardMilestone(b, 'M002');
128
140
  const s = await deriveState(b);
129
- assert.deepStrictEqual(s.activeMilestone, null, 'no active milestone');
130
- assert.deepStrictEqual(s.phase, 'pre-planning', 'phase is pre-planning');
131
- assert.deepStrictEqual(s.registry.length, 0, 'empty registry');
132
- assert.ok(s.nextAction.includes('No milestones'), 'nextAction mentions no milestones');
141
+ assertEq(s.activeMilestone, null, 'no active milestone');
142
+ assertEq(s.phase, 'pre-planning', 'phase is pre-planning');
143
+ assertEq(s.registry.length, 0, 'empty registry');
144
+ assert(s.nextAction.includes('No milestones'), 'nextAction mentions no milestones');
133
145
  } finally {
134
146
  cleanup(b);
135
147
  }
136
- });
148
+ }
137
149
 
138
150
  // ─── EDGE 5: Discard non-existent → graceful false ───────────────────
139
- test('EDGE 5: Discard non-existent', () => {
151
+ console.log('\n=== EDGE 5: Discard non-existent ===');
152
+ {
140
153
  const b = createFixture();
141
154
  try {
142
155
  const result = discardMilestone(b, 'M999');
143
- assert.ok(!result, 'returns false for non-existent');
156
+ assert(!result, 'returns false for non-existent');
144
157
  } finally {
145
158
  cleanup(b);
146
159
  }
147
- });
160
+ }
148
161
 
149
162
  // ─── EDGE 6: Queue order survives discards ───────────────────────────
150
- test('EDGE 6: Queue order after discard', async () => {
163
+ console.log('\n=== EDGE 6: Queue order after discard ===');
164
+ {
151
165
  const b = createFixture();
152
166
  try {
153
167
  createM(b, 'M001', { roadmap: true });
@@ -162,23 +176,24 @@ test('EDGE 6: Queue order after discard', async () => {
162
176
 
163
177
  // With custom queue order, M003 should be active first
164
178
  let s = await deriveState(b);
165
- assert.deepStrictEqual(s.activeMilestone?.id, 'M003', 'M003 active (custom queue order)');
179
+ assertEq(s.activeMilestone?.id, 'M003', 'M003 active (custom queue order)');
166
180
 
167
181
  // Discard M003 → M001 should be next per queue order
168
182
  discardMilestone(b, 'M003');
169
183
  s = await deriveState(b);
170
- assert.deepStrictEqual(s.activeMilestone?.id, 'M001', 'M001 active after M003 discarded');
184
+ assertEq(s.activeMilestone?.id, 'M001', 'M001 active after M003 discarded');
171
185
 
172
186
  // Verify queue order file was updated
173
187
  const order = JSON.parse(readFileSync(join(b, '.gsd', 'QUEUE-ORDER.json'), 'utf-8'));
174
- assert.ok(!order.order.includes('M003'), 'M003 removed from QUEUE-ORDER.json');
188
+ assert(!order.order.includes('M003'), 'M003 removed from QUEUE-ORDER.json');
175
189
  } finally {
176
190
  cleanup(b);
177
191
  }
178
- });
192
+ }
179
193
 
180
194
  // ─── EDGE 7: Discard milestone that has deps on others ───────────────
181
- test('EDGE 7: Discard a milestone that depends on others', async () => {
195
+ console.log('\n=== EDGE 7: Discard a milestone that depends on others ===');
196
+ {
182
197
  const b = createFixture();
183
198
  try {
184
199
  createM(b, 'M001', { roadmap: true });
@@ -188,22 +203,23 @@ test('EDGE 7: Discard a milestone that depends on others', async () => {
188
203
 
189
204
  // M002 depends on M001, so M001 is active, M002 is pending
190
205
  let s = await deriveState(b);
191
- assert.deepStrictEqual(s.activeMilestone?.id, 'M001', 'M001 is active');
192
- assert.deepStrictEqual(s.registry.find(e => e.id === 'M002')?.status, 'pending', 'M002 pending (dep on M001)');
206
+ assertEq(s.activeMilestone?.id, 'M001', 'M001 is active');
207
+ assertEq(s.registry.find(e => e.id === 'M002')?.status, 'pending', 'M002 pending (dep on M001)');
193
208
 
194
209
  // Discard M002 (the one WITH deps) — should be fine, M003 becomes pending
195
210
  discardMilestone(b, 'M002');
196
211
  s = await deriveState(b);
197
- assert.deepStrictEqual(s.activeMilestone?.id, 'M001', 'M001 still active');
198
- assert.ok(!s.registry.some(e => e.id === 'M002'), 'M002 gone from registry');
199
- assert.deepStrictEqual(s.registry.find(e => e.id === 'M003')?.status, 'pending', 'M003 is pending (after M001)');
212
+ assertEq(s.activeMilestone?.id, 'M001', 'M001 still active');
213
+ assert(!s.registry.some(e => e.id === 'M002'), 'M002 gone from registry');
214
+ assertEq(s.registry.find(e => e.id === 'M003')?.status, 'pending', 'M003 is pending (after M001)');
200
215
  } finally {
201
216
  cleanup(b);
202
217
  }
203
- });
218
+ }
204
219
 
205
220
  // ─── EDGE 8: Park → Discard → state transitions ─────────────────────
206
- test('EDGE 8: Park then discard same milestone', async () => {
221
+ console.log('\n=== EDGE 8: Park then discard same milestone ===');
222
+ {
207
223
  const b = createFixture();
208
224
  try {
209
225
  createM(b, 'M001', { roadmap: true });
@@ -212,21 +228,22 @@ test('EDGE 8: Park then discard same milestone', async () => {
212
228
 
213
229
  parkMilestone(b, 'M001', 'temp');
214
230
  let s = await deriveState(b);
215
- assert.deepStrictEqual(s.activeMilestone?.id, 'M002', 'M002 active while M001 parked');
231
+ assertEq(s.activeMilestone?.id, 'M002', 'M002 active while M001 parked');
216
232
 
217
233
  // Now discard the parked milestone
218
234
  discardMilestone(b, 'M001');
219
235
  s = await deriveState(b);
220
- assert.deepStrictEqual(s.activeMilestone?.id, 'M002', 'M002 still active');
221
- assert.ok(!s.registry.some(e => e.id === 'M001'), 'M001 gone completely');
222
- assert.deepStrictEqual(s.registry.length, 1, 'only M002 in registry');
236
+ assertEq(s.activeMilestone?.id, 'M002', 'M002 still active');
237
+ assert(!s.registry.some(e => e.id === 'M001'), 'M001 gone completely');
238
+ assertEq(s.registry.length, 1, 'only M002 in registry');
223
239
  } finally {
224
240
  cleanup(b);
225
241
  }
226
- });
242
+ }
227
243
 
228
244
  // ─── EDGE 9: Complete + parked + pending coexist ─────────────────────
229
- test('EDGE 9: Mixed states — complete + parked + active', async () => {
245
+ console.log('\n=== EDGE 9: Mixed states — complete + parked + active ===');
246
+ {
230
247
  const b = createFixture();
231
248
  try {
232
249
  createM(b, 'M001', { roadmap: true, summary: true }); // complete
@@ -237,17 +254,23 @@ test('EDGE 9: Mixed states — complete + parked + active', async () => {
237
254
 
238
255
  parkMilestone(b, 'M002', 'parked');
239
256
  const s = await deriveState(b);
240
- assert.deepStrictEqual(s.registry.find(e => e.id === 'M001')?.status, 'complete', 'M001 complete');
241
- assert.deepStrictEqual(s.registry.find(e => e.id === 'M002')?.status, 'parked', 'M002 parked');
242
- assert.deepStrictEqual(s.registry.find(e => e.id === 'M003')?.status, 'active', 'M003 active');
243
- assert.deepStrictEqual(s.registry.find(e => e.id === 'M004')?.status, 'pending', 'M004 pending');
244
- assert.deepStrictEqual(s.activeMilestone?.id, 'M003', 'M003 is the active milestone');
245
- assert.deepStrictEqual(s.progress?.milestones.done, 1, '1 done');
246
- assert.deepStrictEqual(s.progress?.milestones.total, 4, '4 total');
257
+ assertEq(s.registry.find(e => e.id === 'M001')?.status, 'complete', 'M001 complete');
258
+ assertEq(s.registry.find(e => e.id === 'M002')?.status, 'parked', 'M002 parked');
259
+ assertEq(s.registry.find(e => e.id === 'M003')?.status, 'active', 'M003 active');
260
+ assertEq(s.registry.find(e => e.id === 'M004')?.status, 'pending', 'M004 pending');
261
+ assertEq(s.activeMilestone?.id, 'M003', 'M003 is the active milestone');
262
+ assertEq(s.progress?.milestones.done, 1, '1 done');
263
+ assertEq(s.progress?.milestones.total, 4, '4 total');
247
264
  } finally {
248
265
  cleanup(b);
249
266
  }
250
- });
267
+ }
251
268
 
252
- });
269
+ // ═══════════════════════════════════════════════════════════════════════
270
+ console.log(`\n${'='.repeat(50)}`);
271
+ console.log(`Results: ${passed} passed, ${failed} failed`);
272
+ if (failed > 0) process.exit(1);
273
+ else console.log('All edge cases passed!');
274
+ }
253
275
 
276
+ main().catch(e => { console.error(e); process.exit(1); });