gsd-pi 2.45.0 → 2.46.0-dev.cc9d310

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 (347) hide show
  1. package/dist/help-text.js +1 -1
  2. package/dist/loader.js +34 -0
  3. package/dist/resources/extensions/gsd/auto/phases.js +27 -42
  4. package/dist/resources/extensions/gsd/auto/run-unit.js +6 -3
  5. package/dist/resources/extensions/gsd/auto/session.js +0 -11
  6. package/dist/resources/extensions/gsd/auto-artifact-paths.js +112 -0
  7. package/dist/resources/extensions/gsd/auto-post-unit.js +25 -96
  8. package/dist/resources/extensions/gsd/auto-start.js +2 -3
  9. package/dist/resources/extensions/gsd/auto-worktree.js +5 -4
  10. package/dist/resources/extensions/gsd/auto.js +12 -57
  11. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +15 -12
  12. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +18 -0
  13. package/dist/resources/extensions/gsd/commands/context.js +0 -4
  14. package/dist/resources/extensions/gsd/commands/handlers/parallel.js +1 -1
  15. package/dist/resources/extensions/gsd/crash-recovery.js +2 -4
  16. package/dist/resources/extensions/gsd/dashboard-overlay.js +0 -44
  17. package/dist/resources/extensions/gsd/db-writer.js +9 -9
  18. package/dist/resources/extensions/gsd/doctor-checks.js +167 -2
  19. package/dist/resources/extensions/gsd/doctor.js +5 -3
  20. package/dist/resources/extensions/gsd/gsd-db.js +16 -3
  21. package/dist/resources/extensions/gsd/guided-flow.js +1 -2
  22. package/dist/resources/extensions/gsd/parallel-merge.js +1 -1
  23. package/dist/resources/extensions/gsd/parallel-orchestrator.js +5 -18
  24. package/dist/resources/extensions/gsd/preferences-types.js +2 -2
  25. package/dist/resources/extensions/gsd/preferences.js +8 -4
  26. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +21 -8
  27. package/dist/resources/extensions/gsd/prompts/complete-slice.md +10 -23
  28. package/dist/resources/extensions/gsd/prompts/discuss.md +2 -2
  29. package/dist/resources/extensions/gsd/prompts/execute-task.md +5 -15
  30. package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +1 -1
  31. package/dist/resources/extensions/gsd/prompts/guided-discuss-slice.md +1 -1
  32. package/dist/resources/extensions/gsd/prompts/guided-plan-slice.md +1 -1
  33. package/dist/resources/extensions/gsd/prompts/guided-research-slice.md +1 -1
  34. package/dist/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
  35. package/dist/resources/extensions/gsd/prompts/plan-slice.md +4 -2
  36. package/dist/resources/extensions/gsd/prompts/queue.md +2 -2
  37. package/dist/resources/extensions/gsd/prompts/quick-task.md +2 -0
  38. package/dist/resources/extensions/gsd/prompts/reactive-execute.md +1 -1
  39. package/dist/resources/extensions/gsd/prompts/research-slice.md +3 -3
  40. package/dist/resources/extensions/gsd/prompts/rethink.md +7 -2
  41. package/dist/resources/extensions/gsd/prompts/system.md +1 -1
  42. package/dist/resources/extensions/gsd/session-lock.js +1 -3
  43. package/dist/resources/extensions/gsd/state.js +7 -0
  44. package/dist/resources/extensions/gsd/sync-lock.js +89 -0
  45. package/dist/resources/extensions/gsd/tools/complete-milestone.js +61 -11
  46. package/dist/resources/extensions/gsd/tools/complete-slice.js +56 -11
  47. package/dist/resources/extensions/gsd/tools/complete-task.js +50 -2
  48. package/dist/resources/extensions/gsd/tools/plan-milestone.js +37 -1
  49. package/dist/resources/extensions/gsd/tools/plan-slice.js +30 -1
  50. package/dist/resources/extensions/gsd/tools/plan-task.js +27 -1
  51. package/dist/resources/extensions/gsd/tools/reassess-roadmap.js +32 -2
  52. package/dist/resources/extensions/gsd/tools/reopen-slice.js +86 -0
  53. package/dist/resources/extensions/gsd/tools/reopen-task.js +90 -0
  54. package/dist/resources/extensions/gsd/tools/replan-slice.js +32 -2
  55. package/dist/resources/extensions/gsd/unit-ownership.js +85 -0
  56. package/dist/resources/extensions/gsd/workflow-events.js +102 -0
  57. package/dist/resources/extensions/gsd/workflow-logger.js +193 -0
  58. package/dist/resources/extensions/gsd/workflow-manifest.js +244 -0
  59. package/dist/resources/extensions/gsd/workflow-migration.js +280 -0
  60. package/dist/resources/extensions/gsd/workflow-projections.js +373 -0
  61. package/dist/resources/extensions/gsd/workflow-reconcile.js +411 -0
  62. package/dist/resources/extensions/gsd/worktree-manager.js +4 -3
  63. package/dist/resources/extensions/gsd/worktree-resolver.js +37 -0
  64. package/dist/resources/extensions/gsd/write-intercept.js +84 -0
  65. package/dist/resources/extensions/voice/index.js +11 -16
  66. package/dist/resources/extensions/voice/linux-ready.js +67 -0
  67. package/dist/web/standalone/.next/BUILD_ID +1 -1
  68. package/dist/web/standalone/.next/app-path-routes-manifest.json +17 -17
  69. package/dist/web/standalone/.next/build-manifest.json +3 -3
  70. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  71. package/dist/web/standalone/.next/required-server-files.json +3 -3
  72. package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
  73. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  74. package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
  75. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  76. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  77. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  78. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  79. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  80. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  81. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  82. package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
  83. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  84. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  85. package/dist/web/standalone/.next/server/app/_not-found.rsc +3 -3
  86. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
  87. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  88. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +3 -3
  89. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  90. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  91. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  92. package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
  93. package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
  94. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
  95. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
  96. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
  97. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
  98. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
  99. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
  100. package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
  101. package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
  102. package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
  103. package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
  104. package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
  105. package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
  106. package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
  107. package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
  108. package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
  109. package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
  110. package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
  111. package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
  112. package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
  113. package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
  114. package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
  115. package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
  116. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  117. package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
  118. package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
  119. package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
  120. package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
  121. package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
  122. package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
  123. package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
  124. package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
  125. package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
  126. package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
  127. package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
  128. package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
  129. package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
  130. package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
  131. package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
  132. package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
  133. package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
  134. package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
  135. package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
  136. package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +5 -5
  137. package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
  138. package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
  139. package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
  140. package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
  141. package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
  142. package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
  143. package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
  144. package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
  145. package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
  146. package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
  147. package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
  148. package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
  149. package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
  150. package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
  151. package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
  152. package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
  153. package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
  154. package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
  155. package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
  156. package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +2 -2
  157. package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
  158. package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
  159. package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
  160. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +2 -2
  161. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
  162. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +4 -4
  163. package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
  164. package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
  165. package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
  166. package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
  167. package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
  168. package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
  169. package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
  170. package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
  171. package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
  172. package/dist/web/standalone/.next/server/app/index.html +1 -1
  173. package/dist/web/standalone/.next/server/app/index.rsc +4 -4
  174. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  175. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +4 -4
  176. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  177. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +3 -3
  178. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  179. package/dist/web/standalone/.next/server/app/page.js +2 -2
  180. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  181. package/dist/web/standalone/.next/server/app-paths-manifest.json +17 -17
  182. package/dist/web/standalone/.next/server/chunks/229.js +1 -1
  183. package/dist/web/standalone/.next/server/chunks/471.js +3 -3
  184. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  185. package/dist/web/standalone/.next/server/middleware.js +2 -2
  186. package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
  187. package/dist/web/standalone/.next/server/next-font-manifest.json +1 -1
  188. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  189. package/dist/web/standalone/.next/server/pages/500.html +2 -2
  190. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  191. package/dist/web/standalone/.next/static/chunks/app/_not-found/{page-2f24283c162b6ab3.js → page-f2a7482d42a5614b.js} +1 -1
  192. package/dist/web/standalone/.next/static/chunks/app/{layout-9ecfd95f343793f0.js → layout-a16c7a7ecdf0c2cf.js} +1 -1
  193. package/dist/web/standalone/.next/static/chunks/app/page-6654a8cca61a3d1c.js +1 -0
  194. package/dist/web/standalone/.next/static/chunks/main-app-fdab67f7802d7832.js +1 -0
  195. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-459824ffb8c323dd.js +1 -0
  196. package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
  197. package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
  198. package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
  199. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api.target.mk +14 -14
  200. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_except.target.mk +14 -14
  201. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_maybe.target.mk +14 -14
  202. package/dist/web/standalone/server.js +1 -1
  203. package/package.json +2 -1
  204. package/packages/pi-coding-agent/dist/core/compaction-orchestrator.d.ts.map +1 -1
  205. package/packages/pi-coding-agent/dist/core/compaction-orchestrator.js +2 -0
  206. package/packages/pi-coding-agent/dist/core/compaction-orchestrator.js.map +1 -1
  207. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +2 -1
  208. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
  209. package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
  210. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.d.ts +4 -0
  211. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.d.ts.map +1 -1
  212. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.js +10 -5
  213. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.js.map +1 -1
  214. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.test.d.ts +2 -0
  215. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.test.d.ts.map +1 -0
  216. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.test.js +185 -0
  217. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.test.js.map +1 -0
  218. package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js +239 -10
  219. package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js.map +1 -1
  220. package/packages/pi-coding-agent/dist/core/model-registry.d.ts +2 -1
  221. package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
  222. package/packages/pi-coding-agent/dist/core/model-registry.js +20 -2
  223. package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
  224. package/packages/pi-coding-agent/dist/core/package-commands.test.js +206 -195
  225. package/packages/pi-coding-agent/dist/core/package-commands.test.js.map +1 -1
  226. package/packages/pi-coding-agent/package.json +1 -1
  227. package/packages/pi-coding-agent/src/core/compaction-orchestrator.ts +2 -0
  228. package/packages/pi-coding-agent/src/core/extensions/types.ts +2 -1
  229. package/packages/pi-coding-agent/src/core/lifecycle-hooks.test.ts +227 -0
  230. package/packages/pi-coding-agent/src/core/lifecycle-hooks.ts +11 -5
  231. package/packages/pi-coding-agent/src/core/model-registry-auth-mode.test.ts +297 -11
  232. package/packages/pi-coding-agent/src/core/model-registry.ts +30 -3
  233. package/packages/pi-coding-agent/src/core/package-commands.test.ts +227 -205
  234. package/pkg/package.json +1 -1
  235. package/src/resources/extensions/gsd/auto/loop-deps.ts +0 -19
  236. package/src/resources/extensions/gsd/auto/phases.ts +24 -44
  237. package/src/resources/extensions/gsd/auto/run-unit.ts +6 -3
  238. package/src/resources/extensions/gsd/auto/session.ts +0 -18
  239. package/src/resources/extensions/gsd/auto-artifact-paths.ts +131 -0
  240. package/src/resources/extensions/gsd/auto-dashboard.ts +0 -1
  241. package/src/resources/extensions/gsd/auto-post-unit.ts +25 -106
  242. package/src/resources/extensions/gsd/auto-start.ts +1 -3
  243. package/src/resources/extensions/gsd/auto-worktree.ts +8 -5
  244. package/src/resources/extensions/gsd/auto.ts +7 -83
  245. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +15 -12
  246. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +22 -0
  247. package/src/resources/extensions/gsd/commands/context.ts +0 -5
  248. package/src/resources/extensions/gsd/commands/handlers/parallel.ts +1 -1
  249. package/src/resources/extensions/gsd/crash-recovery.ts +1 -5
  250. package/src/resources/extensions/gsd/dashboard-overlay.ts +0 -50
  251. package/src/resources/extensions/gsd/db-writer.ts +9 -17
  252. package/src/resources/extensions/gsd/doctor-checks.ts +180 -2
  253. package/src/resources/extensions/gsd/doctor-types.ts +7 -1
  254. package/src/resources/extensions/gsd/doctor.ts +6 -3
  255. package/src/resources/extensions/gsd/gsd-db.ts +16 -3
  256. package/src/resources/extensions/gsd/guided-flow.ts +1 -2
  257. package/src/resources/extensions/gsd/journal.ts +6 -1
  258. package/src/resources/extensions/gsd/parallel-merge.ts +1 -1
  259. package/src/resources/extensions/gsd/parallel-orchestrator.ts +5 -21
  260. package/src/resources/extensions/gsd/preferences-types.ts +2 -2
  261. package/src/resources/extensions/gsd/preferences.ts +7 -3
  262. package/src/resources/extensions/gsd/prompts/complete-milestone.md +21 -8
  263. package/src/resources/extensions/gsd/prompts/complete-slice.md +10 -23
  264. package/src/resources/extensions/gsd/prompts/discuss.md +2 -2
  265. package/src/resources/extensions/gsd/prompts/execute-task.md +5 -15
  266. package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +1 -1
  267. package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +1 -1
  268. package/src/resources/extensions/gsd/prompts/guided-plan-slice.md +1 -1
  269. package/src/resources/extensions/gsd/prompts/guided-research-slice.md +1 -1
  270. package/src/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
  271. package/src/resources/extensions/gsd/prompts/plan-slice.md +4 -2
  272. package/src/resources/extensions/gsd/prompts/queue.md +2 -2
  273. package/src/resources/extensions/gsd/prompts/quick-task.md +2 -0
  274. package/src/resources/extensions/gsd/prompts/reactive-execute.md +1 -1
  275. package/src/resources/extensions/gsd/prompts/research-slice.md +3 -3
  276. package/src/resources/extensions/gsd/prompts/rethink.md +7 -2
  277. package/src/resources/extensions/gsd/prompts/system.md +1 -1
  278. package/src/resources/extensions/gsd/session-lock.ts +0 -4
  279. package/src/resources/extensions/gsd/state.ts +8 -0
  280. package/src/resources/extensions/gsd/sync-lock.ts +94 -0
  281. package/src/resources/extensions/gsd/tests/auto-lock-creation.test.ts +5 -13
  282. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +6 -10
  283. package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +96 -0
  284. package/src/resources/extensions/gsd/tests/complete-slice.test.ts +264 -228
  285. package/src/resources/extensions/gsd/tests/complete-task.test.ts +317 -250
  286. package/src/resources/extensions/gsd/tests/crash-recovery.test.ts +2 -8
  287. package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +0 -3
  288. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +1 -1
  289. package/src/resources/extensions/gsd/tests/idle-recovery.test.ts +1 -1
  290. package/src/resources/extensions/gsd/tests/integration-proof.test.ts +15 -24
  291. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +0 -3
  292. package/src/resources/extensions/gsd/tests/md-importer.test.ts +1 -1
  293. package/src/resources/extensions/gsd/tests/memory-store.test.ts +2 -2
  294. package/src/resources/extensions/gsd/tests/merge-conflict-stops-loop.test.ts +1 -1
  295. package/src/resources/extensions/gsd/tests/milestone-transition-state-rebuild.test.ts +8 -9
  296. package/src/resources/extensions/gsd/tests/none-mode-gates.test.ts +42 -3
  297. package/src/resources/extensions/gsd/tests/parallel-budget-atomicity.test.ts +0 -1
  298. package/src/resources/extensions/gsd/tests/parallel-crash-recovery.test.ts +0 -7
  299. package/src/resources/extensions/gsd/tests/parallel-merge.test.ts +7 -8
  300. package/src/resources/extensions/gsd/tests/parallel-orchestration.test.ts +20 -24
  301. package/src/resources/extensions/gsd/tests/parallel-worker-monitoring.test.ts +0 -2
  302. package/src/resources/extensions/gsd/tests/plan-milestone.test.ts +9 -6
  303. package/src/resources/extensions/gsd/tests/post-mutation-hook.test.ts +171 -0
  304. package/src/resources/extensions/gsd/tests/preferences.test.ts +7 -9
  305. package/src/resources/extensions/gsd/tests/projection-regression.test.ts +174 -0
  306. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +15 -14
  307. package/src/resources/extensions/gsd/tests/reopen-slice.test.ts +155 -0
  308. package/src/resources/extensions/gsd/tests/reopen-task.test.ts +165 -0
  309. package/src/resources/extensions/gsd/tests/session-lock-regression.test.ts +1 -4
  310. package/src/resources/extensions/gsd/tests/stop-auto-remote.test.ts +2 -3
  311. package/src/resources/extensions/gsd/tests/sync-lock.test.ts +122 -0
  312. package/src/resources/extensions/gsd/tests/unit-ownership.test.ts +175 -0
  313. package/src/resources/extensions/gsd/tests/workflow-events.test.ts +205 -0
  314. package/src/resources/extensions/gsd/tests/workflow-logger.test.ts +275 -0
  315. package/src/resources/extensions/gsd/tests/workflow-manifest.test.ts +186 -0
  316. package/src/resources/extensions/gsd/tests/workflow-projections.test.ts +171 -0
  317. package/src/resources/extensions/gsd/tests/worktree-journal-events.test.ts +220 -0
  318. package/src/resources/extensions/gsd/tests/write-intercept.test.ts +76 -0
  319. package/src/resources/extensions/gsd/tools/complete-milestone.ts +74 -11
  320. package/src/resources/extensions/gsd/tools/complete-slice.ts +68 -11
  321. package/src/resources/extensions/gsd/tools/complete-task.ts +63 -1
  322. package/src/resources/extensions/gsd/tools/plan-milestone.ts +45 -0
  323. package/src/resources/extensions/gsd/tools/plan-slice.ts +38 -0
  324. package/src/resources/extensions/gsd/tools/plan-task.ts +35 -1
  325. package/src/resources/extensions/gsd/tools/reassess-roadmap.ts +39 -1
  326. package/src/resources/extensions/gsd/tools/reopen-slice.ts +125 -0
  327. package/src/resources/extensions/gsd/tools/reopen-task.ts +129 -0
  328. package/src/resources/extensions/gsd/tools/replan-slice.ts +38 -1
  329. package/src/resources/extensions/gsd/types.ts +8 -0
  330. package/src/resources/extensions/gsd/unit-ownership.ts +104 -0
  331. package/src/resources/extensions/gsd/workflow-events.ts +154 -0
  332. package/src/resources/extensions/gsd/workflow-logger.ts +243 -0
  333. package/src/resources/extensions/gsd/workflow-manifest.ts +334 -0
  334. package/src/resources/extensions/gsd/workflow-migration.ts +345 -0
  335. package/src/resources/extensions/gsd/workflow-projections.ts +425 -0
  336. package/src/resources/extensions/gsd/workflow-reconcile.ts +503 -0
  337. package/src/resources/extensions/gsd/worktree-manager.ts +4 -9
  338. package/src/resources/extensions/gsd/worktree-resolver.ts +37 -0
  339. package/src/resources/extensions/gsd/write-intercept.ts +90 -0
  340. package/src/resources/extensions/voice/index.ts +11 -21
  341. package/src/resources/extensions/voice/linux-ready.ts +87 -0
  342. package/src/resources/extensions/voice/tests/linux-ready.test.ts +124 -0
  343. package/dist/web/standalone/.next/static/chunks/app/page-12dd5ece0df4badc.js +0 -1
  344. package/dist/web/standalone/.next/static/chunks/main-app-d3d4c336195465f9.js +0 -1
  345. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-ab5a8926e07ec673.js +0 -1
  346. /package/dist/web/standalone/.next/static/{wUzEX1U3CmFcMry2SUDJn → ZIDqryyYDroh_8AnaAOSG}/_buildManifest.js +0 -0
  347. /package/dist/web/standalone/.next/static/{wUzEX1U3CmFcMry2SUDJn → ZIDqryyYDroh_8AnaAOSG}/_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,356 @@ 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 (v11 after state machine migration)
