gsd-pi 2.44.0-dev.d25d507 → 2.45.0-dev.1afbdaa

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 (407) hide show
  1. package/dist/help-text.js +1 -1
  2. package/dist/loader.js +34 -0
  3. package/dist/resources/extensions/gsd/activity-log.js +7 -0
  4. package/dist/resources/extensions/gsd/auto/infra-errors.js +3 -0
  5. package/dist/resources/extensions/gsd/auto/phases.js +63 -77
  6. package/dist/resources/extensions/gsd/auto/run-unit.js +6 -3
  7. package/dist/resources/extensions/gsd/auto/session.js +0 -11
  8. package/dist/resources/extensions/gsd/auto-artifact-paths.js +112 -0
  9. package/dist/resources/extensions/gsd/auto-post-unit.js +25 -96
  10. package/dist/resources/extensions/gsd/auto-prompts.js +24 -1
  11. package/dist/resources/extensions/gsd/auto-start.js +23 -5
  12. package/dist/resources/extensions/gsd/auto-timers.js +57 -3
  13. package/dist/resources/extensions/gsd/auto-worktree-sync.js +4 -0
  14. package/dist/resources/extensions/gsd/auto-worktree.js +14 -10
  15. package/dist/resources/extensions/gsd/auto.js +42 -60
  16. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +170 -11
  17. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +18 -0
  18. package/dist/resources/extensions/gsd/commands/catalog.js +7 -1
  19. package/dist/resources/extensions/gsd/commands/context.js +0 -4
  20. package/dist/resources/extensions/gsd/commands/handlers/core.js +2 -0
  21. package/dist/resources/extensions/gsd/commands/handlers/ops.js +10 -0
  22. package/dist/resources/extensions/gsd/commands/handlers/parallel.js +1 -1
  23. package/dist/resources/extensions/gsd/commands-mcp-status.js +187 -0
  24. package/dist/resources/extensions/gsd/crash-recovery.js +2 -4
  25. package/dist/resources/extensions/gsd/dashboard-overlay.js +0 -44
  26. package/dist/resources/extensions/gsd/db-writer.js +40 -22
  27. package/dist/resources/extensions/gsd/doctor-checks.js +167 -2
  28. package/dist/resources/extensions/gsd/doctor.js +13 -3
  29. package/dist/resources/extensions/gsd/git-service.js +8 -3
  30. package/dist/resources/extensions/gsd/gsd-db.js +28 -4
  31. package/dist/resources/extensions/gsd/guided-flow.js +1 -2
  32. package/dist/resources/extensions/gsd/markdown-renderer.js +1 -1
  33. package/dist/resources/extensions/gsd/parallel-merge.js +1 -1
  34. package/dist/resources/extensions/gsd/parallel-orchestrator.js +5 -18
  35. package/dist/resources/extensions/gsd/preferences-types.js +2 -2
  36. package/dist/resources/extensions/gsd/preferences.js +8 -4
  37. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +21 -10
  38. package/dist/resources/extensions/gsd/prompts/complete-slice.md +10 -23
  39. package/dist/resources/extensions/gsd/prompts/discuss.md +2 -2
  40. package/dist/resources/extensions/gsd/prompts/execute-task.md +5 -15
  41. package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +1 -1
  42. package/dist/resources/extensions/gsd/prompts/guided-discuss-slice.md +1 -1
  43. package/dist/resources/extensions/gsd/prompts/guided-plan-slice.md +1 -1
  44. package/dist/resources/extensions/gsd/prompts/guided-research-slice.md +1 -1
  45. package/dist/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
  46. package/dist/resources/extensions/gsd/prompts/plan-slice.md +5 -3
  47. package/dist/resources/extensions/gsd/prompts/queue.md +2 -2
  48. package/dist/resources/extensions/gsd/prompts/quick-task.md +2 -0
  49. package/dist/resources/extensions/gsd/prompts/reactive-execute.md +1 -1
  50. package/dist/resources/extensions/gsd/prompts/reassess-roadmap.md +6 -6
  51. package/dist/resources/extensions/gsd/prompts/replan-slice.md +3 -14
  52. package/dist/resources/extensions/gsd/prompts/research-slice.md +3 -3
  53. package/dist/resources/extensions/gsd/prompts/rethink.md +83 -0
  54. package/dist/resources/extensions/gsd/prompts/system.md +1 -1
  55. package/dist/resources/extensions/gsd/prompts/validate-milestone.md +7 -37
  56. package/dist/resources/extensions/gsd/provider-error-pause.js +7 -0
  57. package/dist/resources/extensions/gsd/repo-identity.js +45 -7
  58. package/dist/resources/extensions/gsd/rethink.js +115 -0
  59. package/dist/resources/extensions/gsd/session-lock.js +1 -3
  60. package/dist/resources/extensions/gsd/state.js +48 -3
  61. package/dist/resources/extensions/gsd/sync-lock.js +89 -0
  62. package/dist/resources/extensions/gsd/tools/complete-milestone.js +61 -11
  63. package/dist/resources/extensions/gsd/tools/complete-slice.js +56 -11
  64. package/dist/resources/extensions/gsd/tools/complete-task.js +50 -2
  65. package/dist/resources/extensions/gsd/tools/plan-milestone.js +37 -1
  66. package/dist/resources/extensions/gsd/tools/plan-slice.js +31 -1
  67. package/dist/resources/extensions/gsd/tools/plan-task.js +28 -1
  68. package/dist/resources/extensions/gsd/tools/reassess-roadmap.js +32 -2
  69. package/dist/resources/extensions/gsd/tools/reopen-slice.js +86 -0
  70. package/dist/resources/extensions/gsd/tools/reopen-task.js +90 -0
  71. package/dist/resources/extensions/gsd/tools/replan-slice.js +34 -2
  72. package/dist/resources/extensions/gsd/tools/validate-milestone.js +88 -0
  73. package/dist/resources/extensions/gsd/unit-ownership.js +85 -0
  74. package/dist/resources/extensions/gsd/workflow-events.js +102 -0
  75. package/dist/resources/extensions/gsd/workflow-logger.js +193 -0
  76. package/dist/resources/extensions/gsd/workflow-manifest.js +244 -0
  77. package/dist/resources/extensions/gsd/workflow-migration.js +280 -0
  78. package/dist/resources/extensions/gsd/workflow-projections.js +373 -0
  79. package/dist/resources/extensions/gsd/workflow-reconcile.js +411 -0
  80. package/dist/resources/extensions/gsd/worktree-manager.js +34 -3
  81. package/dist/resources/extensions/gsd/worktree-resolver.js +43 -0
  82. package/dist/resources/extensions/gsd/write-intercept.js +84 -0
  83. package/dist/resources/extensions/mcp-client/index.js +14 -0
  84. package/dist/resources/extensions/voice/index.js +11 -16
  85. package/dist/resources/extensions/voice/linux-ready.js +67 -0
  86. package/dist/web/standalone/.next/BUILD_ID +1 -1
  87. package/dist/web/standalone/.next/app-path-routes-manifest.json +14 -14
  88. package/dist/web/standalone/.next/build-manifest.json +3 -3
  89. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  90. package/dist/web/standalone/.next/react-loadable-manifest.json +2 -2
  91. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  92. package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
  93. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  94. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  95. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  96. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  97. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  98. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  99. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  100. package/dist/web/standalone/.next/server/app/_not-found/page.js +1 -1
  101. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  102. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  103. package/dist/web/standalone/.next/server/app/_not-found.rsc +3 -3
  104. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
  105. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  106. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
  107. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  108. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  109. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  110. package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
  111. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
  112. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
  113. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
  114. package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
  115. package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
  116. package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
  117. package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
  118. package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
  119. package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
  120. package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
  121. package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
  122. package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
  123. package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
  124. package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
  125. package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
  126. package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.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_client-reference-manifest.js +1 -1
  129. package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
  130. package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
  131. package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
  132. package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
  133. package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
  134. package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
  135. package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
  136. package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
  137. package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
  138. package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
  139. package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
  140. package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
  141. package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
  142. package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
  143. package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
  144. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
  145. package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
  146. package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
  147. package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
  148. package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
  149. package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
  150. package/dist/web/standalone/.next/server/app/index.html +1 -1
  151. package/dist/web/standalone/.next/server/app/index.rsc +4 -4
  152. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  153. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +4 -4
  154. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  155. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +2 -2
  156. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  157. package/dist/web/standalone/.next/server/app/page.js +1 -1
  158. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  159. package/dist/web/standalone/.next/server/app-paths-manifest.json +14 -14
  160. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  161. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  162. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  163. package/dist/web/standalone/.next/server/pages/500.html +2 -2
  164. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  165. package/dist/web/standalone/.next/static/chunks/4024.11ca5c01938e5948.js +9 -0
  166. package/dist/web/standalone/.next/static/chunks/{3721.bf31263de6d5fa46.js → 485.243af25f0cdf50d6.js} +2 -2
  167. package/dist/web/standalone/.next/static/chunks/app/{page-b9367c5ae13b99c6.js → page-6654a8cca61a3d1c.js} +1 -1
  168. package/dist/web/standalone/.next/static/chunks/webpack-0a4cd455ec4197d2.js +1 -0
  169. package/dist/web/standalone/.next/static/css/dd4ae3f58ac9b600.css +1 -0
  170. package/package.json +2 -1
  171. package/packages/native/dist/stream-process/index.js +2 -2
  172. package/packages/native/src/__tests__/stream-process.test.mjs +34 -0
  173. package/packages/native/src/stream-process/index.ts +2 -2
  174. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts +3 -1
  175. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts.map +1 -1
  176. package/packages/pi-coding-agent/dist/core/auth-storage.js +15 -1
  177. package/packages/pi-coding-agent/dist/core/auth-storage.js.map +1 -1
  178. package/packages/pi-coding-agent/dist/core/compaction-orchestrator.d.ts.map +1 -1
  179. package/packages/pi-coding-agent/dist/core/compaction-orchestrator.js +2 -0
  180. package/packages/pi-coding-agent/dist/core/compaction-orchestrator.js.map +1 -1
  181. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +2 -1
  182. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
  183. package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
  184. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.d.ts +4 -0
  185. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.d.ts.map +1 -1
  186. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.js +10 -5
  187. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.js.map +1 -1
  188. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.test.d.ts +2 -0
  189. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.test.d.ts.map +1 -0
  190. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.test.js +185 -0
  191. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.test.js.map +1 -0
  192. package/packages/pi-coding-agent/dist/core/local-model-check.d.ts +15 -0
  193. package/packages/pi-coding-agent/dist/core/local-model-check.d.ts.map +1 -0
  194. package/packages/pi-coding-agent/dist/core/local-model-check.js +41 -0
  195. package/packages/pi-coding-agent/dist/core/local-model-check.js.map +1 -0
  196. package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js +239 -10
  197. package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js.map +1 -1
  198. package/packages/pi-coding-agent/dist/core/model-registry.d.ts +13 -1
  199. package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
  200. package/packages/pi-coding-agent/dist/core/model-registry.js +40 -3
  201. package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
  202. package/packages/pi-coding-agent/dist/core/package-commands.test.js +206 -195
  203. package/packages/pi-coding-agent/dist/core/package-commands.test.js.map +1 -1
  204. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +3 -0
  205. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
  206. package/packages/pi-coding-agent/dist/core/settings-manager.js +6 -0
  207. package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
  208. package/packages/pi-coding-agent/dist/main.d.ts.map +1 -1
  209. package/packages/pi-coding-agent/dist/main.js +17 -0
  210. package/packages/pi-coding-agent/dist/main.js.map +1 -1
  211. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.d.ts +2 -0
  212. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.d.ts.map +1 -0
  213. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.js +32 -0
  214. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.js.map +1 -0
  215. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts +3 -1
  216. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  217. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js +8 -1
  218. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js.map +1 -1
  219. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts +2 -0
  220. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  221. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js +12 -0
  222. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js.map +1 -1
  223. package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.d.ts +15 -0
  224. package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.d.ts.map +1 -0
  225. package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.js +40 -0
  226. package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.js.map +1 -0
  227. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  228. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +4 -1
  229. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
  230. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.d.ts +5 -2
  231. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.d.ts.map +1 -1
  232. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.js +13 -2
  233. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.js.map +1 -1
  234. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  235. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +17 -8
  236. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  237. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  238. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +7 -3
  239. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  240. package/packages/pi-coding-agent/package.json +1 -1
  241. package/packages/pi-coding-agent/src/core/auth-storage.ts +15 -1
  242. package/packages/pi-coding-agent/src/core/compaction-orchestrator.ts +2 -0
  243. package/packages/pi-coding-agent/src/core/extensions/types.ts +2 -1
  244. package/packages/pi-coding-agent/src/core/lifecycle-hooks.test.ts +227 -0
  245. package/packages/pi-coding-agent/src/core/lifecycle-hooks.ts +11 -5
  246. package/packages/pi-coding-agent/src/core/local-model-check.ts +45 -0
  247. package/packages/pi-coding-agent/src/core/model-registry-auth-mode.test.ts +297 -11
  248. package/packages/pi-coding-agent/src/core/model-registry.ts +51 -4
  249. package/packages/pi-coding-agent/src/core/package-commands.test.ts +227 -205
  250. package/packages/pi-coding-agent/src/core/settings-manager.ts +9 -0
  251. package/packages/pi-coding-agent/src/main.ts +19 -0
  252. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/timestamp.test.ts +38 -0
  253. package/packages/pi-coding-agent/src/modes/interactive/components/assistant-message.ts +10 -0
  254. package/packages/pi-coding-agent/src/modes/interactive/components/settings-selector.ts +15 -0
  255. package/packages/pi-coding-agent/src/modes/interactive/components/timestamp.ts +48 -0
  256. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +3 -1
  257. package/packages/pi-coding-agent/src/modes/interactive/components/user-message.ts +18 -3
  258. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +16 -7
  259. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +8 -1
  260. package/pkg/package.json +1 -1
  261. package/src/resources/extensions/gsd/activity-log.ts +1 -0
  262. package/src/resources/extensions/gsd/auto/infra-errors.ts +3 -0
  263. package/src/resources/extensions/gsd/auto/loop-deps.ts +0 -19
  264. package/src/resources/extensions/gsd/auto/phases.ts +69 -91
  265. package/src/resources/extensions/gsd/auto/run-unit.ts +6 -3
  266. package/src/resources/extensions/gsd/auto/session.ts +0 -18
  267. package/src/resources/extensions/gsd/auto-artifact-paths.ts +131 -0
  268. package/src/resources/extensions/gsd/auto-dashboard.ts +0 -1
  269. package/src/resources/extensions/gsd/auto-post-unit.ts +25 -106
  270. package/src/resources/extensions/gsd/auto-prompts.ts +24 -1
  271. package/src/resources/extensions/gsd/auto-start.ts +26 -5
  272. package/src/resources/extensions/gsd/auto-timers.ts +64 -3
  273. package/src/resources/extensions/gsd/auto-worktree-sync.ts +5 -0
  274. package/src/resources/extensions/gsd/auto-worktree.ts +17 -11
  275. package/src/resources/extensions/gsd/auto.ts +44 -86
  276. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +162 -11
  277. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +22 -0
  278. package/src/resources/extensions/gsd/commands/catalog.ts +7 -1
  279. package/src/resources/extensions/gsd/commands/context.ts +0 -5
  280. package/src/resources/extensions/gsd/commands/handlers/core.ts +2 -0
  281. package/src/resources/extensions/gsd/commands/handlers/ops.ts +10 -0
  282. package/src/resources/extensions/gsd/commands/handlers/parallel.ts +1 -1
  283. package/src/resources/extensions/gsd/commands-mcp-status.ts +247 -0
  284. package/src/resources/extensions/gsd/crash-recovery.ts +1 -5
  285. package/src/resources/extensions/gsd/dashboard-overlay.ts +0 -50
  286. package/src/resources/extensions/gsd/db-writer.ts +41 -27
  287. package/src/resources/extensions/gsd/doctor-checks.ts +180 -2
  288. package/src/resources/extensions/gsd/doctor-types.ts +7 -1
  289. package/src/resources/extensions/gsd/doctor.ts +13 -4
  290. package/src/resources/extensions/gsd/git-service.ts +6 -2
  291. package/src/resources/extensions/gsd/gsd-db.ts +32 -4
  292. package/src/resources/extensions/gsd/guided-flow.ts +1 -2
  293. package/src/resources/extensions/gsd/journal.ts +6 -1
  294. package/src/resources/extensions/gsd/markdown-renderer.ts +1 -1
  295. package/src/resources/extensions/gsd/parallel-merge.ts +1 -1
  296. package/src/resources/extensions/gsd/parallel-orchestrator.ts +5 -21
  297. package/src/resources/extensions/gsd/preferences-types.ts +2 -2
  298. package/src/resources/extensions/gsd/preferences.ts +7 -3
  299. package/src/resources/extensions/gsd/prompts/complete-milestone.md +21 -10
  300. package/src/resources/extensions/gsd/prompts/complete-slice.md +10 -23
  301. package/src/resources/extensions/gsd/prompts/discuss.md +2 -2
  302. package/src/resources/extensions/gsd/prompts/execute-task.md +5 -15
  303. package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +1 -1
  304. package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +1 -1
  305. package/src/resources/extensions/gsd/prompts/guided-plan-slice.md +1 -1
  306. package/src/resources/extensions/gsd/prompts/guided-research-slice.md +1 -1
  307. package/src/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
  308. package/src/resources/extensions/gsd/prompts/plan-slice.md +5 -3
  309. package/src/resources/extensions/gsd/prompts/queue.md +2 -2
  310. package/src/resources/extensions/gsd/prompts/quick-task.md +2 -0
  311. package/src/resources/extensions/gsd/prompts/reactive-execute.md +1 -1
  312. package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +6 -6
  313. package/src/resources/extensions/gsd/prompts/replan-slice.md +3 -14
  314. package/src/resources/extensions/gsd/prompts/research-slice.md +3 -3
  315. package/src/resources/extensions/gsd/prompts/rethink.md +83 -0
  316. package/src/resources/extensions/gsd/prompts/system.md +1 -1
  317. package/src/resources/extensions/gsd/prompts/validate-milestone.md +7 -37
  318. package/src/resources/extensions/gsd/provider-error-pause.ts +9 -0
  319. package/src/resources/extensions/gsd/repo-identity.ts +46 -7
  320. package/src/resources/extensions/gsd/rethink.ts +154 -0
  321. package/src/resources/extensions/gsd/session-lock.ts +0 -4
  322. package/src/resources/extensions/gsd/state.ts +49 -1
  323. package/src/resources/extensions/gsd/sync-lock.ts +94 -0
  324. package/src/resources/extensions/gsd/tests/auto-lock-creation.test.ts +5 -13
  325. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +6 -10
  326. package/src/resources/extensions/gsd/tests/auto-pr-bugs.test.ts +88 -0
  327. package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +96 -0
  328. package/src/resources/extensions/gsd/tests/complete-slice.test.ts +264 -228
  329. package/src/resources/extensions/gsd/tests/complete-task.test.ts +317 -250
  330. package/src/resources/extensions/gsd/tests/completed-units-metrics-sync.test.ts +114 -0
  331. package/src/resources/extensions/gsd/tests/crash-recovery.test.ts +2 -8
  332. package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +0 -3
  333. package/src/resources/extensions/gsd/tests/db-writer.test.ts +79 -0
  334. package/src/resources/extensions/gsd/tests/derive-state-db-disk-reconcile.test.ts +121 -0
  335. package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +60 -0
  336. package/src/resources/extensions/gsd/tests/est-annotation-timeout.test.ts +120 -0
  337. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +1 -1
  338. package/src/resources/extensions/gsd/tests/idle-recovery.test.ts +1 -1
  339. package/src/resources/extensions/gsd/tests/infra-error.test.ts +20 -2
  340. package/src/resources/extensions/gsd/tests/inherited-repo-home-dir.test.ts +121 -0
  341. package/src/resources/extensions/gsd/tests/integration-proof.test.ts +15 -24
  342. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +0 -3
  343. package/src/resources/extensions/gsd/tests/mcp-status.test.ts +103 -0
  344. package/src/resources/extensions/gsd/tests/md-importer.test.ts +1 -1
  345. package/src/resources/extensions/gsd/tests/memory-store.test.ts +2 -2
  346. package/src/resources/extensions/gsd/tests/merge-conflict-stops-loop.test.ts +66 -0
  347. package/src/resources/extensions/gsd/tests/milestone-transition-state-rebuild.test.ts +8 -9
  348. package/src/resources/extensions/gsd/tests/none-mode-gates.test.ts +42 -3
  349. package/src/resources/extensions/gsd/tests/parallel-budget-atomicity.test.ts +0 -1
  350. package/src/resources/extensions/gsd/tests/parallel-crash-recovery.test.ts +0 -7
  351. package/src/resources/extensions/gsd/tests/parallel-merge.test.ts +7 -8
  352. package/src/resources/extensions/gsd/tests/parallel-orchestration.test.ts +20 -24
  353. package/src/resources/extensions/gsd/tests/parallel-worker-monitoring.test.ts +0 -2
  354. package/src/resources/extensions/gsd/tests/plan-milestone.test.ts +9 -6
  355. package/src/resources/extensions/gsd/tests/post-mutation-hook.test.ts +171 -0
  356. package/src/resources/extensions/gsd/tests/preferences.test.ts +7 -9
  357. package/src/resources/extensions/gsd/tests/projection-regression.test.ts +174 -0
  358. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +26 -21
  359. package/src/resources/extensions/gsd/tests/recovery-attempts-reset.test.ts +176 -0
  360. package/src/resources/extensions/gsd/tests/reopen-slice.test.ts +155 -0
  361. package/src/resources/extensions/gsd/tests/reopen-task.test.ts +165 -0
  362. package/src/resources/extensions/gsd/tests/session-lock-regression.test.ts +1 -4
  363. package/src/resources/extensions/gsd/tests/stop-auto-merge-back.test.ts +67 -0
  364. package/src/resources/extensions/gsd/tests/stop-auto-remote.test.ts +2 -3
  365. package/src/resources/extensions/gsd/tests/survivor-branch-complete.test.ts +108 -0
  366. package/src/resources/extensions/gsd/tests/sync-lock.test.ts +122 -0
  367. package/src/resources/extensions/gsd/tests/terminated-transient.test.ts +49 -0
  368. package/src/resources/extensions/gsd/tests/tool-naming.test.ts +2 -1
  369. package/src/resources/extensions/gsd/tests/unit-ownership.test.ts +175 -0
  370. package/src/resources/extensions/gsd/tests/workflow-events.test.ts +205 -0
  371. package/src/resources/extensions/gsd/tests/workflow-logger.test.ts +275 -0
  372. package/src/resources/extensions/gsd/tests/workflow-manifest.test.ts +186 -0
  373. package/src/resources/extensions/gsd/tests/workflow-projections.test.ts +171 -0
  374. package/src/resources/extensions/gsd/tests/worktree-journal-events.test.ts +220 -0
  375. package/src/resources/extensions/gsd/tests/worktree-submodule-safety.test.ts +65 -0
  376. package/src/resources/extensions/gsd/tests/write-intercept.test.ts +76 -0
  377. package/src/resources/extensions/gsd/tools/complete-milestone.ts +74 -11
  378. package/src/resources/extensions/gsd/tools/complete-slice.ts +68 -11
  379. package/src/resources/extensions/gsd/tools/complete-task.ts +63 -1
  380. package/src/resources/extensions/gsd/tools/plan-milestone.ts +45 -0
  381. package/src/resources/extensions/gsd/tools/plan-slice.ts +40 -0
  382. package/src/resources/extensions/gsd/tools/plan-task.ts +37 -1
  383. package/src/resources/extensions/gsd/tools/reassess-roadmap.ts +39 -1
  384. package/src/resources/extensions/gsd/tools/reopen-slice.ts +125 -0
  385. package/src/resources/extensions/gsd/tools/reopen-task.ts +129 -0
  386. package/src/resources/extensions/gsd/tools/replan-slice.ts +41 -1
  387. package/src/resources/extensions/gsd/tools/validate-milestone.ts +127 -0
  388. package/src/resources/extensions/gsd/types.ts +8 -0
  389. package/src/resources/extensions/gsd/unit-ownership.ts +104 -0
  390. package/src/resources/extensions/gsd/workflow-events.ts +154 -0
  391. package/src/resources/extensions/gsd/workflow-logger.ts +243 -0
  392. package/src/resources/extensions/gsd/workflow-manifest.ts +334 -0
  393. package/src/resources/extensions/gsd/workflow-migration.ts +345 -0
  394. package/src/resources/extensions/gsd/workflow-projections.ts +425 -0
  395. package/src/resources/extensions/gsd/workflow-reconcile.ts +503 -0
  396. package/src/resources/extensions/gsd/worktree-manager.ts +41 -5
  397. package/src/resources/extensions/gsd/worktree-resolver.ts +44 -0
  398. package/src/resources/extensions/gsd/write-intercept.ts +90 -0
  399. package/src/resources/extensions/mcp-client/index.ts +20 -0
  400. package/src/resources/extensions/voice/index.ts +11 -21
  401. package/src/resources/extensions/voice/linux-ready.ts +87 -0
  402. package/src/resources/extensions/voice/tests/linux-ready.test.ts +124 -0
  403. package/dist/web/standalone/.next/static/chunks/4024.0de81b543b28b9fe.js +0 -9
  404. package/dist/web/standalone/.next/static/chunks/webpack-9014b5adb127a98a.js +0 -1
  405. package/dist/web/standalone/.next/static/css/8a727f372cf53002.css +0 -1
  406. /package/dist/web/standalone/.next/static/{tokoGmfkYfWf1_Yl_Gz7i → j-BskPs0nxxPeYY-bSrab}/_buildManifest.js +0 -0
  407. /package/dist/web/standalone/.next/static/{tokoGmfkYfWf1_Yl_Gz7i → j-BskPs0nxxPeYY-bSrab}/_ssgManifest.js +0 -0
