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,4 @@
1
- import { describe, test } from "node:test";
2
- import assert from "node:assert/strict";
1
+ import { createTestContext } from './test-helpers.ts';
3
2
  import * as fs from 'node:fs';
4
3
  import * as path from 'node:path';
5
4
  import * as os from 'node:os';
@@ -18,6 +17,8 @@ import {
18
17
  } from '../gsd-db.ts';
19
18
  import { handleCompleteTask } from '../tools/complete-task.ts';
20
19
 
20
+ const { assertEq, assertTrue, assertMatch, report } = createTestContext();
21
+
21
22
  // ═══════════════════════════════════════════════════════════════════════════
22
23
  // Helpers
23
24
  // ═══════════════════════════════════════════════════════════════════════════
@@ -98,290 +99,341 @@ function makeValidParams() {
98
99
  }
99
100
 
100
101
  // ═══════════════════════════════════════════════════════════════════════════
101
- // Tests
102
+ // complete-task: Schema v5 migration
102
103
  // ═══════════════════════════════════════════════════════════════════════════
103
104
 
104
- describe("complete-task: schema v5 migration", () => {
105
- test("schema version and tables exist", () => {
106
- const dbPath = tempDbPath();
107
- openDatabase(dbPath);
105
+ console.log('\n=== complete-task: schema v5 migration ===');
106
+ {
107
+ const dbPath = tempDbPath();
108
+ openDatabase(dbPath);
108
109
 
109
- const adapter = _getAdapter()!;
110
+ const adapter = _getAdapter()!;
110
111
 
111
- // Verify schema version is current (v10 after M001 planning migrations)
112
- const versionRow = adapter.prepare('SELECT MAX(version) as v FROM schema_version').get();
113
- assert.strictEqual(versionRow?.['v'], 10, 'schema version should be 10');
112
+ // Verify schema version is current (v10 after M001 planning migrations)
113
+ const versionRow = adapter.prepare('SELECT MAX(version) as v FROM schema_version').get();
114
+ assertEq(versionRow?.['v'], 10, 'schema version should be 10');
114
115
 
115
- // Verify all 4 new tables exist
116
- const tables = adapter.prepare(
117
- "SELECT name FROM sqlite_master WHERE type='table' ORDER BY name"
118
- ).all();
119
- const tableNames = tables.map(t => t['name'] as string);
120
- assert.ok(tableNames.includes('milestones'), 'milestones table should exist');
121
- assert.ok(tableNames.includes('slices'), 'slices table should exist');
122
- assert.ok(tableNames.includes('tasks'), 'tasks table should exist');
123
- assert.ok(tableNames.includes('verification_evidence'), 'verification_evidence table should exist');
116
+ // Verify all 4 new tables exist
117
+ const tables = adapter.prepare(
118
+ "SELECT name FROM sqlite_master WHERE type='table' ORDER BY name"
119
+ ).all();
120
+ const tableNames = tables.map(t => t['name'] as string);
121
+ assertTrue(tableNames.includes('milestones'), 'milestones table should exist');
122
+ assertTrue(tableNames.includes('slices'), 'slices table should exist');
123
+ assertTrue(tableNames.includes('tasks'), 'tasks table should exist');
124
+ assertTrue(tableNames.includes('verification_evidence'), 'verification_evidence table should exist');
124
125
 
125
- cleanup(dbPath);
126
+ cleanup(dbPath);
127
+ }
128
+
129
+ // ═══════════════════════════════════════════════════════════════════════════
130
+ // complete-task: Accessor CRUD
131
+ // ═══════════════════════════════════════════════════════════════════════════
132
+
133
+ console.log('\n=== complete-task: accessor CRUD ===');
134
+ {
135
+ const dbPath = tempDbPath();
136
+ openDatabase(dbPath);
137
+
138
+ // Insert milestone
139
+ insertMilestone({ id: 'M001', title: 'Test Milestone' });
140
+ const adapter = _getAdapter()!;
141
+ const mRow = adapter.prepare("SELECT * FROM milestones WHERE id = 'M001'").get();
142
+ assertEq(mRow?.['id'], 'M001', 'milestone id should be M001');
143
+ assertEq(mRow?.['title'], 'Test Milestone', 'milestone title should match');
144
+
145
+ // Insert slice
146
+ insertSlice({ id: 'S01', milestoneId: 'M001', title: 'Test Slice', risk: 'high' });
147
+ const sRow = adapter.prepare("SELECT * FROM slices WHERE id = 'S01' AND milestone_id = 'M001'").get();
148
+ assertEq(sRow?.['id'], 'S01', 'slice id should be S01');
149
+ assertEq(sRow?.['risk'], 'high', 'slice risk should be high');
150
+
151
+ // Insert task with all fields
152
+ insertTask({
153
+ id: 'T01',
154
+ sliceId: 'S01',
155
+ milestoneId: 'M001',
156
+ title: 'Test Task',
157
+ status: 'complete',
158
+ oneLiner: 'Did the thing',
159
+ narrative: 'Full story here.',
160
+ verificationResult: 'passed',
161
+ duration: '30m',
162
+ blockerDiscovered: false,
163
+ deviations: 'None',
164
+ knownIssues: 'None',
165
+ keyFiles: ['file1.ts', 'file2.ts'],
166
+ keyDecisions: ['D001'],
167
+ fullSummaryMd: '# Summary',
126
168
  });
127
- });
128
169
 
129
- describe("complete-task: accessor CRUD", () => {
130
- test("insert and query milestones, slices, tasks, evidence", () => {
131
- const dbPath = tempDbPath();
132
- openDatabase(dbPath);
170
+ // getTask verifies all fields
171
+ const task = getTask('M001', 'S01', 'T01');
172
+ assertTrue(task !== null, 'task should not be null');
173
+ assertEq(task!.id, 'T01', 'task id');
174
+ assertEq(task!.slice_id, 'S01', 'task slice_id');
175
+ assertEq(task!.milestone_id, 'M001', 'task milestone_id');
176
+ assertEq(task!.title, 'Test Task', 'task title');
177
+ assertEq(task!.status, 'complete', 'task status');
178
+ assertEq(task!.one_liner, 'Did the thing', 'task one_liner');
179
+ assertEq(task!.narrative, 'Full story here.', 'task narrative');
180
+ assertEq(task!.verification_result, 'passed', 'task verification_result');
181
+ assertEq(task!.blocker_discovered, false, 'task blocker_discovered');
182
+ assertEq(task!.key_files, ['file1.ts', 'file2.ts'], 'task key_files JSON round-trip');
183
+ assertEq(task!.key_decisions, ['D001'], 'task key_decisions JSON round-trip');
184
+ assertEq(task!.full_summary_md, '# Summary', 'task full_summary_md');
185
+
186
+ // getTask returns null for non-existent
187
+ const noTask = getTask('M001', 'S01', 'T99');
188
+ assertEq(noTask, null, 'non-existent task should return null');
189
+
190
+ // Insert verification evidence
191
+ insertVerificationEvidence({
192
+ taskId: 'T01',
193
+ sliceId: 'S01',
194
+ milestoneId: 'M001',
195
+ command: 'npm test',
196
+ exitCode: 0,
197
+ verdict: '✅ pass',
198
+ durationMs: 3000,
199
+ });
200
+ const evRows = adapter.prepare(
201
+ "SELECT * FROM verification_evidence WHERE task_id = 'T01' AND slice_id = 'S01' AND milestone_id = 'M001'"
202
+ ).all();
203
+ assertEq(evRows.length, 1, 'should have 1 verification evidence row');
204
+ assertEq(evRows[0]['command'], 'npm test', 'evidence command');
205
+ assertEq(evRows[0]['exit_code'], 0, 'evidence exit_code');
206
+ assertEq(evRows[0]['verdict'], '✅ pass', 'evidence verdict');
207
+ assertEq(evRows[0]['duration_ms'], 3000, 'evidence duration_ms');
208
+
209
+ // getSliceTasks returns array
210
+ const sliceTasks = getSliceTasks('M001', 'S01');
211
+ assertEq(sliceTasks.length, 1, 'getSliceTasks should return 1 task');
212
+ assertEq(sliceTasks[0].id, 'T01', 'getSliceTasks first task id');
213
+
214
+ // updateTaskStatus changes status
215
+ updateTaskStatus('M001', 'S01', 'T01', 'failed', new Date().toISOString());
216
+ const updatedTask = getTask('M001', 'S01', 'T01');
217
+ assertEq(updatedTask!.status, 'failed', 'task status should be updated to failed');
218
+ assertTrue(updatedTask!.completed_at !== null, 'completed_at should be set after status update');
219
+
220
+ cleanup(dbPath);
221
+ }
133
222
 
134
- // Insert milestone
135
- insertMilestone({ id: 'M001', title: 'Test Milestone' });
136
- const adapter = _getAdapter()!;
137
- const mRow = adapter.prepare("SELECT * FROM milestones WHERE id = 'M001'").get();
138
- assert.strictEqual(mRow?.['id'], 'M001', 'milestone id should be M001');
139
- assert.strictEqual(mRow?.['title'], 'Test Milestone', 'milestone title should match');
140
-
141
- // Insert slice
142
- insertSlice({ id: 'S01', milestoneId: 'M001', title: 'Test Slice', risk: 'high' });
143
- const sRow = adapter.prepare("SELECT * FROM slices WHERE id = 'S01' AND milestone_id = 'M001'").get();
144
- assert.strictEqual(sRow?.['id'], 'S01', 'slice id should be S01');
145
- assert.strictEqual(sRow?.['risk'], 'high', 'slice risk should be high');
146
-
147
- // Insert task with all fields
148
- insertTask({
149
- id: 'T01',
150
- sliceId: 'S01',
151
- milestoneId: 'M001',
152
- title: 'Test Task',
153
- status: 'complete',
154
- oneLiner: 'Did the thing',
155
- narrative: 'Full story here.',
156
- verificationResult: 'passed',
157
- duration: '30m',
158
- blockerDiscovered: false,
159
- deviations: 'None',
160
- knownIssues: 'None',
161
- keyFiles: ['file1.ts', 'file2.ts'],
162
- keyDecisions: ['D001'],
163
- fullSummaryMd: '# Summary',
164
- });
223
+ // ═══════════════════════════════════════════════════════════════════════════
224
+ // complete-task: Accessor stale-state error
225
+ // ═══════════════════════════════════════════════════════════════════════════
165
226
 
166
- // getTask verifies all fields
167
- const task = getTask('M001', 'S01', 'T01');
168
- assert.ok(task !== null, 'task should not be null');
169
- assert.strictEqual(task!.id, 'T01', 'task id');
170
- assert.strictEqual(task!.slice_id, 'S01', 'task slice_id');
171
- assert.strictEqual(task!.milestone_id, 'M001', 'task milestone_id');
172
- assert.strictEqual(task!.title, 'Test Task', 'task title');
173
- assert.strictEqual(task!.status, 'complete', 'task status');
174
- assert.strictEqual(task!.one_liner, 'Did the thing', 'task one_liner');
175
- assert.strictEqual(task!.narrative, 'Full story here.', 'task narrative');
176
- assert.strictEqual(task!.verification_result, 'passed', 'task verification_result');
177
- assert.strictEqual(task!.blocker_discovered, false, 'task blocker_discovered');
178
- assert.deepStrictEqual(task!.key_files, ['file1.ts', 'file2.ts'], 'task key_files JSON round-trip');
179
- assert.deepStrictEqual(task!.key_decisions, ['D001'], 'task key_decisions JSON round-trip');
180
- assert.strictEqual(task!.full_summary_md, '# Summary', 'task full_summary_md');
181
-
182
- // getTask returns null for non-existent
183
- const noTask = getTask('M001', 'S01', 'T99');
184
- assert.strictEqual(noTask, null, 'non-existent task should return null');
185
-
186
- // Insert verification evidence
227
+ console.log('\n=== complete-task: accessor stale-state error ===');
228
+ {
229
+ // No DB open — accessors should throw GSD_STALE_STATE
230
+ closeDatabase();
231
+ let threw = false;
232
+ try {
233
+ insertMilestone({ id: 'M001' });
234
+ } catch (err: any) {
235
+ threw = true;
236
+ assertTrue(err.code === 'GSD_STALE_STATE' || err.message.includes('No database open'),
237
+ 'should throw GSD_STALE_STATE when no DB open');
238
+ }
239
+ assertTrue(threw, 'insertMilestone should throw when no DB open');
240
+
241
+ threw = false;
242
+ try {
243
+ insertSlice({ id: 'S01', milestoneId: 'M001' });
244
+ } catch (err: any) {
245
+ threw = true;
246
+ assertTrue(err.code === 'GSD_STALE_STATE' || err.message.includes('No database open'),
247
+ 'insertSlice should throw GSD_STALE_STATE');
248
+ }
249
+ assertTrue(threw, 'insertSlice should throw when no DB open');
250
+
251
+ threw = false;
252
+ try {
253
+ insertTask({ id: 'T01', sliceId: 'S01', milestoneId: 'M001' });
254
+ } catch (err: any) {
255
+ threw = true;
256
+ assertTrue(err.code === 'GSD_STALE_STATE' || err.message.includes('No database open'),
257
+ 'insertTask should throw GSD_STALE_STATE');
258
+ }
259
+ assertTrue(threw, 'insertTask should throw when no DB open');
260
+
261
+ threw = false;
262
+ try {
187
263
  insertVerificationEvidence({
188
- taskId: 'T01',
189
- sliceId: 'S01',
190
- milestoneId: 'M001',
191
- command: 'npm test',
192
- exitCode: 0,
193
- verdict: '✅ pass',
194
- durationMs: 3000,
264
+ taskId: 'T01', sliceId: 'S01', milestoneId: 'M001',
265
+ command: 'test', exitCode: 0, verdict: 'pass', durationMs: 0,
195
266
  });
267
+ } catch (err: any) {
268
+ threw = true;
269
+ assertTrue(err.code === 'GSD_STALE_STATE' || err.message.includes('No database open'),
270
+ 'insertVerificationEvidence should throw GSD_STALE_STATE');
271
+ }
272
+ assertTrue(threw, 'insertVerificationEvidence should throw when no DB open');
273
+ }
274
+
275
+ // ═══════════════════════════════════════════════════════════════════════════
276
+ // complete-task: Handler happy path
277
+ // ═══════════════════════════════════════════════════════════════════════════
278
+
279
+ console.log('\n=== complete-task: handler happy path ===');
280
+ {
281
+ const dbPath = tempDbPath();
282
+ openDatabase(dbPath);
283
+
284
+ const { basePath, planPath } = createTempProject();
285
+
286
+ const params = makeValidParams();
287
+ const result = await handleCompleteTask(params, basePath);
288
+
289
+ assertTrue(!('error' in result), 'handler should succeed without error');
290
+ if (!('error' in result)) {
291
+ assertEq(result.taskId, 'T01', 'result taskId');
292
+ assertEq(result.sliceId, 'S01', 'result sliceId');
293
+ assertEq(result.milestoneId, 'M001', 'result milestoneId');
294
+ assertTrue(result.summaryPath.endsWith('T01-SUMMARY.md'), 'summaryPath should end with T01-SUMMARY.md');
295
+
296
+ // (a) Verify task row in DB with status 'complete'
297
+ const task = getTask('M001', 'S01', 'T01');
298
+ assertTrue(task !== null, 'task should exist in DB after handler');
299
+ assertEq(task!.status, 'complete', 'task status should be complete');
300
+ assertEq(task!.one_liner, 'Added test functionality', 'task one_liner in DB');
301
+ assertEq(task!.key_files, ['src/test.ts', 'src/test.test.ts'], 'task key_files in DB');
302
+
303
+ // (b) Verify verification_evidence rows in DB
304
+ const adapter = _getAdapter()!;
196
305
  const evRows = adapter.prepare(
197
- "SELECT * FROM verification_evidence WHERE task_id = 'T01' AND slice_id = 'S01' AND milestone_id = 'M001'"
306
+ "SELECT * FROM verification_evidence WHERE task_id = 'T01' AND milestone_id = 'M001'"
198
307
  ).all();
199
- assert.strictEqual(evRows.length, 1, 'should have 1 verification evidence row');
200
- assert.strictEqual(evRows[0]['command'], 'npm test', 'evidence command');
201
- assert.strictEqual(evRows[0]['exit_code'], 0, 'evidence exit_code');
202
- assert.strictEqual(evRows[0]['verdict'], '✅ pass', 'evidence verdict');
203
- assert.strictEqual(evRows[0]['duration_ms'], 3000, 'evidence duration_ms');
204
-
205
- // getSliceTasks returns array
206
- const sliceTasks = getSliceTasks('M001', 'S01');
207
- assert.strictEqual(sliceTasks.length, 1, 'getSliceTasks should return 1 task');
208
- assert.strictEqual(sliceTasks[0].id, 'T01', 'getSliceTasks first task id');
209
-
210
- // updateTaskStatus changes status
211
- updateTaskStatus('M001', 'S01', 'T01', 'failed', new Date().toISOString());
212
- const updatedTask = getTask('M001', 'S01', 'T01');
213
- assert.strictEqual(updatedTask!.status, 'failed', 'task status should be updated to failed');
214
- assert.ok(updatedTask!.completed_at !== null, 'completed_at should be set after status update');
215
-
216
- cleanup(dbPath);
217
- });
218
- });
308
+ assertEq(evRows.length, 1, 'should have 1 verification evidence row after handler');
309
+ assertEq(evRows[0]['command'], 'npm run test:unit', 'evidence command from handler');
310
+
311
+ // (c) Verify T01-SUMMARY.md file on disk with correct YAML frontmatter
312
+ assertTrue(fs.existsSync(result.summaryPath), 'summary file should exist on disk');
313
+ const summaryContent = fs.readFileSync(result.summaryPath, 'utf-8');
314
+ assertMatch(summaryContent, /^---\n/, 'summary should start with YAML frontmatter');
315
+ assertMatch(summaryContent, /id: T01/, 'summary should contain id: T01');
316
+ assertMatch(summaryContent, /parent: S01/, 'summary should contain parent: S01');
317
+ assertMatch(summaryContent, /milestone: M001/, 'summary should contain milestone: M001');
318
+ assertMatch(summaryContent, /blocker_discovered: false/, 'summary should contain blocker_discovered');
319
+ assertMatch(summaryContent, /# T01:/, 'summary should have H1 with task ID');
320
+ assertMatch(summaryContent, /\*\*Added test functionality\*\*/, 'summary should have one-liner in bold');
321
+ assertMatch(summaryContent, /## What Happened/, 'summary should have What Happened section');
322
+ assertMatch(summaryContent, /## Verification Evidence/, 'summary should have Verification Evidence section');
323
+ assertMatch(summaryContent, /npm run test:unit/, 'summary evidence should contain command');
324
+
325
+ // (d) Verify plan checkbox changed to [x]
326
+ const planContent = fs.readFileSync(planPath, 'utf-8');
327
+ assertMatch(planContent, /\[x\]\s+\*\*T01:/, 'T01 should be checked in plan');
328
+ // T02 should still be unchecked
329
+ assertMatch(planContent, /\[ \]\s+\*\*T02:/, 'T02 should still be unchecked in plan');
330
+
331
+ // (e) Verify full_summary_md stored in DB for D004 recovery
332
+ const taskAfter = getTask('M001', 'S01', 'T01');
333
+ assertTrue(taskAfter!.full_summary_md.length > 0, 'full_summary_md should be non-empty in DB');
334
+ assertMatch(taskAfter!.full_summary_md, /id: T01/, 'full_summary_md should contain frontmatter');
335
+ }
219
336
 
220
- describe("complete-task: accessor stale-state error", () => {
221
- test("accessors throw when no DB open", () => {
222
- closeDatabase();
337
+ cleanupDir(basePath);
338
+ cleanup(dbPath);
339
+ }
223
340
 
224
- assert.throws(() => insertMilestone({ id: 'M001' }),
225
- (err: any) => err.code === 'GSD_STALE_STATE' || err.message.includes('No database open'),
226
- 'insertMilestone should throw when no DB open');
341
+ // ═══════════════════════════════════════════════════════════════════════════
342
+ // complete-task: Handler validation errors
343
+ // ═══════════════════════════════════════════════════════════════════════════
227
344
 
228
- assert.throws(() => insertSlice({ id: 'S01', milestoneId: 'M001' }),
229
- (err: any) => err.code === 'GSD_STALE_STATE' || err.message.includes('No database open'),
230
- 'insertSlice should throw when no DB open');
345
+ console.log('\n=== complete-task: handler validation errors ===');
346
+ {
347
+ const dbPath = tempDbPath();
348
+ openDatabase(dbPath);
231
349
 
232
- assert.throws(() => insertTask({ id: 'T01', sliceId: 'S01', milestoneId: 'M001' }),
233
- (err: any) => err.code === 'GSD_STALE_STATE' || err.message.includes('No database open'),
234
- 'insertTask should throw when no DB open');
350
+ const params = makeValidParams();
235
351
 
236
- assert.throws(() => insertVerificationEvidence({
237
- taskId: 'T01', sliceId: 'S01', milestoneId: 'M001',
238
- command: 'test', exitCode: 0, verdict: 'pass', durationMs: 0,
239
- }),
240
- (err: any) => err.code === 'GSD_STALE_STATE' || err.message.includes('No database open'),
241
- 'insertVerificationEvidence should throw when no DB open');
242
- });
243
- });
244
-
245
- describe("complete-task: handler", () => {
246
- test("happy path", async () => {
247
- const dbPath = tempDbPath();
248
- openDatabase(dbPath);
249
-
250
- const { basePath, planPath } = createTempProject();
251
-
252
- const params = makeValidParams();
253
- const result = await handleCompleteTask(params, basePath);
254
-
255
- assert.ok(!('error' in result), 'handler should succeed without error');
256
- if (!('error' in result)) {
257
- assert.strictEqual(result.taskId, 'T01', 'result taskId');
258
- assert.strictEqual(result.sliceId, 'S01', 'result sliceId');
259
- assert.strictEqual(result.milestoneId, 'M001', 'result milestoneId');
260
- assert.ok(result.summaryPath.endsWith('T01-SUMMARY.md'), 'summaryPath should end with T01-SUMMARY.md');
261
-
262
- // (a) Verify task row in DB with status 'complete'
263
- const task = getTask('M001', 'S01', 'T01');
264
- assert.ok(task !== null, 'task should exist in DB after handler');
265
- assert.strictEqual(task!.status, 'complete', 'task status should be complete');
266
- assert.strictEqual(task!.one_liner, 'Added test functionality', 'task one_liner in DB');
267
- assert.deepStrictEqual(task!.key_files, ['src/test.ts', 'src/test.test.ts'], 'task key_files in DB');
268
-
269
- // (b) Verify verification_evidence rows in DB
270
- const adapter = _getAdapter()!;
271
- const evRows = adapter.prepare(
272
- "SELECT * FROM verification_evidence WHERE task_id = 'T01' AND milestone_id = 'M001'"
273
- ).all();
274
- assert.strictEqual(evRows.length, 1, 'should have 1 verification evidence row after handler');
275
- assert.strictEqual(evRows[0]['command'], 'npm run test:unit', 'evidence command from handler');
276
-
277
- // (c) Verify T01-SUMMARY.md file on disk with correct YAML frontmatter
278
- assert.ok(fs.existsSync(result.summaryPath), 'summary file should exist on disk');
279
- const summaryContent = fs.readFileSync(result.summaryPath, 'utf-8');
280
- assert.match(summaryContent, /^---\n/, 'summary should start with YAML frontmatter');
281
- assert.match(summaryContent, /id: T01/, 'summary should contain id: T01');
282
- assert.match(summaryContent, /parent: S01/, 'summary should contain parent: S01');
283
- assert.match(summaryContent, /milestone: M001/, 'summary should contain milestone: M001');
284
- assert.match(summaryContent, /blocker_discovered: false/, 'summary should contain blocker_discovered');
285
- assert.match(summaryContent, /# T01:/, 'summary should have H1 with task ID');
286
- assert.match(summaryContent, /\*\*Added test functionality\*\*/, 'summary should have one-liner in bold');
287
- assert.match(summaryContent, /## What Happened/, 'summary should have What Happened section');
288
- assert.match(summaryContent, /## Verification Evidence/, 'summary should have Verification Evidence section');
289
- assert.match(summaryContent, /npm run test:unit/, 'summary evidence should contain command');
290
-
291
- // (d) Verify plan checkbox changed to [x]
292
- const planContent = fs.readFileSync(planPath, 'utf-8');
293
- assert.match(planContent, /\[x\]\s+\*\*T01:/, 'T01 should be checked in plan');
294
- // T02 should still be unchecked
295
- assert.match(planContent, /\[ \]\s+\*\*T02:/, 'T02 should still be unchecked in plan');
296
-
297
- // (e) Verify full_summary_md stored in DB for D004 recovery
298
- const taskAfter = getTask('M001', 'S01', 'T01');
299
- assert.ok(taskAfter!.full_summary_md.length > 0, 'full_summary_md should be non-empty in DB');
300
- assert.match(taskAfter!.full_summary_md, /id: T01/, 'full_summary_md should contain frontmatter');
301
- }
352
+ // Empty taskId
353
+ const r1 = await handleCompleteTask({ ...params, taskId: '' }, '/tmp/fake');
354
+ assertTrue('error' in r1, 'should return error for empty taskId');
355
+ if ('error' in r1) {
356
+ assertMatch(r1.error, /taskId/, 'error should mention taskId');
357
+ }
302
358
 
303
- cleanupDir(basePath);
304
- cleanup(dbPath);
305
- });
359
+ // Empty milestoneId
360
+ const r2 = await handleCompleteTask({ ...params, milestoneId: '' }, '/tmp/fake');
361
+ assertTrue('error' in r2, 'should return error for empty milestoneId');
362
+ if ('error' in r2) {
363
+ assertMatch(r2.error, /milestoneId/, 'error should mention milestoneId');
364
+ }
306
365
 
307
- test("validation errors", async () => {
308
- const dbPath = tempDbPath();
309
- openDatabase(dbPath);
366
+ // Empty sliceId
367
+ const r3 = await handleCompleteTask({ ...params, sliceId: '' }, '/tmp/fake');
368
+ assertTrue('error' in r3, 'should return error for empty sliceId');
369
+ if ('error' in r3) {
370
+ assertMatch(r3.error, /sliceId/, 'error should mention sliceId');
371
+ }
310
372
 
311
- const params = makeValidParams();
373
+ cleanup(dbPath);
374
+ }
312
375
 
313
- // Empty taskId
314
- const r1 = await handleCompleteTask({ ...params, taskId: '' }, '/tmp/fake');
315
- assert.ok('error' in r1, 'should return error for empty taskId');
316
- if ('error' in r1) {
317
- assert.match(r1.error, /taskId/, 'error should mention taskId');
318
- }
376
+ // ═══════════════════════════════════════════════════════════════════════════
377
+ // complete-task: Handler idempotency
378
+ // ═══════════════════════════════════════════════════════════════════════════
319
379
 
320
- // Empty milestoneId
321
- const r2 = await handleCompleteTask({ ...params, milestoneId: '' }, '/tmp/fake');
322
- assert.ok('error' in r2, 'should return error for empty milestoneId');
323
- if ('error' in r2) {
324
- assert.match(r2.error, /milestoneId/, 'error should mention milestoneId');
325
- }
380
+ console.log('\n=== complete-task: handler idempotency ===');
381
+ {
382
+ const dbPath = tempDbPath();
383
+ openDatabase(dbPath);
326
384
 
327
- // Empty sliceId
328
- const r3 = await handleCompleteTask({ ...params, sliceId: '' }, '/tmp/fake');
329
- assert.ok('error' in r3, 'should return error for empty sliceId');
330
- if ('error' in r3) {
331
- assert.match(r3.error, /sliceId/, 'error should mention sliceId');
332
- }
385
+ const { basePath, planPath } = createTempProject();
333
386
 
334
- cleanup(dbPath);
335
- });
387
+ const params = makeValidParams();
336
388
 
337
- test("idempotency", async () => {
338
- const dbPath = tempDbPath();
339
- openDatabase(dbPath);
389
+ // First call
390
+ const r1 = await handleCompleteTask(params, basePath);
391
+ assertTrue(!('error' in r1), 'first call should succeed');
340
392
 
341
- const { basePath, planPath } = createTempProject();
393
+ // Second call with same params — should not crash (INSERT OR REPLACE)
394
+ const r2 = await handleCompleteTask(params, basePath);
395
+ assertTrue(!('error' in r2), 'second call should succeed (idempotent)');
342
396
 
343
- const params = makeValidParams();
397
+ // Verify only 1 task row (upserted, not duplicated)
398
+ const tasks = getSliceTasks('M001', 'S01');
399
+ assertEq(tasks.length, 1, 'should have exactly 1 task row after 2 calls (upsert)');
344
400
 
345
- // First call
346
- const r1 = await handleCompleteTask(params, basePath);
347
- assert.ok(!('error' in r1), 'first call should succeed');
401
+ // File should still exist
402
+ if (!('error' in r2)) {
403
+ assertTrue(fs.existsSync(r2.summaryPath), 'summary should still exist after second call');
404
+ }
348
405
 
349
- // Second call with same params — should not crash (INSERT OR REPLACE)
350
- const r2 = await handleCompleteTask(params, basePath);
351
- assert.ok(!('error' in r2), 'second call should succeed (idempotent)');
406
+ cleanupDir(basePath);
407
+ cleanup(dbPath);
408
+ }
352
409
 
353
- // Verify only 1 task row (upserted, not duplicated)
354
- const tasks = getSliceTasks('M001', 'S01');
355
- assert.strictEqual(tasks.length, 1, 'should have exactly 1 task row after 2 calls (upsert)');
410
+ // ═══════════════════════════════════════════════════════════════════════════
411
+ // complete-task: Handler with missing plan file (graceful)
412
+ // ═══════════════════════════════════════════════════════════════════════════
356
413
 
357
- // File should still exist
358
- if (!('error' in r2)) {
359
- assert.ok(fs.existsSync(r2.summaryPath), 'summary should still exist after second call');
360
- }
414
+ console.log('\n=== complete-task: handler with missing plan file ===');
415
+ {
416
+ const dbPath = tempDbPath();
417
+ openDatabase(dbPath);
361
418
 
362
- cleanupDir(basePath);
363
- cleanup(dbPath);
364
- });
419
+ // Create a temp dir WITHOUT a plan file
420
+ const basePath = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-no-plan-'));
421
+ const tasksDir = path.join(basePath, '.gsd', 'milestones', 'M001', 'slices', 'S01', 'tasks');
422
+ fs.mkdirSync(tasksDir, { recursive: true });
365
423
 
366
- test("missing plan file (graceful)", async () => {
367
- const dbPath = tempDbPath();
368
- openDatabase(dbPath);
424
+ const params = makeValidParams();
425
+ const result = await handleCompleteTask(params, basePath);
369
426
 
370
- // Create a temp dir WITHOUT a plan file
371
- const basePath = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-no-plan-'));
372
- const tasksDir = path.join(basePath, '.gsd', 'milestones', 'M001', 'slices', 'S01', 'tasks');
373
- fs.mkdirSync(tasksDir, { recursive: true });
427
+ // Should succeed even without plan file just skip checkbox toggle
428
+ assertTrue(!('error' in result), 'handler should succeed without plan file');
429
+ if (!('error' in result)) {
430
+ assertTrue(fs.existsSync(result.summaryPath), 'summary should be written even without plan file');
431
+ }
374
432
 
375
- const params = makeValidParams();
376
- const result = await handleCompleteTask(params, basePath);
433
+ cleanupDir(basePath);
434
+ cleanup(dbPath);
435
+ }
377
436
 
378
- // Should succeed even without plan file — just skip checkbox toggle
379
- assert.ok(!('error' in result), 'handler should succeed without plan file');
380
- if (!('error' in result)) {
381
- assert.ok(fs.existsSync(result.summaryPath), 'summary should be written even without plan file');
382
- }
437
+ // ═══════════════════════════════════════════════════════════════════════════
383
438
 
384
- cleanupDir(basePath);
385
- cleanup(dbPath);
386
- });
387
- });
439
+ report();