113
+ const versionRow = adapter.prepare('SELECT MAX(version) as v FROM schema_version').get();
114
+ assertEq(versionRow?.['v'], 11, 'schema version should be 11');
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
+ // Seed milestone + slice + both tasks so projection renders T01 ([x]) and T02 ([ ])
287
+ insertMilestone({ id: 'M001', title: 'Test Milestone' });
288
+ insertSlice({ id: 'S01', milestoneId: 'M001', title: 'Test Slice' });
289
+ insertTask({ id: 'T02', sliceId: 'S01', milestoneId: 'M001', status: 'pending', title: 'Second task' });
290
+
291
+ const params = makeValidParams();
292
+ const result = await handleCompleteTask(params, basePath);
293
+
294
+ assertTrue(!('error' in result), 'handler should succeed without error');
295
+ if (!('error' in result)) {
296
+ assertEq(result.taskId, 'T01', 'result taskId');
297
+ assertEq(result.sliceId, 'S01', 'result sliceId');
298
+ assertEq(result.milestoneId, 'M001', 'result milestoneId');
299
+ assertTrue(result.summaryPath.endsWith('T01-SUMMARY.md'), 'summaryPath should end with T01-SUMMARY.md');
300
+
301
+ // (a) Verify task row in DB with status 'complete'
302
+ const task = getTask('M001', 'S01', 'T01');
303
+ assertTrue(task !== null, 'task should exist in DB after handler');
304
+ assertEq(task!.status, 'complete', 'task status should be complete');
305
+ assertEq(task!.one_liner, 'Added test functionality', 'task one_liner in DB');
306
+ assertEq(task!.key_files, ['src/test.ts', 'src/test.test.ts'], 'task key_files in DB');
307
+
308
+ // (b) Verify verification_evidence rows in DB
309
+ const adapter = _getAdapter()!;
196
310
  const evRows = adapter.prepare(
197
- "SELECT * FROM verification_evidence WHERE task_id = 'T01' AND slice_id = 'S01' AND milestone_id = 'M001'"
311
+ "SELECT * FROM verification_evidence WHERE task_id = 'T01' AND milestone_id = 'M001'"
198
312
  ).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
- });
313
+ assertEq(evRows.length, 1, 'should have 1 verification evidence row after handler');
314
+ assertEq(evRows[0]['command'], 'npm run test:unit', 'evidence command from handler');
315
+
316
+ // (c) Verify T01-SUMMARY.md file on disk with correct YAML frontmatter
317
+ assertTrue(fs.existsSync(result.summaryPath), 'summary file should exist on disk');
318
+ const summaryContent = fs.readFileSync(result.summaryPath, 'utf-8');
319
+ assertMatch(summaryContent, /^---\n/, 'summary should start with YAML frontmatter');
320
+ assertMatch(summaryContent, /id: T01/, 'summary should contain id: T01');
321
+ assertMatch(summaryContent, /parent: S01/, 'summary should contain parent: S01');
322
+ assertMatch(summaryContent, /milestone: M001/, 'summary should contain milestone: M001');
323
+ assertMatch(summaryContent, /blocker_discovered: false/, 'summary should contain blocker_discovered');
324
+ assertMatch(summaryContent, /# T01:/, 'summary should have H1 with task ID');
325
+ assertMatch(summaryContent, /\*\*Added test functionality\*\*/, 'summary should have one-liner in bold');
326
+ assertMatch(summaryContent, /## What Happened/, 'summary should have What Happened section');
327
+ assertMatch(summaryContent, /## Verification Evidence/, 'summary should have Verification Evidence section');
328
+ assertMatch(summaryContent, /npm run test:unit/, 'summary evidence should contain command');
329
+
330
+ // (d) Verify plan checkbox changed to [x]
331
+ const planContent = fs.readFileSync(planPath, 'utf-8');
332
+ assertMatch(planContent, /\[x\]\s+\*\*T01:/, 'T01 should be checked in plan');
333
+ // T02 should still be unchecked
334
+ assertMatch(planContent, /\[ \]\s+\*\*T02:/, 'T02 should still be unchecked in plan');
335
+
336
+ // (e) Verify full_summary_md stored in DB for D004 recovery
337
+ const taskAfter = getTask('M001', 'S01', 'T01');
338
+ assertTrue(taskAfter!.full_summary_md.length > 0, 'full_summary_md should be non-empty in DB');
339
+ assertMatch(taskAfter!.full_summary_md, /id: T01/, 'full_summary_md should contain frontmatter');
340
+ }
219
341
 
220
- describe("complete-task: accessor stale-state error", () => {
221
- test("accessors throw when no DB open", () => {
222
- closeDatabase();
342
+ cleanupDir(basePath);
343
+ cleanup(dbPath);
344
+ }
223
345
 
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');
346
+ // ═══════════════════════════════════════════════════════════════════════════
347
+ // complete-task: Handler validation errors
348
+ // ═══════════════════════════════════════════════════════════════════════════
227
349
 
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');
350
+ console.log('\n=== complete-task: handler validation errors ===');
351
+ {
352
+ const dbPath = tempDbPath();
353
+ openDatabase(dbPath);
231
354
 
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');
355
+ const params = makeValidParams();
235
356
 
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
- }
357
+ // Empty taskId
358
+ const r1 = await handleCompleteTask({ ...params, taskId: '' }, '/tmp/fake');
359
+ assertTrue('error' in r1, 'should return error for empty taskId');
360
+ if ('error' in r1) {
361
+ assertMatch(r1.error, /taskId/, 'error should mention taskId');
362
+ }
302
363
 
303
- cleanupDir(basePath);
304
- cleanup(dbPath);
305
- });
364
+ // Empty milestoneId
365
+ const r2 = await handleCompleteTask({ ...params, milestoneId: '' }, '/tmp/fake');
366
+ assertTrue('error' in r2, 'should return error for empty milestoneId');
367
+ if ('error' in r2) {
368
+ assertMatch(r2.error, /milestoneId/, 'error should mention milestoneId');
369
+ }
370
+
371
+ // Empty sliceId
372
+ const r3 = await handleCompleteTask({ ...params, sliceId: '' }, '/tmp/fake');
373
+ assertTrue('error' in r3, 'should return error for empty sliceId');
374
+ if ('error' in r3) {
375
+ assertMatch(r3.error, /sliceId/, 'error should mention sliceId');
376
+ }
306
377
 
307
- test("validation errors", async () => {
308
- const dbPath = tempDbPath();
309
- openDatabase(dbPath);
378
+ cleanup(dbPath);
379
+ }
310
380
 
311
- const params = makeValidParams();
381
+ // ═══════════════════════════════════════════════════════════════════════════
382
+ // complete-task: Handler idempotency
383
+ // ═══════════════════════════════════════════════════════════════════════════
312
384
 
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
- }
385
+ console.log('\n=== complete-task: handler idempotency ===');
386
+ {
387
+ const dbPath = tempDbPath();
388
+ openDatabase(dbPath);
319
389
 
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
- }
390
+ const { basePath, planPath } = createTempProject();
326
391
 
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
- }
392
+ // Seed milestone + slice so state machine guards pass
393
+ insertMilestone({ id: 'M001', title: 'Test Milestone' });
394
+ insertSlice({ id: 'S01', milestoneId: 'M001', title: 'Test Slice' });
333
395
 
334
- cleanup(dbPath);
335
- });
396
+ const params = makeValidParams();
336
397
 
