@opengsd/gsd-pi 1.1.1-dev.3ea310e → 1.1.1-dev.595401e

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 (314) hide show
  1. package/dist/resources/.managed-resources-content-hash +1 -1
  2. package/dist/resources/extensions/gsd/auto/orchestrator.js +0 -1
  3. package/dist/resources/extensions/gsd/auto/phases.js +4 -3
  4. package/dist/resources/extensions/gsd/auto-dashboard.js +92 -17
  5. package/dist/resources/extensions/gsd/auto-dispatch.js +5 -0
  6. package/dist/resources/extensions/gsd/auto-post-unit.js +132 -8
  7. package/dist/resources/extensions/gsd/auto-prompts.js +68 -22
  8. package/dist/resources/extensions/gsd/auto-start.js +41 -12
  9. package/dist/resources/extensions/gsd/auto-unit-tool-scope.js +2 -1
  10. package/dist/resources/extensions/gsd/auto.js +12 -5
  11. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +82 -3
  12. package/dist/resources/extensions/gsd/bootstrap/exec-tools.js +43 -0
  13. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +30 -9
  14. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +16 -10
  15. package/dist/resources/extensions/gsd/browser-evidence.js +29 -2
  16. package/dist/resources/extensions/gsd/commands/handlers/core.js +1 -1
  17. package/dist/resources/extensions/gsd/commands-prefs-wizard.js +3 -1
  18. package/dist/resources/extensions/gsd/commands-verdict.js +1 -1
  19. package/dist/resources/extensions/gsd/config-overlay.js +2 -1
  20. package/dist/resources/extensions/gsd/dashboard-overlay.js +21 -7
  21. package/dist/resources/extensions/gsd/docs/preferences-reference.md +8 -0
  22. package/dist/resources/extensions/gsd/doctor-runtime-checks.js +2 -2
  23. package/dist/resources/extensions/gsd/error-classifier.js +2 -1
  24. package/dist/resources/extensions/gsd/escalation.js +4 -4
  25. package/dist/resources/extensions/gsd/exec-sandbox.js +2 -0
  26. package/dist/resources/extensions/gsd/forensics.js +74 -2
  27. package/dist/resources/extensions/gsd/gsd-db.js +5 -2
  28. package/dist/resources/extensions/gsd/guided-flow.js +29 -68
  29. package/dist/resources/extensions/gsd/memory-store.js +4 -1
  30. package/dist/resources/extensions/gsd/post-unit-hooks.js +9 -0
  31. package/dist/resources/extensions/gsd/preferences-validation.js +39 -0
  32. package/dist/resources/extensions/gsd/prompt-loader.js +7 -0
  33. package/dist/resources/extensions/gsd/prompts/forensics.md +61 -1
  34. package/dist/resources/extensions/gsd/prompts/gate-evaluate.md +3 -1
  35. package/dist/resources/extensions/gsd/prompts/parallel-research-slices.md +3 -1
  36. package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  37. package/dist/resources/extensions/gsd/prompts/reactive-execute.md +3 -1
  38. package/dist/resources/extensions/gsd/prompts/run-uat.md +48 -24
  39. package/dist/resources/extensions/gsd/prompts/system.md +3 -1
  40. package/dist/resources/extensions/gsd/prompts/validate-milestone.md +3 -3
  41. package/dist/resources/extensions/gsd/rule-registry.js +428 -52
  42. package/dist/resources/extensions/gsd/safety/destructive-guard.js +3 -0
  43. package/dist/resources/extensions/gsd/skill-activation.js +20 -3
  44. package/dist/resources/extensions/gsd/state-reconciliation/drift/roadmap.js +18 -1
  45. package/dist/resources/extensions/gsd/state-reconciliation/index.js +6 -0
  46. package/dist/resources/extensions/gsd/state.js +3 -3
  47. package/dist/resources/extensions/gsd/templates/plan.md +3 -1
  48. package/dist/resources/extensions/gsd/tools/complete-task.js +11 -1
  49. package/dist/resources/extensions/gsd/tools/exec-tool.js +109 -0
  50. package/dist/resources/extensions/gsd/tools/validate-milestone.js +46 -16
  51. package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +403 -3
  52. package/dist/resources/extensions/gsd/unit-context-manifest.js +8 -3
  53. package/dist/resources/extensions/gsd/validation-block-guard.js +2 -0
  54. package/dist/resources/extensions/gsd/verdict-parser.js +59 -15
  55. package/dist/resources/extensions/gsd/verification-gate.js +72 -1
  56. package/dist/resources/extensions/gsd/workflow-mcp-auto-prep.js +1 -1
  57. package/dist/resources/extensions/gsd/workflow-mcp.js +5 -1
  58. package/dist/rtk.d.ts +7 -1
  59. package/dist/rtk.js +27 -11
  60. package/dist/web/standalone/.next/BUILD_ID +1 -1
  61. package/dist/web/standalone/.next/app-path-routes-manifest.json +7 -7
  62. package/dist/web/standalone/.next/build-manifest.json +2 -2
  63. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  64. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  65. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  66. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  67. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  68. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  69. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  70. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  71. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  72. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  73. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  74. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  75. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  76. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  77. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  78. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  79. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  80. package/dist/web/standalone/.next/server/app/index.html +1 -1
  81. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  82. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  83. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  84. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  85. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  86. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  87. package/dist/web/standalone/.next/server/app-paths-manifest.json +7 -7
  88. package/dist/web/standalone/.next/server/chunks/8357.js +1 -1
  89. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  90. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  91. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  92. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  93. package/package.json +3 -2
  94. package/packages/cloud-mcp-gateway/package.json +2 -2
  95. package/packages/contracts/dist/workflow.d.ts +14 -0
  96. package/packages/contracts/dist/workflow.d.ts.map +1 -1
  97. package/packages/contracts/dist/workflow.js +16 -0
  98. package/packages/contracts/dist/workflow.js.map +1 -1
  99. package/packages/contracts/package.json +1 -1
  100. package/packages/daemon/package.json +4 -4
  101. package/packages/gsd-agent-core/dist/agent-session.d.ts +9 -0
  102. package/packages/gsd-agent-core/dist/agent-session.d.ts.map +1 -1
  103. package/packages/gsd-agent-core/dist/agent-session.js +32 -0
  104. package/packages/gsd-agent-core/dist/agent-session.js.map +1 -1
  105. package/packages/gsd-agent-core/dist/index.d.ts +1 -0
  106. package/packages/gsd-agent-core/dist/index.d.ts.map +1 -1
  107. package/packages/gsd-agent-core/dist/index.js +1 -0
  108. package/packages/gsd-agent-core/dist/index.js.map +1 -1
  109. package/packages/gsd-agent-core/dist/session/agent-session-compaction.d.ts +2 -0
  110. package/packages/gsd-agent-core/dist/session/agent-session-compaction.d.ts.map +1 -1
  111. package/packages/gsd-agent-core/dist/session/agent-session-compaction.js +8 -2
  112. package/packages/gsd-agent-core/dist/session/agent-session-compaction.js.map +1 -1
  113. package/packages/gsd-agent-core/dist/session/agent-session-host.d.ts +7 -0
  114. package/packages/gsd-agent-core/dist/session/agent-session-host.d.ts.map +1 -1
  115. package/packages/gsd-agent-core/dist/session/agent-session-host.js.map +1 -1
  116. package/packages/gsd-agent-core/dist/session/agent-session-prompt.d.ts.map +1 -1
  117. package/packages/gsd-agent-core/dist/session/agent-session-prompt.js +69 -1
  118. package/packages/gsd-agent-core/dist/session/agent-session-prompt.js.map +1 -1
  119. package/packages/gsd-agent-core/dist/turn-latency.d.ts +47 -0
  120. package/packages/gsd-agent-core/dist/turn-latency.d.ts.map +1 -0
  121. package/packages/gsd-agent-core/dist/turn-latency.js +123 -0
  122. package/packages/gsd-agent-core/dist/turn-latency.js.map +1 -0
  123. package/packages/gsd-agent-core/package.json +6 -6
  124. package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/gsd-widget-prototype.d.ts +21 -0
  125. package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/gsd-widget-prototype.d.ts.map +1 -0
  126. package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/gsd-widget-prototype.js +213 -0
  127. package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/gsd-widget-prototype.js.map +1 -0
  128. package/packages/gsd-agent-modes/dist/modes/interactive/components/settings-selector.d.ts +2 -0
  129. package/packages/gsd-agent-modes/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  130. package/packages/gsd-agent-modes/dist/modes/interactive/components/settings-selector.js +10 -0
  131. package/packages/gsd-agent-modes/dist/modes/interactive/components/settings-selector.js.map +1 -1
  132. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.d.ts +1 -0
  133. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  134. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js +89 -31
  135. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  136. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/input-controller.d.ts.map +1 -1
  137. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/input-controller.js +7 -1
  138. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/input-controller.js.map +1 -1
  139. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-command-handlers.d.ts.map +1 -1
  140. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-command-handlers.js +6 -0
  141. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-command-handlers.js.map +1 -1
  142. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-class-constants.d.ts +1 -1
  143. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-class-constants.d.ts.map +1 -1
  144. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-class-constants.js +1 -1
  145. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-class-constants.js.map +1 -1
  146. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  147. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.js +1 -0
  148. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.js.map +1 -1
  149. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-settings.d.ts.map +1 -1
  150. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-settings.js +5 -0
  151. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-settings.js.map +1 -1
  152. package/packages/gsd-agent-modes/package.json +7 -7
  153. package/packages/mcp-server/dist/remote-questions.d.ts.map +1 -1
  154. package/packages/mcp-server/dist/remote-questions.js +23 -9
  155. package/packages/mcp-server/dist/remote-questions.js.map +1 -1
  156. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  157. package/packages/mcp-server/dist/workflow-tools.js +84 -2
  158. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  159. package/packages/mcp-server/package.json +3 -3
  160. package/packages/native/package.json +1 -1
  161. package/packages/pi-agent-core/dist/agent-loop.js +38 -0
  162. package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
  163. package/packages/pi-agent-core/dist/agent.d.ts +5 -1
  164. package/packages/pi-agent-core/dist/agent.d.ts.map +1 -1
  165. package/packages/pi-agent-core/dist/agent.js +2 -0
  166. package/packages/pi-agent-core/dist/agent.js.map +1 -1
  167. package/packages/pi-agent-core/dist/types.d.ts +3 -0
  168. package/packages/pi-agent-core/dist/types.d.ts.map +1 -1
  169. package/packages/pi-agent-core/dist/types.js.map +1 -1
  170. package/packages/pi-agent-core/package.json +1 -1
  171. package/packages/pi-ai/dist/api-registry.d.ts +2 -0
  172. package/packages/pi-ai/dist/api-registry.d.ts.map +1 -1
  173. package/packages/pi-ai/dist/api-registry.js +23 -0
  174. package/packages/pi-ai/dist/api-registry.js.map +1 -1
  175. package/packages/pi-ai/dist/image-models.generated.d.ts +15 -0
  176. package/packages/pi-ai/dist/image-models.generated.d.ts.map +1 -1
  177. package/packages/pi-ai/dist/image-models.generated.js +15 -0
  178. package/packages/pi-ai/dist/image-models.generated.js.map +1 -1
  179. package/packages/pi-ai/dist/models.generated.d.ts +86 -18
  180. package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
  181. package/packages/pi-ai/dist/models.generated.js +108 -40
  182. package/packages/pi-ai/dist/models.generated.js.map +1 -1
  183. package/packages/pi-ai/dist/stream.js +6 -6
  184. package/packages/pi-ai/dist/stream.js.map +1 -1
  185. package/packages/pi-ai/package.json +1 -1
  186. package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
  187. package/packages/pi-coding-agent/dist/core/model-registry.js +2 -2
  188. package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
  189. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +3 -0
  190. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
  191. package/packages/pi-coding-agent/dist/core/settings-manager.js +11 -0
  192. package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
  193. package/packages/pi-coding-agent/package.json +7 -7
  194. package/packages/pi-tui/dist/terminal.d.ts +1 -0
  195. package/packages/pi-tui/dist/terminal.d.ts.map +1 -1
  196. package/packages/pi-tui/dist/terminal.js +8 -4
  197. package/packages/pi-tui/dist/terminal.js.map +1 -1
  198. package/packages/pi-tui/package.json +1 -1
  199. package/packages/rpc-client/package.json +2 -2
  200. package/pkg/package.json +1 -1
  201. package/src/resources/extensions/gsd/auto/orchestrator.ts +0 -1
  202. package/src/resources/extensions/gsd/auto/phases.ts +5 -3
  203. package/src/resources/extensions/gsd/auto-dashboard.ts +98 -18
  204. package/src/resources/extensions/gsd/auto-dispatch.ts +5 -0
  205. package/src/resources/extensions/gsd/auto-post-unit.ts +164 -7
  206. package/src/resources/extensions/gsd/auto-prompts.ts +102 -15
  207. package/src/resources/extensions/gsd/auto-start.ts +54 -14
  208. package/src/resources/extensions/gsd/auto-unit-tool-scope.ts +2 -1
  209. package/src/resources/extensions/gsd/auto.ts +15 -4
  210. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +89 -3
  211. package/src/resources/extensions/gsd/bootstrap/exec-tools.ts +51 -0
  212. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +51 -14
  213. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +21 -10
  214. package/src/resources/extensions/gsd/browser-evidence.ts +26 -2
  215. package/src/resources/extensions/gsd/commands/handlers/core.ts +1 -1
  216. package/src/resources/extensions/gsd/commands-prefs-wizard.ts +4 -1
  217. package/src/resources/extensions/gsd/commands-verdict.ts +1 -1
  218. package/src/resources/extensions/gsd/config-overlay.ts +3 -1
  219. package/src/resources/extensions/gsd/dashboard-overlay.ts +28 -7
  220. package/src/resources/extensions/gsd/docs/preferences-reference.md +8 -0
  221. package/src/resources/extensions/gsd/doctor-runtime-checks.ts +2 -2
  222. package/src/resources/extensions/gsd/error-classifier.ts +2 -1
  223. package/src/resources/extensions/gsd/escalation.ts +4 -4
  224. package/src/resources/extensions/gsd/exec-sandbox.ts +4 -0
  225. package/src/resources/extensions/gsd/forensics.ts +99 -5
  226. package/src/resources/extensions/gsd/gsd-db.ts +5 -2
  227. package/src/resources/extensions/gsd/guided-flow.ts +90 -82
  228. package/src/resources/extensions/gsd/memory-store.ts +4 -1
  229. package/src/resources/extensions/gsd/post-unit-hooks.ts +14 -1
  230. package/src/resources/extensions/gsd/preferences-types.ts +1 -1
  231. package/src/resources/extensions/gsd/preferences-validation.ts +36 -0
  232. package/src/resources/extensions/gsd/prompt-loader.ts +8 -0
  233. package/src/resources/extensions/gsd/prompts/forensics.md +61 -1
  234. package/src/resources/extensions/gsd/prompts/gate-evaluate.md +3 -1
  235. package/src/resources/extensions/gsd/prompts/parallel-research-slices.md +3 -1
  236. package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  237. package/src/resources/extensions/gsd/prompts/reactive-execute.md +3 -1
  238. package/src/resources/extensions/gsd/prompts/run-uat.md +48 -24
  239. package/src/resources/extensions/gsd/prompts/system.md +3 -1
  240. package/src/resources/extensions/gsd/prompts/validate-milestone.md +3 -3
  241. package/src/resources/extensions/gsd/rule-registry.ts +558 -58
  242. package/src/resources/extensions/gsd/rule-types.ts +2 -0
  243. package/src/resources/extensions/gsd/safety/destructive-guard.ts +3 -0
  244. package/src/resources/extensions/gsd/skill-activation.ts +20 -2
  245. package/src/resources/extensions/gsd/state-reconciliation/drift/roadmap.ts +20 -0
  246. package/src/resources/extensions/gsd/state-reconciliation/index.ts +6 -0
  247. package/src/resources/extensions/gsd/state-reconciliation/types.ts +1 -0
  248. package/src/resources/extensions/gsd/state.ts +3 -3
  249. package/src/resources/extensions/gsd/templates/plan.md +3 -1
  250. package/src/resources/extensions/gsd/tests/auto-dashboard.test.ts +156 -4
  251. package/src/resources/extensions/gsd/tests/auto-orchestrator.test.ts +37 -0
  252. package/src/resources/extensions/gsd/tests/auto-start-orphan-bootstrap.test.ts +16 -3
  253. package/src/resources/extensions/gsd/tests/browser-evidence.test.ts +142 -0
  254. package/src/resources/extensions/gsd/tests/commands-dispatcher-validation-block.test.ts +38 -3
  255. package/src/resources/extensions/gsd/tests/commands-verdict.test.ts +6 -2
  256. package/src/resources/extensions/gsd/tests/complete-milestone-excerpt.test.ts +30 -0
  257. package/src/resources/extensions/gsd/tests/dashboard-overlay.test.ts +45 -0
  258. package/src/resources/extensions/gsd/tests/deep-planning-mode-dispatch.test.ts +53 -0
  259. package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +8 -0
  260. package/src/resources/extensions/gsd/tests/derive-state-helpers.test.ts +8 -0
  261. package/src/resources/extensions/gsd/tests/discuss-milestone-structured-questions.test.ts +31 -0
  262. package/src/resources/extensions/gsd/tests/doctor-runtime-checks.test.ts +27 -0
  263. package/src/resources/extensions/gsd/tests/escalation.test.ts +16 -27
  264. package/src/resources/extensions/gsd/tests/exec-sandbox.test.ts +18 -0
  265. package/src/resources/extensions/gsd/tests/exec-tool.test.ts +69 -0
  266. package/src/resources/extensions/gsd/tests/forensics-issue-routing.test.ts +20 -0
  267. package/src/resources/extensions/gsd/tests/forensics-prompt-rendering.test.ts +3 -0
  268. package/src/resources/extensions/gsd/tests/forensics-tool-scope.test.ts +69 -0
  269. package/src/resources/extensions/gsd/tests/guided-discuss-milestone-prompt-rendering.test.ts +40 -1
  270. package/src/resources/extensions/gsd/tests/guided-dispatch-root.test.ts +86 -0
  271. package/src/resources/extensions/gsd/tests/guided-flow.test.ts +12 -9
  272. package/src/resources/extensions/gsd/tests/integration/auto-recovery.test.ts +4 -4
  273. package/src/resources/extensions/gsd/tests/integration/run-uat.test.ts +66 -10
  274. package/src/resources/extensions/gsd/tests/memory-maintenance.test.ts +39 -8
  275. package/src/resources/extensions/gsd/tests/new-milestone-discuss-routing.test.ts +3 -3
  276. package/src/resources/extensions/gsd/tests/parallel-skill-prompt-integration.test.ts +54 -7
  277. package/src/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +9 -0
  278. package/src/resources/extensions/gsd/tests/post-unit-hooks.test.ts +157 -0
  279. package/src/resources/extensions/gsd/tests/post-unit-retry-on-orchestrator-bridge.test.ts +179 -0
  280. package/src/resources/extensions/gsd/tests/preferences.test.ts +29 -0
  281. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +53 -1
  282. package/src/resources/extensions/gsd/tests/prompt-loader-extension-dir.test.ts +14 -0
  283. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +18 -1
  284. package/src/resources/extensions/gsd/tests/queued-discuss-fast-path.test.ts +7 -8
  285. package/src/resources/extensions/gsd/tests/reactive-executor.test.ts +36 -0
  286. package/src/resources/extensions/gsd/tests/register-hooks-depth-verification.test.ts +35 -0
  287. package/src/resources/extensions/gsd/tests/restore-tools-after-discuss.test.ts +1 -1
  288. package/src/resources/extensions/gsd/tests/rule-registry.test.ts +75 -0
  289. package/src/resources/extensions/gsd/tests/skill-activation.test.ts +55 -0
  290. package/src/resources/extensions/gsd/tests/state-reconciliation-drift.test.ts +191 -0
  291. package/src/resources/extensions/gsd/tests/token-tool-gating.test.ts +84 -10
  292. package/src/resources/extensions/gsd/tests/tool-naming.test.ts +12 -2
  293. package/src/resources/extensions/gsd/tests/tui-header-lifecycle.test.ts +29 -6
  294. package/src/resources/extensions/gsd/tests/unit-context-manifest.test.ts +29 -6
  295. package/src/resources/extensions/gsd/tests/validate-milestone-prompt-verification-classes.test.ts +6 -3
  296. package/src/resources/extensions/gsd/tests/validate-milestone-write-order.test.ts +133 -0
  297. package/src/resources/extensions/gsd/tests/validation-block-guard.test.ts +21 -0
  298. package/src/resources/extensions/gsd/tests/verification-gate.test.ts +51 -0
  299. package/src/resources/extensions/gsd/tests/workflow-mcp-auto-prep.test.ts +2 -2
  300. package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +213 -0
  301. package/src/resources/extensions/gsd/tests/write-gate-planning-unit.test.ts +25 -0
  302. package/src/resources/extensions/gsd/tools/complete-task.ts +20 -2
  303. package/src/resources/extensions/gsd/tools/exec-tool.ts +130 -0
  304. package/src/resources/extensions/gsd/tools/validate-milestone.ts +46 -15
  305. package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +489 -3
  306. package/src/resources/extensions/gsd/types.ts +67 -4
  307. package/src/resources/extensions/gsd/unit-context-manifest.ts +14 -5
  308. package/src/resources/extensions/gsd/validation-block-guard.ts +2 -0
  309. package/src/resources/extensions/gsd/verdict-parser.ts +54 -13
  310. package/src/resources/extensions/gsd/verification-gate.ts +87 -1
  311. package/src/resources/extensions/gsd/workflow-mcp-auto-prep.ts +1 -1
  312. package/src/resources/extensions/gsd/workflow-mcp.ts +5 -1
  313. /package/dist/web/standalone/.next/static/{xACmObbrDjwLriepRgaa9 → IDKjyRHLIaumjgonPcYiX}/_buildManifest.js +0 -0
  314. /package/dist/web/standalone/.next/static/{xACmObbrDjwLriepRgaa9 → IDKjyRHLIaumjgonPcYiX}/_ssgManifest.js +0 -0