@@ -0,0 +1,89 @@
1
+ // GSD Extension — Advisory Sync Lock
2
+ // Prevents concurrent worktree syncs from colliding via a simple file lock.
3
+ // Stale locks (mtime > 60s) are auto-overridden. Lock acquisition waits up
4
+ // to 5 seconds then skips non-fatally.
5
+ import { existsSync, statSync, unlinkSync } from "node:fs";
6
+ import { join } from "node:path";
7
+ import { atomicWriteSync } from "./atomic-write.js";
8
+ const STALE_THRESHOLD_MS = 60_000; // 60 seconds
9
+ const DEFAULT_TIMEOUT_MS = 5_000; // 5 seconds
10
+ const SPIN_INTERVAL_MS = 100; // 100ms polling interval
11
+ // SharedArrayBuffer for synchronous sleep via Atomics.wait
12
+ const SLEEP_BUFFER = new SharedArrayBuffer(4);
13
+ const SLEEP_VIEW = new Int32Array(SLEEP_BUFFER);
14
+ function lockFilePath(basePath) {
15
+ return join(basePath, ".gsd", "sync.lock");
16
+ }
17
+ function sleepSync(ms) {
18
+ Atomics.wait(SLEEP_VIEW, 0, 0, ms);
19
+ }
20
+ /**
21
+ * Acquire an advisory sync lock for the given basePath.
22
+ * Returns { acquired: true } on success, { acquired: false } after timeout.
23
+ *
24
+ * - Creates lock file at {basePath}/.gsd/sync.lock with JSON { pid, acquired_at }
25
+ * - If lock exists and mtime > 60s (stale), overrides it
26
+ * - If lock exists and not stale, spins up to timeoutMs before giving up
27
+ */
28
+ export function acquireSyncLock(basePath, timeoutMs = DEFAULT_TIMEOUT_MS) {
29
+ const lp = lockFilePath(basePath);
30
+ const deadline = Date.now() + timeoutMs;
31
+ while (true) {
32
+ // Check if lock file exists
33
+ if (existsSync(lp)) {
34
+ // Check staleness
35
+ try {
36
+ const stat = statSync(lp);
37
+ const age = Date.now() - stat.mtimeMs;
38
+ if (age > STALE_THRESHOLD_MS) {
39
+ // Stale lock — override it
40
+ try {
41
+ unlinkSync(lp);
42
+ }
43
+ catch { /* race: already removed */ }
44
+ }
45
+ else {
46
+ // Lock is held and not stale — wait or give up
47
+ if (Date.now() >= deadline) {
48
+ return { acquired: false };
49
+ }
50
+ sleepSync(SPIN_INTERVAL_MS);
51
+ continue;
52
+ }
53
+ }
54
+ catch {
55
+ // stat failed (file removed between exists check and stat) — try to acquire
56
+ }
57
+ }
58
+ // Lock file does not exist (or was just removed) — try to write it
59
+ try {
60
+ const lockData = {
61
+ pid: process.pid,
62
+ acquired_at: new Date().toISOString(),
63
+ };
64
+ atomicWriteSync(lp, JSON.stringify(lockData, null, 2));
65
+ return { acquired: true };
66
+ }
67
+ catch {
68
+ // Write failed (race condition with another process) — retry or give up
69
+ if (Date.now() >= deadline) {
70
+ return { acquired: false };
71
+ }
72
+ sleepSync(SPIN_INTERVAL_MS);
73
+ }
74
+ }
75
+ }
76
+ /**
77
+ * Release the advisory sync lock. No-op if lock file does not exist.
78
+ */
79
+ export function releaseSyncLock(basePath) {
80
+ const lp = lockFilePath(basePath);
81
+ try {
82
+ if (existsSync(lp)) {
83
+ unlinkSync(lp);
84
+ }
85
+ }
86
+ catch {
87
+ // Non-fatal — lock may have been released by another process
88
+ }
89
+ }
@@ -7,10 +7,13 @@
7
7
  */