337
- test("idempotency", async () => {
338
- const dbPath = tempDbPath();
339
- openDatabase(dbPath);
398
+ // First call should succeed
399
+ const r1 = await handleCompleteTask(params, basePath);
400
+ assertTrue(!('error' in r1), 'first call should succeed');
340
401
 
341
- const { basePath, planPath } = createTempProject();
402
+ // Verify only 1 task row
403
+ const tasks = getSliceTasks('M001', 'S01');
404
+ assertEq(tasks.length, 1, 'should have exactly 1 task row after first call');
342
405
 
343
- const params = makeValidParams();
406
+ // Second call with same params state machine guard rejects (task is already complete)
407
+ const r2 = await handleCompleteTask(params, basePath);
408
+ assertTrue('error' in r2, 'second call should return error (task already complete)');
409
+ if ('error' in r2) {
410
+ assertMatch(r2.error, /already complete/, 'error should mention already complete');
411
+ }
344
412
 
345
- // First call
346
- const r1 = await handleCompleteTask(params, basePath);
347
- assert.ok(!('error' in r1), 'first call should succeed');
413
+ // Still only 1 task row (no duplication from rejected second call)
414
+ const tasksAfter = getSliceTasks('M001', 'S01');
415
+ assertEq(tasksAfter.length, 1, 'should still have exactly 1 task row after rejected second call');
348
416
 
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)');
417
+ cleanupDir(basePath);
418
+ cleanup(dbPath);
419
+ }
352
420
 
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)');
421
+ // ═══════════════════════════════════════════════════════════════════════════
422
+ // complete-task: Handler with missing plan file (graceful)
423
+ // ═══════════════════════════════════════════════════════════════════════════
356
424
 
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
- }
425
+ console.log('\n=== complete-task: handler with missing plan file ===');
426
+ {
427
+ const dbPath = tempDbPath();
428
+ openDatabase(dbPath);
361
429
 
362
- cleanupDir(basePath);
363
- cleanup(dbPath);
364
- });
430
+ // Create a temp dir WITHOUT a plan file
431
+ const basePath = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-no-plan-'));
432
+ const tasksDir = path.join(basePath, '.gsd', 'milestones', 'M001', 'slices', 'S01', 'tasks');
433
+ fs.mkdirSync(tasksDir, { recursive: true });
434
+
435
+ // Seed milestone + slice so state machine guards pass
436
+ insertMilestone({ id: 'M001', title: 'Test Milestone' });
437
+ insertSlice({ id: 'S01', milestoneId: 'M001', title: 'Test Slice' });
365
438
 
366
- test("missing plan file (graceful)", async () => {
367
- const dbPath = tempDbPath();
368
- openDatabase(dbPath);
439
+ const params = makeValidParams();
440
+ const result = await handleCompleteTask(params, basePath);
369
441
 
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 });
442
+ // Should succeed even without plan file just skip checkbox toggle
443
+ assertTrue(!('error' in result), 'handler should succeed without plan file');
444
+ if (!('error' in result)) {
445
+ assertTrue(fs.existsSync(result.summaryPath), 'summary should be written even without plan file');
446
+ }
374
447
 
375
- const params = makeValidParams();
376
- const result = await handleCompleteTask(params, basePath);
448
+ cleanupDir(basePath);
449
+ cleanup(dbPath);
450
+ }
377
451
 
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
- }
452
+ // ═══════════════════════════════════════════════════════════════════════════
383
453
 
384
- cleanupDir(basePath);
385
- cleanup(dbPath);
386
- });
387
- });
454
+ report();