@@ -277,7 +277,6 @@ export class AutoOrchestrator implements AutoOrchestrationModule {
277
277
  this.status.activeUnit = { unitType: decision.unitType, unitId: decision.unitId };
278
278
  this.status.phase = "running";
279
279
  this.lastAdvanceKey = nextKey;
280
- this.lastFinalizedUnitKey = null;
281
280
  this.bumpTransition();
282
281
 
283
282
  await this.deps.runtime.journalTransition({
@@ -88,7 +88,7 @@ import { resolveManifest } from "../unit-context-manifest.js";
88
88
  import { createWorktreeSafetyModule, type WorktreeSafetyResult } from "../worktree-safety.js";
89
89
  import { isSuspiciousGhostCompletion } from "../auto-unit-closeout.js";
90
90
  import { decideVerificationRetry, verificationRetryKey } from "./verification-retry-policy.js";
91
- import { buildPhaseHandoffOutcome, setAutoOutcomeWidget } from "../auto-dashboard.js";
91
+ import { buildPhaseHandoffOutcome, setAutoActiveStatus, setAutoOutcomeWidget } from "../auto-dashboard.js";
92
92
  import { getConsecutiveDispatchBlocker } from "../dispatch-guard.js";
93
93
  import {
94
94
  captureRootDirtySnapshot,
@@ -100,7 +100,7 @@ import { classifyError, isTransient } from "../error-classifier.js";
100
100
  export const STUCK_WINDOW_SIZE = 6;
101
101
  const STUCK_RECOVERY_ATTEMPTS_KEY = "stuck_recovery_attempts";
102
102
  const ZERO_TOOL_PROVIDER_ERROR_PREFIX_RE =
103
- /^(?:api error(?::|$|\s*\()|provider error(?::|$|\s*\()|request failed\b|(?:http\s*)?(?:429|500|502|503)\b|\b(?:econnreset|etimedout|econnrefused|epipe)\b|socket hang up\b|fetch failed\b|(?:network|connection|server) error(?::|$)|connection (?:reset|refused)(?::|$|\s+by\b)|dns\b.*(?:fail|error|timeout)|unexpected eof\b|stream idle timeout\b|partial response received\b|stream_exhausted\b|terminated(?::|$)|(?:connection|stream|request)\b.{0,40}\bterminated\b|other side closed\b|rate.?limit(?:ed| exceeded| reached| error)|too many requests\b|you(?:'ve| have) hit your limit\b|usage limit\b|out of extra usage\b|service.?unavailable\b|internal(?: server)? error(?::|$)|internal(?:[_-]server)?[_-]error\b|server[_-]error\b|(?:provider|server|api|model|codex|claude|openai|anthropic|gemini)\b.{0,80}\boverloaded\b|overloaded\b.{0,80}\b(?:provider|server|api|model)\b|context (?:window|length) exceed|context window exceed)/i;
103
+ /^(?:api error(?::|$|\s*\()|provider error(?::|$|\s*\()|request failed\b|(?:http\s*)?(?:429|500|502|503)\b|\b(?:econnreset|etimedout|econnrefused|epipe)\b|socket hang up\b|fetch failed\b|(?:network|connection|server) error(?::|$)|connection (?:reset|refused)(?::|$|\s+by\b)|dns\b.*(?:fail|error|timeout)|unexpected eof\b|stream idle timeout\b|partial response received\b|stream_exhausted\b|terminated(?::|$)|(?:connection|stream|request)\b.{0,40}\bterminated\b|other side closed\b|rate.?limit(?:ed| exceeded| reached| error)|too many requests\b|you(?:'ve| have) (?:hit|reached) your (?:\w+ )?limit\b|.*\b(?:usage|session|weekly|daily|monthly|quota) limit\b|limit\b.{0,40}\bresets?\b|out of extra usage\b|service.?unavailable\b|internal(?: server)? error(?::|$)|internal(?:[_-]server)?[_-]error\b|server[_-]error\b|(?:provider|server|api|model|codex|claude|openai|anthropic|gemini)\b.{0,80}\boverloaded\b|overloaded\b.{0,80}\b(?:provider|server|api|model)\b|context (?:window|length) exceed|context window exceed)/i;
104
104
  const ZERO_TOOL_PROVIDER_ERROR_SIGNAL_RE =
105
105
  /(?:\b(?:http|status(?: code)?|code|error:)\s*(?:429|500|502|503)\b|\b(?:api|provider) error\s*[:(]?\s*(?:429|500|502|503)\b|\b(?:typeerror|error):\s*(?:fetch failed\b|socket hang up\b|terminated(?::|$)|connection (?:reset|refused)(?::|$|\s+by\b)|(?:network|connection|server) error(?::|$)|stream idle timeout\b|partial response received\b|unexpected eof\b)|\b(?:server_error|api_error|stream_exhausted(?:_without_result)?)\b|\b(?:econnreset|etimedout|econnrefused|epipe)\b|context (?:window|length) exceed|context window exceed)/i;
106
106
 
@@ -114,6 +114,8 @@ function classifyZeroToolProviderMessage(message: string): ReturnType<typeof cla
114
114
  return classifyError(firstLine);
115
115
  }
116
116
 
117
+ export const _classifyZeroToolProviderMessageForTest = classifyZeroToolProviderMessage;
118
+
117
119
  export function resolveDispatchRecoveryAttempts(
118
120
  unitRecoveryCount: Map<string, number>,
119
121
  unitType: string,
@@ -2092,7 +2094,7 @@ export async function runUnitPhase(
2092
2094
  const nextDispatchCount = (s.unitDispatchCount.get(dispatchKey) ?? 0) + 1;
2093
2095
 
2094
2096
  // Status bar (widget + preconditions deferred until after model selection — see #2899)
2095
- ctx.ui.setStatus("gsd-auto", s.stepMode ? "next" : "auto");
2097
+ setAutoActiveStatus(ctx, s.stepMode ? "next" : "auto");
2096
2098
  if (mid)
2097
2099
  deps.updateSliceProgressCache(s.basePath, mid, state.activeSlice?.id);
2098
2100
 
@@ -13,10 +13,11 @@ import type {
13
13
  ExtensionCommandContext,
14
14
  ReadonlyFooterDataProvider,
15
15
  Theme,
16
+ ThemeColor,
16
17
  } from "@gsd/pi-coding-agent";
17
18
  import type { GSDState } from "./types.js";
18
19
  import { getActiveHook } from "./post-unit-hooks.js";
19
- import { getLedger } from "./metrics.js";
20
+ import { getLedger, getProjectTotals } from "./metrics.js";
20
21
  import { getErrorMessage } from "./error-utils.js";
21
22
  import { nativeIsRepo } from "./native-git-bridge.js";
22
23
  import {
@@ -304,6 +305,40 @@ export function shouldRenderRoadmapProgress(
304
305
  return !!progress && progress.total > 0;
305
306
  }
306
307
 
308
+ function widgetGridLabel(theme: Theme, text: string, color: ThemeColor = "borderAccent"): string {
309
+ return theme.fg(color, theme.bold(text.toUpperCase()));
310
+ }
311
+
312
+ function widgetGridColumn(content: string, width: number): string {
313
+ return padRightVisible(truncateToWidth(content, width, "…"), width);
314
+ }
315
+
316
+ function widgetGridColumns(theme: Theme, width: number, parts: string[]): string {
317
+ if (parts.length === 0) return "";
318
+ const gap = theme.fg("dim", " │ ");
319
+ const gapWidth = visibleWidth(gap) * (parts.length - 1);
320
+ const available = Math.max(parts.length * 8, width - gapWidth);
321
+ const base = Math.floor(available / parts.length);
322
+ let remaining = available - base * parts.length;
323
+ const columns = parts.map((part) => {
324
+ const columnWidth = base + (remaining > 0 ? 1 : 0);
325
+ remaining--;
326
+ return widgetGridColumn(part, columnWidth);
327
+ });
328
+ return truncateToWidth(columns.join(gap), width, "…");
329
+ }
330
+
331
+ function formatSmallWidgetSpend(): string {
332
+ const ledger = getLedger();
333
+ if (!ledger || ledger.units.length === 0) return "--";
334
+
335
+ const totals = getProjectTotals(ledger.units);
336
+ const parts: string[] = [];
337
+ if (totals.tokens.total > 0) parts.push(formatWidgetTokens(totals.tokens.total));
338
+ if (totals.cost > 0) parts.push(`$${totals.cost.toFixed(2)}`);
339
+ return parts.length > 0 ? parts.join(" · ") : "--";
340
+ }
341
+
307
342
  // ─── ETA Estimation ──────────────────────────────────────────────────────────
308
343
 
309
344
  /**
@@ -516,8 +551,9 @@ export const hideFooter = (_tui: unknown, theme: Theme, footerData: ReadonlyFoot
516
551
 
517
552
  /** Widget display modes: full → small → min → off → full */
518
553
  export type WidgetMode = "full" | "small" | "min" | "off";
554
+ export const DEFAULT_WIDGET_MODE: WidgetMode = "small";
519
555
  const WIDGET_MODES: WidgetMode[] = ["full", "small", "min", "off"];
520
- let widgetMode: WidgetMode = "full";
556
+ let widgetMode: WidgetMode = DEFAULT_WIDGET_MODE;
521
557
  let widgetModeInitialized = false;
522
558
  let widgetModePreferencePath: string | null = null;
523
559
 
@@ -628,7 +664,7 @@ export function getWidgetMode(projectPath?: string, globalPath?: string): Widget
628
664
 
629
665
  /** Test-only reset for widget mode caching. */
630
666
  export function _resetWidgetModeForTests(): void {
631
- widgetMode = "full";
667
+ widgetMode = DEFAULT_WIDGET_MODE;
632
668
  widgetModeInitialized = false;
633
669
  widgetModePreferencePath = null;
634
670
  }
@@ -648,6 +684,16 @@ export interface WidgetStateAccessors {
648
684
  getCurrentDispatchedModelId(): string | null;
649
685
  }
650
686
 
687
+ function clearAutoOutcomeWidget(ctx: ExtensionContext): void {
688
+ if (!ctx.hasUI) return;
689
+ ctx.ui.setWidget("gsd-outcome", undefined);
690
+ }
691
+
692
+ export function setAutoActiveStatus(ctx: ExtensionContext, status: "auto" | "next"): void {
693
+ ctx.ui.setStatus("gsd-auto", status);
694
+ clearAutoOutcomeWidget(ctx);
695
+ }
696
+
651
697
  export function updateProgressWidget(
652
698
  ctx: ExtensionContext,
653
699
  unitType: string,
@@ -673,7 +719,7 @@ export function updateProgressWidget(
673
719
  ctx.ui.setStatus("gsd-step", undefined);
674
720
  }
675
721
  if (!accessors.isSessionSwitching()) {
676
- ctx.ui.setWidget("gsd-outcome", undefined);
722
+ clearAutoOutcomeWidget(ctx);
677
723
  }
678
724
 
679
725
  const verb = unitVerb(unitType);
@@ -732,6 +778,7 @@ export function updateProgressWidget(
732
778
  logWarning("dashboard", `DB status update failed: ${err instanceof Error ? err.message : String(err)}`);
733
779
  }
734
780
  }, 15_000);
781
+ progressRefreshTimer.unref?.();
735
782
 
736
783
  return {
737
784
  render(width: number): string[] {
@@ -824,29 +871,62 @@ export function updateProgressWidget(
824
871
  return lines;
825
872
  }
826
873
 
827
- // ── Mode: small — header + active work progress ───────────────
874
+ // ── Mode: small — dense horizontal grid ───────────────────────
828
875
  if (widgetMode === "small") {
829
- lines.push("");
830
-
831
- // Action line
832
- const target = task ? `${task.id}: ${task.title}` : unitId;
833
- const actionLeft = `${pad}${theme.fg("accent", "▸")} ${theme.fg("accent", verb)} ${theme.fg("text", target)}`;
834
- lines.push(rightAlign(actionLeft, theme.fg("dim", phaseLabel), width));
876
+ lines.length = 0;
877
+ lines.push(...ui.bar());
835
878
 
836
- // Progress bar
837
879
  const roadmapSlices = mid ? getRoadmapSlicesSync() : null;
880
+ const unitLabel = unitId || [mid?.id, slice?.id, task?.id].filter(Boolean).join("/");
881
+ const statusParts = [
882
+ spinner,
883
+ theme.fg("success", modeTag),
884
+ theme.fg(stateColor, activeState),
885
+ ];
886
+ if (runtimeSignal?.summary) {
887
+ statusParts.push(theme.fg(healthColor, healthSummary));
888
+ } else if (healthLevel !== "green") {
889
+ statusParts.push(`${theme.fg(healthColor, healthIcon)} ${theme.fg(healthColor, healthSummary)}`);
890
+ }
891
+
892
+ const timeValue = [elapsed, etaShort].filter(Boolean).join(" · ") || "--";
893
+ const rowOne = widgetGridColumns(theme, width, [
894
+ `${widgetGridLabel(theme, "status", "border")} ${statusParts.join(" ")}`,
895
+ `${widgetGridLabel(theme, "unit")} ${theme.fg("text", unitLabel || "--")}`,
896
+ `${widgetGridLabel(theme, "spend", "border")} ${theme.fg("dim", formatSmallWidgetSpend())}`,
897
+ `${widgetGridLabel(theme, "time")} ${theme.fg("dim", timeValue)}`,
898
+ ]);
899
+
900
+ const target = task
901
+ ? `${task.id}: ${task.title}`
902
+ : slice
903
+ ? `${slice.id}: ${slice.title}`
904
+ : unitId;
905
+
906
+ let taskValue = task?.id ?? "--";
907
+ let sliceValue = slice?.id ?? "--";
838
908
  if (shouldRenderRoadmapProgress(roadmapSlices)) {
839
909
  const { done, total, activeSliceTasks } = roadmapSlices;
840
- const barWidth = Math.max(6, Math.min(18, Math.floor(width * 0.25)));
910
+ const barWidth = Math.max(4, Math.min(14, Math.floor(width * 0.12)));
841
911
  const bar = renderProgressBar(theme, done, total, barWidth);
842
- let meta = `${theme.fg("text", `${done}`)}${theme.fg("dim", `/${total} slices`)}`;
912
+ sliceValue = `${bar} ${theme.fg("text", `${done}/${total}`)}`;
843
913
  if (activeSliceTasks && activeSliceTasks.total > 0) {
844
- const tn = Math.min(activeSliceTasks.done + 1, activeSliceTasks.total);
845
- meta += `${theme.fg("dim", " · task ")}${theme.fg("accent", `${tn}`)}${theme.fg("dim", `/${activeSliceTasks.total}`)}`;
914
+ const taskNum = isHook
915
+ ? Math.max(activeSliceTasks.done, 1)
916
+ : Math.min(activeSliceTasks.done + 1, activeSliceTasks.total);
917
+ taskValue = `${theme.fg("accent", `${taskNum}`)}${theme.fg("dim", `/${activeSliceTasks.total}`)}`;
846
918
  }
847
- lines.push(`${pad}${bar} ${meta}`);
848
919
  }
849
920
 
921
+ const rowTwo = widgetGridColumns(theme, width, [
922
+ `${widgetGridLabel(theme, "phase", "border")} ${theme.fg("dim", unitType)}`,
923
+ `${widgetGridLabel(theme, "work")} ${theme.fg("text", target || "--")}`,
924
+ `${widgetGridLabel(theme, "task", "border")} ${taskValue}`,
925
+ `${widgetGridLabel(theme, "slice")} ${sliceValue}`,
926
+ ]);
927
+
928
+ lines.push(rowOne);
929
+ lines.push(rowTwo);
850
930
  lines.push(...ui.bar());
851
931
  cachedLines = lines;
852
932
  cachedWidth = width;
@@ -1003,7 +1083,7 @@ export function setCompletionProgressWidget(
1003
1083
  ): void {
1004
1084
  if (!ctx.hasUI) return;
1005
1085
  const widgetKey = "gsd-progress";
1006
- ctx.ui.setWidget("gsd-outcome", undefined);
1086
+ clearAutoOutcomeWidget(ctx);
1007
1087
 
1008
1088
  if (typeof ctx.ui?.setHeader === "function") {
1009
1089
  ctx.ui.setHeader(() => ({
@@ -622,6 +622,7 @@ export const DISPATCH_RULES: DispatchRule[] = [
622
622
  structuredQuestionsAvailable,
623
623
  { headless: !!process.env.GSD_HEADLESS },
624
624
  ),
625
+ pauseAfterDispatch: !process.env.GSD_HEADLESS,
625
626
  };
626
627
  },
627
628
  },
@@ -772,6 +773,7 @@ export const DISPATCH_RULES: DispatchRule[] = [
772
773
  structuredQuestionsAvailable,
773
774
  { headless: !!process.env.GSD_HEADLESS },
774
775
  ),
776
+ pauseAfterDispatch: !process.env.GSD_HEADLESS,
775
777
  };
776
778
  },
777
779
  },
@@ -805,6 +807,7 @@ export const DISPATCH_RULES: DispatchRule[] = [
805
807
  unitType: "discuss-project",
806
808
  unitId: "PROJECT",
807
809
  prompt: await buildDiscussProjectPrompt(basePath, structuredQuestionsAvailable),
810
+ pauseAfterDispatch: !process.env.GSD_HEADLESS,
808
811
  };
809
812
  },
810
813
  },
@@ -826,6 +829,7 @@ export const DISPATCH_RULES: DispatchRule[] = [
826
829
  unitType: "discuss-requirements",
827
830
  unitId: "REQUIREMENTS",
828
831
  prompt: await buildDiscussRequirementsPrompt(basePath, structuredQuestionsAvailable),
832
+ pauseAfterDispatch: !process.env.GSD_HEADLESS,
829
833
  };
830
834
  },
831
835
  },
@@ -940,6 +944,7 @@ export const DISPATCH_RULES: DispatchRule[] = [
940
944
  structuredQuestionsAvailable,
941
945
  { headless: !!process.env.GSD_HEADLESS },
942
946
  ),
947
+ pauseAfterDispatch: !process.env.GSD_HEADLESS,
943
948
  };
944
949
  },
945
950
  },
@@ -55,8 +55,10 @@ import { parseRoadmap as parseLegacyRoadmap } from "./parsers-legacy.js";
55
55
  import { consumeSignal } from "./session-status-io.js";
56
56
  import {
57
57
  checkPostUnitHooks,
58
+ consumeHookFailure,
58
59
  isRetryPending,
59
60
  consumeRetryTrigger,
61
+ consumeGateBlock,
60
62
  persistHookState,
61
63
  resolveHookArtifactPath,
62
64
  } from "./post-unit-hooks.js";
@@ -394,6 +396,50 @@ function stripKnownIdPrefix(value: string | undefined | null, id: string): strin
394
396
  return raw;
395
397
  }
396
398
 
399
+ function parseReactiveBatchTaskIds(unitId: string): string[] {
400
+ const { task: batchPart } = parseUnitId(unitId);
401
+ if (!batchPart?.startsWith("reactive+")) return [];
402
+
403
+ const rawIds = batchPart
404
+ .slice("reactive+".length)
405
+ .split(",")
406
+ .map((taskId) => taskId.trim().toUpperCase())
407
+ .filter(Boolean);
408
+
409
+ const unique = new Set<string>();
410
+ for (const taskId of rawIds) {
411
+ unique.add(taskId);
412
+ }
413
+ return [...unique];
414
+ }
415
+
416
+ function dedupePaths(values: string[]): string[] {
417
+ const seen = new Set<string>();
418
+ const result: string[] = [];
419
+ for (const value of values) {
420
+ if (!seen.has(value)) {
421
+ seen.add(value);
422
+ result.push(value);
423
+ }
424
+ }
425
+ return result;
426
+ }
427
+
428
+ function getPlannedKeyFiles(tasks: Array<
429
+ { expected_output?: string[]; files?: string[]; key_files?: string[] }
430
+ >): string[] {
431
+ return dedupePaths(
432
+ tasks.flatMap((taskRow) => [
433
+ ...(taskRow.expected_output ?? []),
434
+ ...(taskRow.files ?? []),
435
+ ...(taskRow.key_files ?? []),
436
+ ]),
437
+ );
438
+ }
439
+
440
+ export const _parseReactiveBatchTaskIdsForTest = parseReactiveBatchTaskIds;
441
+ export const _getPlannedKeyFilesForTest = getPlannedKeyFiles;
442
+
397
443
  function resolveVerificationFailureMarkerPath(
398
444
  unitType: string,
399
445
  unitId: string,
@@ -481,6 +527,40 @@ async function buildTaskCommitContextForUnit(
481
527
  };
482
528
  }
483
529
 
530
+ async function buildReactiveTaskCommitContext(
531
+ _basePath: string,
532
+ unitId: string,
533
+ ): Promise<TaskCommitContext | undefined> {
534
+ const { milestone: mid, slice: sid } = parseUnitId(unitId);
535
+ if (!mid || !sid || !isDbAvailable()) return undefined;
536
+
537
+ const batchTaskIds = parseReactiveBatchTaskIds(unitId);
538
+ if (batchTaskIds.length === 0) return undefined;
539
+
540
+ const milestone = getMilestone(mid);
541
+ const slice = getSlice(mid, sid);
542
+ const taskRows = batchTaskIds
543
+ .map((tid) => getTask(mid, sid, tid))
544
+ .filter((taskRow): taskRow is NonNullable<ReturnType<typeof getTask>> => taskRow !== null);
545
+
546
+ const keyFiles = getPlannedKeyFiles(taskRows);
547
+ if (taskRows.length === 0 || keyFiles.length === 0) return undefined;
548
+
549
+ const taskLabel = taskRows.map((row) => row.id).join(",");
550
+
551
+ return {
552
+ taskId: `${sid}/${taskLabel}`,
553
+ taskDisplayId: "reactive-batch",
554
+ taskTitle: `Reactive batch: ${taskLabel}`,
555
+ milestoneId: mid,
556
+ milestoneTitle: stripKnownIdPrefix(milestone?.title, mid),
557
+ sliceId: sid,
558
+ sliceTitle: stripKnownIdPrefix(slice?.title, sid),
559
+ oneLiner: `Reactive execute for ${taskLabel}`,
560
+ keyFiles,
561
+ };
562
+ }
563
+
484
564
  async function runPostUnitGitHubSyncIfNeeded(
485
565
  basePath: string,
486
566
  unit: NonNullable<AutoSession["currentUnit"]>,
@@ -943,6 +1023,8 @@ export async function autoCommitUnit(
943
1023
 
944
1024
  if (unitType === "execute-task") {
945
1025
  taskContext = await buildTaskCommitContextForUnit(basePath, unitId);
1026
+ } else if (unitType === "reactive-execute") {
1027
+ taskContext = await buildReactiveTaskCommitContext(basePath, unitId);
946
1028
  }
947
1029
 
948
1030
  _resetHasChangesCache();
@@ -1005,6 +1087,21 @@ async function runCloseoutGitAction(
1005
1087
  if (mid && sid && tid && isDbAvailable()) {
1006
1088
  targetRepositories = getTask(mid, sid, tid)?.target_repositories;
1007
1089
  }
1090
+ } else if (turnAction === "commit" && unit.type === "reactive-execute") {
1091
+ taskContext = await buildReactiveTaskCommitContext(s.basePath, unit.id);
1092
+ const { milestone: mid, slice: sid } = parseUnitId(unit.id);
1093
+ if (mid && sid && isDbAvailable()) {
1094
+ const repositories = new Set<string>();
1095
+ for (const tid of parseReactiveBatchTaskIds(unit.id)) {
1096
+ const taskRow = getTask(mid, sid, tid);
1097
+ for (const repoId of taskRow?.target_repositories ?? []) {
1098
+ repositories.add(repoId);
1099
+ }
1100
+ }
1101
+ if (repositories.size > 0) {
1102
+ targetRepositories = [...repositories];
1103
+ }
1104
+ }
1008
1105
  }
1009
1106
 
1010
1107
  // Invalidate the nativeHasChanges cache before auto-commit (#1853).
@@ -1436,12 +1533,24 @@ export async function postUnitPreVerification(pctx: PostUnitContext, opts?: PreV
1436
1533
  const { milestone: sMid, slice: sSid, task: sTid } = parseUnitId(s.currentUnit.id);
1437
1534
 
1438
1535
  // File change validation (execute-task only, after unit execution)
1439
- if (safetyConfig.file_change_validation && s.currentUnit.type === "execute-task" && sMid && sSid && sTid && isDbAvailable()) {
1536
+ if (safetyConfig.file_change_validation && s.currentUnit.type === "execute-task" && sMid && sSid && sTid) {
1440
1537
  try {
1441
- const taskRow = getTask(sMid, sSid, sTid);
1442
- if (taskRow) {
1443
- const expectedOutput = taskRow.expected_output ?? [];
1444
- const plannedFiles = taskRow.files ?? [];
1538
+ const sliceTaskRows = isDbAvailable()
1539
+ ? getSliceTasks(sMid, sSid).filter((t) => isClosedStatus(t.status) || t.id === sTid)
1540
+ : [];
1541
+
1542
+ if (sliceTaskRows.length > 0) {
1543
+ const expectedOutput = getPlannedKeyFiles(
1544
+ sliceTaskRows.map((taskRow) => ({
1545
+ expected_output: taskRow.expected_output,
1546
+ files: taskRow.files,
1547
+ })),
1548
+ );
1549
+ const plannedFiles = getPlannedKeyFiles(
1550
+ sliceTaskRows.map((taskRow) => ({
1551
+ files: taskRow.files,
1552
+ })),
1553
+ );
1445
1554
  const audit = validateFileChanges(s.basePath, expectedOutput, plannedFiles, safetyConfig.file_change_allowlist);
1446
1555
  if (audit && audit.violations.length > 0) {
1447
1556
  const warnings = audit.violations.filter(v => v.severity === "warning");
@@ -1455,6 +1564,30 @@ export async function postUnitPreVerification(pctx: PostUnitContext, opts?: PreV
1455
1564
  );
1456
1565
  }
1457
1566
  }
1567
+ } else {
1568
+ const taskRow = getTask(sMid, sSid, sTid);
1569
+ if (taskRow) {
1570
+ const expectedOutput = taskRow.expected_output ?? [];
1571
+ const plannedFiles = taskRow.files ?? [];
1572
+ const audit = validateFileChanges(
1573
+ s.basePath,
1574
+ expectedOutput,
1575
+ plannedFiles,
1576
+ safetyConfig.file_change_allowlist,
1577
+ );
1578
+ if (audit && audit.violations.length > 0) {
1579
+ const warnings = audit.violations.filter(v => v.severity === "warning");
1580
+ for (const v of warnings) {
1581
+ logWarning("safety", `file-change: ${v.file} — ${v.reason}`);
1582
+ }
1583
+ if (warnings.length > 0) {
1584
+ ctx.ui.notify(
1585
+ `Safety: ${warnings.length} unexpected file change(s) outside task plan`,
1586
+ "warning",
1587
+ );
1588
+ }
1589
+ }
1590
+ }
1458
1591
  }
1459
1592
  } catch (e) {
1460
1593
  debugLog("postUnit", { phase: "safety-file-change", error: String(e) });
@@ -2096,11 +2229,11 @@ export async function postUnitPostVerification(pctx: PostUnitContext): Promise<"
2096
2229
  // ── Post-unit hooks ──
2097
2230
  if (s.currentUnit && !s.stepMode) {
2098
2231
  const hookUnit = checkPostUnitHooks(s.currentUnit.type, s.currentUnit.id, s.basePath);
2232
+ persistHookState(s.basePath);
2099
2233
  if (hookUnit) {
2100
2234
  if (s.currentUnit) {
2101
2235
  await closeoutUnit(ctx, s.basePath, s.currentUnit.type, s.currentUnit.id, s.currentUnit.startedAt, buildSnapshotOpts(s.currentUnit.type, s.currentUnit.id));
2102
2236
  }
2103
- persistHookState(s.basePath);
2104
2237
 
2105
2238
  return enqueueSidecar(
2106
2239
  s, ctx,
@@ -2109,12 +2242,23 @@ export async function postUnitPostVerification(pctx: PostUnitContext): Promise<"
2109
2242
  );
2110
2243
  }
2111
2244
 
2245
+ const hookFailure = consumeHookFailure();
2246
+ if (hookFailure) {
2247
+ ctx.ui.notify(
2248
+ `Post-unit hook ${hookFailure.hookName} failed for ${hookFailure.unitId}: ${hookFailure.reason}. Pausing auto-mode.`,
2249
+ "warning",
2250
+ );
2251
+ await pauseAuto(ctx, pi);
2252
+ return "stopped";
2253
+ }
2254
+
2112
2255
  // Check if a hook requested a retry of the trigger unit
2113
2256
  if (isRetryPending()) {
2114
2257
  const trigger = consumeRetryTrigger();
2115
2258
  if (trigger) {
2259
+ persistHookState(s.basePath);
2116
2260
  ctx.ui.notify(
2117
- `Hook requested retry of ${trigger.unitType} ${trigger.unitId} — resetting task state.`,
2261
+ `Hook requested retry of ${trigger.unitType} ${trigger.unitId} — resetting trigger unit state.`,
2118
2262
  "info",
2119
2263
  );
2120
2264
 
@@ -2168,6 +2312,19 @@ export async function postUnitPostVerification(pctx: PostUnitContext): Promise<"
2168
2312
  // Fall through to normal dispatch — deriveState will re-derive the unit
2169
2313
  }
2170
2314
  }
2315
+
2316
+ const gateBlock = consumeGateBlock();
2317
+ if (gateBlock) {
2318
+ persistHookState(s.basePath);
2319
+ const verdict = gateBlock.verdict ? ` verdict=${gateBlock.verdict};` : "";
2320
+ const artifact = gateBlock.artifact ? ` artifact=${gateBlock.artifact};` : "";
2321
+ const message =
2322
+ `Post-unit gate "${gateBlock.hookName}" blocked ${gateBlock.triggerUnitType} ${gateBlock.triggerUnitId}:` +
2323
+ `${verdict}${artifact} ${gateBlock.reason}. Run /gsd status to inspect, then /gsd auto after recovery.`;
2324
+ ctx.ui.notify(message, "warning");
2325
+ await pauseAuto(ctx, pi);
2326
+ return "stopped";
2327
+ }
2171
2328
  }
2172
2329
 
2173
2330
  // ── Fast-path stop detection (#3487) ──