8
8
  import { join } from "node:path";
9
9
  import { mkdirSync } from "node:fs";
10
- import { transaction, getMilestoneSlices, _getAdapter, } from "../gsd-db.js";
10
+ import { transaction, getMilestone, getMilestoneSlices, getSliceTasks, _getAdapter, } from "../gsd-db.js";
11
11
  import { resolveMilestonePath, clearPathCache } from "../paths.js";
12
12
  import { saveFile, clearParseCache } from "../files.js";
13
13
  import { invalidateStateCache } from "../state.js";
14
+ import { renderAllProjections } from "../workflow-projections.js";
15
+ import { writeManifest } from "../workflow-manifest.js";
16
+ import { appendEvent } from "../workflow-events.js";
14
17
  function renderMilestoneSummaryMarkdown(params) {
15
18
  const now = new Date().toISOString();
16
19
  const keyDecisionsYaml = params.keyDecisions.length > 0
@@ -72,25 +75,56 @@ export async function handleCompleteMilestone(params, basePath) {
72
75
  if (!params.title || typeof params.title !== "string" || params.title.trim() === "") {
73
76
  return { error: "title is required and must be a non-empty string" };
74
77
  }
75
- // ── Verify all slices are complete ───────────────────────────────────────
76
- const slices = getMilestoneSlices(params.milestoneId);
77
- if (slices.length === 0) {
78
- return { error: `no slices found for milestone ${params.milestoneId}` };
78
+ // ── Verify that verification passed ─────────────────────────────────────
79
+ if (params.verificationPassed !== true) {
80
+ return { error: "verification did not pass — milestone completion blocked. verificationPassed must be explicitly set to true after all verification steps succeed" };
79
81
  }
80
- const incompleteSlices = slices.filter(s => s.status !== "complete" && s.status !== "done");
81
- if (incompleteSlices.length > 0) {
82
- const incompleteIds = incompleteSlices.map(s => `${s.id} (status: ${s.status})`).join(", ");
83
- return { error: `incomplete slices: ${incompleteIds}` };
84
- }
85
- // ── DB writes inside a transaction ──────────────────────────────────────
82
+ // ── Guards + DB writes inside a single transaction (prevents TOCTOU) ───
86
83
  const completedAt = new Date().toISOString();
84
+ let guardError = null;
87
85
  transaction(() => {
86
+ // State machine preconditions (inside txn for atomicity)
87
+ const milestone = getMilestone(params.milestoneId);
88
+ if (!milestone) {
89
+ guardError = `milestone not found: ${params.milestoneId}`;
90
+ return;
91
+ }
92
+ if (milestone.status === "complete" || milestone.status === "done") {
93
+ guardError = `milestone ${params.milestoneId} is already complete`;
94
+ return;
95
+ }
96
+ // Verify all slices are complete
97
+ const slices = getMilestoneSlices(params.milestoneId);
98
+ if (slices.length === 0) {
99
+ guardError = `no slices found for milestone ${params.milestoneId}`;
100
+ return;
101
+ }
102
+ const incompleteSlices = slices.filter(s => s.status !== "complete" && s.status !== "done");
103
+ if (incompleteSlices.length > 0) {
104
+ const incompleteIds = incompleteSlices.map(s => `${s.id} (status: ${s.status})`).join(", ");
105
+ guardError = `incomplete slices: ${incompleteIds}`;
106
+ return;
107
+ }
108
+ // Deep check: verify all tasks in all slices are complete
109
+ for (const slice of slices) {
110
+ const tasks = getSliceTasks(params.milestoneId, slice.id);
111
+ const incompleteTasks = tasks.filter(t => t.status !== "complete" && t.status !== "done");
112
+ if (incompleteTasks.length > 0) {
113
+ const ids = incompleteTasks.map(t => `${t.id} (status: ${t.status})`).join(", ");
114
+ guardError = `slice ${slice.id} has incomplete tasks: ${ids}`;
115
+ return;
116
+ }
117
+ }
118
+ // All guards passed — perform write
88
119
  const adapter = _getAdapter();
89
120
  adapter.prepare(`UPDATE milestones SET status = 'complete', completed_at = :completed_at WHERE id = :mid`).run({
90
121
  ":completed_at": completedAt,
91
122
  ":mid": params.milestoneId,
92
123
  });
93
124
  });
125
+ if (guardError) {
126
+ return { error: guardError };
127
+ }
94
128
  // ── Filesystem operations (outside transaction) ─────────────────────────
95
129
  const summaryMd = renderMilestoneSummaryMarkdown(params);
96
130
  let summaryPath;
@@ -121,6 +155,22 @@ export async function handleCompleteMilestone(params, basePath) {
121
155
  invalidateStateCache();
122
156
  clearPathCache();
123
157
  clearParseCache();
158
+ // ── Post-mutation hook: projections, manifest, event log ───────────────
159
+ try {
160
+ await renderAllProjections(basePath, params.milestoneId);
161
+ writeManifest(basePath);
162
+ appendEvent(basePath, {
163
+ cmd: "complete-milestone",
164
+ params: { milestoneId: params.milestoneId },
165
+ ts: new Date().toISOString(),
166
+ actor: "agent",
167
+ actor_name: params.actorName,
168
+ trigger_reason: params.triggerReason,
169
+ });
170
+ }
171
+ catch (hookErr) {
172
+ process.stderr.write(`gsd: complete-milestone post-mutation hook warning: ${hookErr.message}\n`);
173
+ }
124
174
  return {
125
175
  milestoneId: params.milestoneId,
126
176
  summaryPath,
@@ -8,11 +8,15 @@
8
8
  */
9
9
  import { join } from "node:path";
10
10
  import { mkdirSync } from "node:fs";
11
- import { transaction, insertMilestone, insertSlice, getSliceTasks, updateSliceStatus, _getAdapter, } from "../gsd-db.js";
11
+ import { transaction, insertMilestone, insertSlice, getSlice, getSliceTasks, getMilestone, updateSliceStatus, _getAdapter, } from "../gsd-db.js";
12
12
  import { resolveSlicePath, clearPathCache } from "../paths.js";
13
+ import { checkOwnership, sliceUnitKey } from "../unit-ownership.js";
13
14
  import { saveFile, clearParseCache } from "../files.js";
14
15
  import { invalidateStateCache } from "../state.js";
15
16
  import { renderRoadmapCheckboxes } from "../markdown-renderer.js";
17
+ import { renderAllProjections } from "../workflow-projections.js";
18
+ import { writeManifest } from "../workflow-manifest.js";
19
+ import { appendEvent } from "../workflow-events.js";
16
20
  /**
17
21
  * Render slice summary markdown matching the template format.
18
22
  * YAML frontmatter uses snake_case keys for parseSummary() compatibility.
@@ -162,23 +166,48 @@ export async function handleCompleteSlice(params, basePath) {
162
166
  if (!params.milestoneId || typeof params.milestoneId !== "string" || params.milestoneId.trim() === "") {
163
167
  return { error: "milestoneId is required and must be a non-empty string" };
164
168
  }
165
- // ── Verify all tasks are complete ───────────────────────────────────────
166
- const tasks = getSliceTasks(params.milestoneId, params.sliceId);
167
- if (tasks.length === 0) {
168
- return { error: `no tasks found for slice ${params.sliceId} in milestone ${params.milestoneId}` };
169
+ // ── Ownership check (opt-in: only enforced when claim file exists) ──────
170
+ const ownershipErr = checkOwnership(basePath, sliceUnitKey(params.milestoneId, params.sliceId), params.actorName);
171
+ if (ownershipErr) {
172
+ return { error: ownershipErr };
169
173
  }
170
- const incompleteTasks = tasks.filter(t => t.status !== "complete");
171
- if (incompleteTasks.length > 0) {
172
- const incompleteIds = incompleteTasks.map(t => `${t.id} (status: ${t.status})`).join(", ");
173
- return { error: `incomplete tasks: ${incompleteIds}` };
174
- }
175
- // ── DB writes inside a transaction ──────────────────────────────────────
174
+ // ── Guards + DB writes inside a single transaction (prevents TOCTOU) ───
176
175
  const completedAt = new Date().toISOString();
176
+ let guardError = null;
177
177
  transaction(() => {
178
+ // State machine preconditions (inside txn for atomicity).
179
+ // Milestone/slice not existing is OK — insertMilestone/insertSlice below will auto-create.
180
+ // Only block if they exist and are closed.
181
+ const milestone = getMilestone(params.milestoneId);
182
+ if (milestone && (milestone.status === "complete" || milestone.status === "done")) {
183
+ guardError = `cannot complete slice in a closed milestone: ${params.milestoneId} (status: ${milestone.status})`;
184
+ return;
185
+ }
186
+ const slice = getSlice(params.milestoneId, params.sliceId);
187
+ if (slice && (slice.status === "complete" || slice.status === "done")) {
188
+ guardError = `slice ${params.sliceId} is already complete — use gsd_slice_reopen first if you need to redo it`;
189
+ return;
190
+ }
191
+ // Verify all tasks are complete
192
+ const tasks = getSliceTasks(params.milestoneId, params.sliceId);
193
+ if (tasks.length === 0) {
194
+ guardError = `no tasks found for slice ${params.sliceId} in milestone ${params.milestoneId}`;
195
+ return;
196
+ }
197
+ const incompleteTasks = tasks.filter(t => t.status !== "complete" && t.status !== "done");
198
+ if (incompleteTasks.length > 0) {
199
+ const incompleteIds = incompleteTasks.map(t => `${t.id} (status: ${t.status})`).join(", ");
200
+ guardError = `incomplete tasks: ${incompleteIds}`;
201
+ return;
202
+ }
203
+ // All guards passed — perform writes
178
204
  insertMilestone({ id: params.milestoneId });
179
205
  insertSlice({ id: params.sliceId, milestoneId: params.milestoneId });
180
206
  updateSliceStatus(params.milestoneId, params.sliceId, "complete", completedAt);
181
207
  });
208
+ if (guardError) {
209
+ return { error: guardError };
210
+ }
182
211
  // ── Filesystem operations (outside transaction) ─────────────────────────
183
212
  // If disk render fails, roll back the DB status so deriveState() and
184
213
  // verifyExpectedArtifact() stay consistent (both say "not done").
@@ -235,6 +264,22 @@ export async function handleCompleteSlice(params, basePath) {
235
264
  invalidateStateCache();
236
265
  clearPathCache();
237
266
  clearParseCache();
267
+ // ── Post-mutation hook: projections, manifest, event log ───────────────
268
+ try {
269
+ await renderAllProjections(basePath, params.milestoneId);
270
+ writeManifest(basePath);
271
+ appendEvent(basePath, {
272
+ cmd: "complete-slice",
273
+ params: { milestoneId: params.milestoneId, sliceId: params.sliceId },
274
+ ts: new Date().toISOString(),
275
+ actor: "agent",
276
+ actor_name: params.actorName,
277
+ trigger_reason: params.triggerReason,
278
+ });
279
+ }
280
+ catch (hookErr) {
281
+ process.stderr.write(`gsd: complete-slice post-mutation hook warning: ${hookErr.message}\n`);
282
+ }
238
283
  return {
239
284
  sliceId: params.sliceId,
240
285
  milestoneId: params.milestoneId,
@@ -8,11 +8,15 @@
8
8
  */
9
9
  import { join } from "node:path";
10
10
  import { mkdirSync } from "node:fs";
11
- import { transaction, insertMilestone, insertSlice, insertTask, insertVerificationEvidence, _getAdapter, } from "../gsd-db.js";
11
+ import { transaction, insertMilestone, insertSlice, insertTask, insertVerificationEvidence, getMilestone, getSlice, getTask, _getAdapter, } from "../gsd-db.js";
12
12
  import { resolveSliceFile, resolveTasksDir, clearPathCache } from "../paths.js";
13
+ import { checkOwnership, taskUnitKey } from "../unit-ownership.js";
13
14
  import { saveFile, clearParseCache } from "../files.js";
14
15
  import { invalidateStateCache } from "../state.js";
15
16
  import { renderPlanCheckboxes } from "../markdown-renderer.js";
17
+ import { renderAllProjections } from "../workflow-projections.js";
18
+ import { writeManifest } from "../workflow-manifest.js";
19
+ import { appendEvent } from "../workflow-events.js";
16
20
  /**
17
21
  * Render task summary markdown matching the template format.
18
22
  * YAML frontmatter uses snake_case keys for parseSummary() compatibility.
@@ -105,9 +109,34 @@ export async function handleCompleteTask(params, basePath) {
105
109
  if (!params.milestoneId || typeof params.milestoneId !== "string" || params.milestoneId.trim() === "") {
106
110
  return { error: "milestoneId is required and must be a non-empty string" };
107
111
  }
108
- // ── DB writes inside a transaction ──────────────────────────────────────
112
+ // ── Ownership check (opt-in: only enforced when claim file exists) ──────
113
+ const ownershipErr = checkOwnership(basePath, taskUnitKey(params.milestoneId, params.sliceId, params.taskId), params.actorName);
114
+ if (ownershipErr) {
115
+ return { error: ownershipErr };
116
+ }
117
+ // ── Guards + DB writes inside a single transaction (prevents TOCTOU) ───
109
118
  const completedAt = new Date().toISOString();
119
+ let guardError = null;
110
120
  transaction(() => {
121
+ // State machine preconditions (inside txn for atomicity).
122
+ // Milestone/slice not existing is OK — insertMilestone/insertSlice below will auto-create.
123
+ // Only block if they exist and are closed.
124
+ const milestone = getMilestone(params.milestoneId);
125
+ if (milestone && (milestone.status === "complete" || milestone.status === "done")) {
126
+ guardError = `cannot complete task in a closed milestone: ${params.milestoneId} (status: ${milestone.status})`;
127
+ return;
128
+ }
129
+ const slice = getSlice(params.milestoneId, params.sliceId);
130
+ if (slice && (slice.status === "complete" || slice.status === "done")) {
131
+ guardError = `cannot complete task in a closed slice: ${params.sliceId} (status: ${slice.status})`;
132
+ return;
133
+ }
134
+ const existingTask = getTask(params.milestoneId, params.sliceId, params.taskId);
135
+ if (existingTask && (existingTask.status === "complete" || existingTask.status === "done")) {
136
+ guardError = `task ${params.taskId} is already complete — use gsd_task_reopen first if you need to redo it`;
137
+ return;
138
+ }
139
+ // All guards passed — perform writes
111
140
  insertMilestone({ id: params.milestoneId });
112
141
  insertSlice({ id: params.sliceId, milestoneId: params.milestoneId });
113
142
  insertTask({
@@ -138,6 +167,9 @@ export async function handleCompleteTask(params, basePath) {
138
167
  });
139
168
  }
140
169
  });
170
+ if (guardError) {
171
+ return { error: guardError };
172
+ }
141
173
  // ── Filesystem operations (outside transaction) ─────────────────────────
142
174
  // If disk render fails, roll back the DB status so deriveState() and
143
175
  // verifyExpectedArtifact() stay consistent (both say "not done").
@@ -195,6 +227,22 @@ export async function handleCompleteTask(params, basePath) {
195
227
  invalidateStateCache();
196
228
  clearPathCache();
197
229
  clearParseCache();
230
+ // ── Post-mutation hook: projections, manifest, event log ───────────────
231
+ try {
232
+ await renderAllProjections(basePath, params.milestoneId);
233
+ writeManifest(basePath);
234
+ appendEvent(basePath, {
235
+ cmd: "complete-task",
236
+ params: { milestoneId: params.milestoneId, sliceId: params.sliceId, taskId: params.taskId },
237
+ ts: new Date().toISOString(),
238
+ actor: "agent",
239
+ actor_name: params.actorName,
240
+ trigger_reason: params.triggerReason,
241
+ });
242
+ }
243
+ catch (hookErr) {
244
+ process.stderr.write(`gsd: complete-task post-mutation hook warning: ${hookErr.message}\n`);
245
+ }
198
246
  return {
199
247
  taskId: params.taskId,
200
248
  sliceId: params.sliceId,
@@ -1,7 +1,10 @@
1
1
  import { clearParseCache } from "../files.js";
2
- import { transaction, insertMilestone, insertSlice, upsertMilestonePlanning, upsertSlicePlanning, } from "../gsd-db.js";
2
+ import { transaction, getMilestone, insertMilestone, insertSlice, upsertMilestonePlanning, upsertSlicePlanning, } from "../gsd-db.js";
3
3
  import { invalidateStateCache } from "../state.js";
4
4
  import { renderRoadmapFromDb } from "../markdown-renderer.js";
5
+ import { renderAllProjections } from "../workflow-projections.js";
6
+ import { writeManifest } from "../workflow-manifest.js";
7
+ import { appendEvent } from "../workflow-events.js";
5
8
  function isNonEmptyString(value) {
6
9
  return typeof value === "string" && value.trim().length > 0;
7
10
  }
@@ -142,6 +145,23 @@ export async function handlePlanMilestone(rawParams, basePath) {
142
145
  catch (err) {
143
146
  return { error: `validation failed: ${err.message}` };
144
147
  }
148
+ // ── State machine preconditions ─────────────────────────────────────────
149
+ const existingMilestone = getMilestone(params.milestoneId);
150
+ if (existingMilestone && (existingMilestone.status === "complete" || existingMilestone.status === "done")) {
151
+ return { error: `cannot re-plan milestone ${params.milestoneId}: it is already complete` };
152
+ }
153
+ // Validate depends_on: all dependencies must exist and be complete
154
+ if (params.dependsOn && params.dependsOn.length > 0) {
155
+ for (const depId of params.dependsOn) {
156
+ const dep = getMilestone(depId);
157
+ if (!dep) {
158
+ return { error: `depends_on references unknown milestone: ${depId}` };
159
+ }
160
+ if (dep.status !== "complete" && dep.status !== "done") {
161
+ return { error: `depends_on milestone ${depId} is not yet complete (status: ${dep.status})` };
162
+ }
163
+ }
164
+ }
145
165
  try {
146
166
  transaction(() => {
147
167
  insertMilestone({
@@ -198,6 +218,22 @@ export async function handlePlanMilestone(rawParams, basePath) {
198
218
  }
199
219
  invalidateStateCache();
200
220
  clearParseCache();
221
+ // ── Post-mutation hook: projections, manifest, event log ───────────────
222
+ try {
223
+ await renderAllProjections(basePath, params.milestoneId);
224
+ writeManifest(basePath);
225
+ appendEvent(basePath, {
226
+ cmd: "plan-milestone",
227
+ params: { milestoneId: params.milestoneId },
228
+ ts: new Date().toISOString(),
229
+ actor: "agent",
230
+ actor_name: params.actorName,
231
+ trigger_reason: params.triggerReason,
232
+ });
233
+ }
234
+ catch (hookErr) {
235
+ process.stderr.write(`gsd: plan-milestone post-mutation hook warning: ${hookErr.message}\n`);
236
+ }
201
237
  return {
202
238
  milestoneId: params.milestoneId,
203
239
  roadmapPath,
@@ -1,7 +1,10 @@
1
1
  import { clearParseCache } from "../files.js";
2
- import { transaction, getSlice, insertTask, upsertSlicePlanning, upsertTaskPlanning, } from "../gsd-db.js";
2
+ import { transaction, getMilestone, getSlice, insertTask, upsertSlicePlanning, upsertTaskPlanning, } from "../gsd-db.js";
3
3
  import { invalidateStateCache } from "../state.js";
4
4
  import { renderPlanFromDb } from "../markdown-renderer.js";
5
+ import { renderAllProjections } from "../workflow-projections.js";
6
+ import { writeManifest } from "../workflow-manifest.js";
7
+ import { appendEvent } from "../workflow-events.js";
5
8
  function isNonEmptyString(value) {
6
9
  return typeof value === "string" && value.trim().length > 0;
7
10
  }
@@ -99,10 +102,20 @@ export async function handlePlanSlice(rawParams, basePath) {
99
102
  catch (err) {
100
103
  return { error: `validation failed: ${err.message}` };
101
104
  }
105
+ const parentMilestone = getMilestone(params.milestoneId);
106
+ if (!parentMilestone) {
107
+ return { error: `milestone not found: ${params.milestoneId}` };
108
+ }
109
+ if (parentMilestone.status === "complete" || parentMilestone.status === "done") {
110
+ return { error: `cannot plan slice in a closed milestone: ${params.milestoneId} (status: ${parentMilestone.status})` };
111
+ }
102
112
  const parentSlice = getSlice(params.milestoneId, params.sliceId);
103
113
  if (!parentSlice) {
104
114
  return { error: `missing parent slice: ${params.milestoneId}/${params.sliceId}` };
105
115
  }
116
+ if (parentSlice.status === "complete" || parentSlice.status === "done") {
117
+ return { error: `cannot re-plan slice ${params.sliceId}: it is already complete — use gsd_slice_reopen first` };
118
+ }
106
119
  try {
107
120
  transaction(() => {
108
121
  upsertSlicePlanning(params.milestoneId, params.sliceId, {
@@ -129,6 +142,7 @@ export async function handlePlanSlice(rawParams, basePath) {
129
142
  inputs: task.inputs,
130
143
  expectedOutput: task.expectedOutput,
131
144
  observabilityImpact: task.observabilityImpact ?? "",
145
+ fullPlanMd: task.fullPlanMd,
132
146
  });
133
147
  }
134
148
  });
@@ -140,6 +154,22 @@ export async function handlePlanSlice(rawParams, basePath) {
140
154
  const renderResult = await renderPlanFromDb(basePath, params.milestoneId, params.sliceId);
141
155
  invalidateStateCache();
142
156
  clearParseCache();
157
+ // ── Post-mutation hook: projections, manifest, event log ─────────────
158
+ try {
159
+ await renderAllProjections(basePath, params.milestoneId);
160
+ writeManifest(basePath);
161
+ appendEvent(basePath, {
162
+ cmd: "plan-slice",
163
+ params: { milestoneId: params.milestoneId, sliceId: params.sliceId },
164
+ ts: new Date().toISOString(),
165
+ actor: "agent",
166
+ actor_name: params.actorName,
167
+ trigger_reason: params.triggerReason,
168
+ });
169
+ }
170
+ catch (hookErr) {
171
+ process.stderr.write(`gsd: plan-slice post-mutation hook warning: ${hookErr.message}\n`);
172
+ }
143
173
  return {
144
174
  milestoneId: params.milestoneId,
145
175
  sliceId: params.sliceId,
@@ -2,6 +2,9 @@ import { clearParseCache } from "../files.js";
2
2
  import { transaction, getSlice, getTask, insertTask, upsertTaskPlanning } from "../gsd-db.js";
3
3
  import { invalidateStateCache } from "../state.js";
4
4
  import { renderTaskPlanFromDb } from "../markdown-renderer.js";
5
+ import { renderAllProjections } from "../workflow-projections.js";
6
+ import { writeManifest } from "../workflow-manifest.js";
7
+ import { appendEvent } from "../workflow-events.js";
5
8
  function isNonEmptyString(value) {
6
9
  return typeof value === "string" && value.trim().length > 0;
7
10
  }
@@ -51,9 +54,16 @@ export async function handlePlanTask(rawParams, basePath) {
51
54
  if (!parentSlice) {
52
55
  return { error: `missing parent slice: ${params.milestoneId}/${params.sliceId}` };
53
56
  }
57
+ if (parentSlice.status === "complete" || parentSlice.status === "done") {
58
+ return { error: `cannot plan task in a closed slice: ${params.sliceId} (status: ${parentSlice.status})` };
59
+ }
60
+ const existingTask = getTask(params.milestoneId, params.sliceId, params.taskId);
61
+ if (existingTask && (existingTask.status === "complete" || existingTask.status === "done")) {
62
+ return { error: `cannot re-plan task ${params.taskId}: it is already complete — use gsd_task_reopen first` };
63
+ }
54
64
  try {
55
65
  transaction(() => {
56
- if (!getTask(params.milestoneId, params.sliceId, params.taskId)) {
66
+ if (!existingTask) {
57
67
  insertTask({
58
68
  id: params.taskId,
59
69
  sliceId: params.sliceId,
@@ -71,6 +81,7 @@ export async function handlePlanTask(rawParams, basePath) {
71
81
  inputs: params.inputs,
72
82
  expectedOutput: params.expectedOutput,
73
83
  observabilityImpact: params.observabilityImpact ?? "",
84
+ fullPlanMd: params.fullPlanMd,
74
85
  });
75
86
  });
76
87
  }
@@ -81,6 +92,22 @@ export async function handlePlanTask(rawParams, basePath) {
81
92
  const renderResult = await renderTaskPlanFromDb(basePath, params.milestoneId, params.sliceId, params.taskId);
82
93
  invalidateStateCache();
83
94
  clearParseCache();
95
+ // ── Post-mutation hook: projections, manifest, event log ─────────────
96
+ try {
97
+ await renderAllProjections(basePath, params.milestoneId);
98
+ writeManifest(basePath);
99
+ appendEvent(basePath, {
100
+ cmd: "plan-task",
101
+ params: { milestoneId: params.milestoneId, sliceId: params.sliceId, taskId: params.taskId },
102
+ ts: new Date().toISOString(),
103
+ actor: "agent",
104
+ actor_name: params.actorName,
105
+ trigger_reason: params.triggerReason,
106
+ });
107
+ }
108
+ catch (hookErr) {
109
+ process.stderr.write(`gsd: plan-task post-mutation hook warning: ${hookErr.message}\n`);
110
+ }
84
111
  return {
85
112
  milestoneId: params.milestoneId,
86
113
  sliceId: params.sliceId,
@@ -1,7 +1,10 @@
1
1
  import { clearParseCache } from "../files.js";
2
- import { transaction, getMilestone, getMilestoneSlices, insertSlice, updateSliceFields, insertAssessment, deleteSlice, } from "../gsd-db.js";
2
+ import { transaction, getMilestone, getMilestoneSlices, getSlice, insertSlice, updateSliceFields, insertAssessment, deleteSlice, } from "../gsd-db.js";
3
3
  import { invalidateStateCache } from "../state.js";
4
4
  import { renderRoadmapFromDb, renderAssessmentFromDb } from "../markdown-renderer.js";
5
+ import { renderAllProjections } from "../workflow-projections.js";
6
+ import { writeManifest } from "../workflow-manifest.js";
7
+ import { appendEvent } from "../workflow-events.js";
5
8
  import { join } from "node:path";
6
9
  function isNonEmptyString(value) {
7
10
  return typeof value === "string" && value.trim().length > 0;
@@ -58,11 +61,22 @@ export async function handleReassessRoadmap(rawParams, basePath) {
58
61
  catch (err) {
59
62
  return { error: `validation failed: ${err.message}` };
60
63
  }
61
- // ── Verify milestone exists ───────────────────────────────────────
64
+ // ── Verify milestone exists and is active ────────────────────────
62
65
  const milestone = getMilestone(params.milestoneId);
63
66
  if (!milestone) {
64
67
  return { error: `milestone not found: ${params.milestoneId}` };
65
68
  }
69
+ if (milestone.status === "complete" || milestone.status === "done") {
70
+ return { error: `cannot reassess a closed milestone: ${params.milestoneId} (status: ${milestone.status})` };
71
+ }
72
+ // ── Verify completedSliceId is actually complete ──────────────────
73
+ const completedSlice = getSlice(params.milestoneId, params.completedSliceId);
74
+ if (!completedSlice) {
75
+ return { error: `completedSliceId not found: ${params.milestoneId}/${params.completedSliceId}` };
76
+ }
77
+ if (completedSlice.status !== "complete" && completedSlice.status !== "done") {
78
+ return { error: `completedSliceId ${params.completedSliceId} is not complete (status: ${completedSlice.status}) — reassess can only be called after a slice finishes` };
79
+ }
66
80
  // ── Structural enforcement ────────────────────────────────────────
67
81
  const existingSlices = getMilestoneSlices(params.milestoneId);
68
82
  const completedSliceIds = new Set();
@@ -139,6 +153,22 @@ export async function handleReassessRoadmap(rawParams, basePath) {
139
153
  // ── Invalidate caches ─────────────────────────────────────────
140
154
  invalidateStateCache();
141
155
  clearParseCache();
156
+ // ── Post-mutation hook: projections, manifest, event log ─────
157
+ try {
158
+ await renderAllProjections(basePath, params.milestoneId);
159
+ writeManifest(basePath);
160
+ appendEvent(basePath, {
161
+ cmd: "reassess-roadmap",
162
+ params: { milestoneId: params.milestoneId, completedSliceId: params.completedSliceId },
163
+ ts: new Date().toISOString(),
164
+ actor: "agent",
165
+ actor_name: params.actorName,
166
+ trigger_reason: params.triggerReason,
167
+ });
168
+ }
169
+ catch (hookErr) {
170
+ process.stderr.write(`gsd: reassess-roadmap post-mutation hook warning: ${hookErr.message}\n`);
171
+ }
142
172
  return {
143
173
  milestoneId: params.milestoneId,
144
174
  completedSliceId: params.completedSliceId,