@opengsd/gsd-pi 1.1.1-dev.74e8dd1 → 1.1.1-dev.a5a2de8

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 (329) hide show
  1. package/dist/cli.js +3 -2
  2. package/dist/help-text.js +10 -6
  3. package/dist/resources/.managed-resources-content-hash +1 -1
  4. package/dist/resources/extensions/browser-tools/engine/managed-gsd-browser.js +495 -0
  5. package/dist/resources/extensions/browser-tools/engine/selection.js +16 -0
  6. package/dist/resources/extensions/browser-tools/extension-manifest.json +2 -2
  7. package/dist/resources/extensions/browser-tools/index.js +57 -9
  8. package/dist/resources/extensions/browser-tools/package.json +5 -1
  9. package/dist/resources/extensions/gsd/auto/orchestrator.js +0 -1
  10. package/dist/resources/extensions/gsd/auto-dashboard.js +77 -13
  11. package/dist/resources/extensions/gsd/auto-dispatch.js +16 -0
  12. package/dist/resources/extensions/gsd/auto-post-unit.js +21 -3
  13. package/dist/resources/extensions/gsd/auto-prompts.js +63 -22
  14. package/dist/resources/extensions/gsd/auto-recovery.js +3 -4
  15. package/dist/resources/extensions/gsd/auto-runtime-state.js +3 -0
  16. package/dist/resources/extensions/gsd/auto-tool-tracking.js +1 -1
  17. package/dist/resources/extensions/gsd/auto-unit-tool-scope.js +18 -66
  18. package/dist/resources/extensions/gsd/auto-worktree.js +18 -5
  19. package/dist/resources/extensions/gsd/auto.js +9 -2
  20. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +20 -14
  21. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +28 -13
  22. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +18 -29
  23. package/dist/resources/extensions/gsd/browser-evidence.js +29 -2
  24. package/dist/resources/extensions/gsd/closeout-consistency-gate.js +61 -0
  25. package/dist/resources/extensions/gsd/commands/handlers/ops.js +2 -2
  26. package/dist/resources/extensions/gsd/commands-handlers.js +76 -11
  27. package/dist/resources/extensions/gsd/commands-mcp-status.js +2 -1
  28. package/dist/resources/extensions/gsd/dashboard-overlay.js +21 -7
  29. package/dist/resources/extensions/gsd/docs/preferences-reference.md +8 -0
  30. package/dist/resources/extensions/gsd/doctor-runtime-checks.js +2 -2
  31. package/dist/resources/extensions/gsd/escalation.js +4 -4
  32. package/dist/resources/extensions/gsd/forensics.js +74 -2
  33. package/dist/resources/extensions/gsd/gsd-db.js +5 -2
  34. package/dist/resources/extensions/gsd/guided-flow.js +118 -175
  35. package/dist/resources/extensions/gsd/mcp-project-config.js +9 -76
  36. package/dist/resources/extensions/gsd/memory-store.js +4 -1
  37. package/dist/resources/extensions/gsd/milestone-closeout.js +3 -1
  38. package/dist/resources/extensions/gsd/pending-auto-start.js +0 -1
  39. package/dist/resources/extensions/gsd/post-unit-hooks.js +9 -0
  40. package/dist/resources/extensions/gsd/preferences-validation.js +39 -0
  41. package/dist/resources/extensions/gsd/prompt-loader.js +7 -0
  42. package/dist/resources/extensions/gsd/prompts/forensics.md +61 -1
  43. package/dist/resources/extensions/gsd/prompts/gate-evaluate.md +3 -1
  44. package/dist/resources/extensions/gsd/prompts/parallel-research-slices.md +3 -1
  45. package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  46. package/dist/resources/extensions/gsd/prompts/reactive-execute.md +3 -1
  47. package/dist/resources/extensions/gsd/prompts/run-uat.md +25 -21
  48. package/dist/resources/extensions/gsd/prompts/validate-milestone.md +3 -3
  49. package/dist/resources/extensions/gsd/recovery-classification.js +20 -0
  50. package/dist/resources/extensions/gsd/rule-registry.js +428 -52
  51. package/dist/resources/extensions/gsd/state.js +2 -2
  52. package/dist/resources/extensions/gsd/templates/plan.md +3 -1
  53. package/dist/resources/extensions/gsd/tool-contract.js +5 -0
  54. package/dist/resources/extensions/gsd/tool-presentation-plan.js +17 -7
  55. package/dist/resources/extensions/gsd/tools/complete-slice.js +15 -1
  56. package/dist/resources/extensions/gsd/tools/complete-task.js +11 -1
  57. package/dist/resources/extensions/gsd/tools/validate-milestone.js +46 -16
  58. package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +132 -18
  59. package/dist/resources/extensions/gsd/unit-tool-contracts.js +169 -0
  60. package/dist/resources/extensions/gsd/verdict-parser.js +59 -15
  61. package/dist/resources/extensions/gsd/verification-gate.js +72 -1
  62. package/dist/resources/extensions/gsd/workflow-mcp.js +3 -75
  63. package/dist/resources/extensions/shared/gsd-browser-cli.js +145 -0
  64. package/dist/rtk.d.ts +7 -1
  65. package/dist/rtk.js +27 -11
  66. package/dist/update-check.d.ts +15 -1
  67. package/dist/update-check.js +87 -12
  68. package/dist/update-cmd.d.ts +1 -0
  69. package/dist/update-cmd.js +53 -2
  70. package/dist/web/standalone/.next/BUILD_ID +1 -1
  71. package/dist/web/standalone/.next/app-path-routes-manifest.json +7 -7
  72. package/dist/web/standalone/.next/build-manifest.json +2 -2
  73. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  74. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  75. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  76. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  77. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  78. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  79. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  80. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  81. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  82. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  83. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  84. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  85. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  86. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  87. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  88. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  89. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  90. package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
  91. package/dist/web/standalone/.next/server/app/index.html +1 -1
  92. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  93. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  94. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  95. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  96. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  97. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  98. package/dist/web/standalone/.next/server/app-paths-manifest.json +7 -7
  99. package/dist/web/standalone/.next/server/chunks/8357.js +1 -1
  100. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  101. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  102. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  103. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  104. package/package.json +4 -2
  105. package/packages/cloud-mcp-gateway/package.json +2 -2
  106. package/packages/contracts/package.json +1 -1
  107. package/packages/daemon/package.json +4 -4
  108. package/packages/gsd-agent-core/dist/agent-session.d.ts +9 -0
  109. package/packages/gsd-agent-core/dist/agent-session.d.ts.map +1 -1
  110. package/packages/gsd-agent-core/dist/agent-session.js +32 -0
  111. package/packages/gsd-agent-core/dist/agent-session.js.map +1 -1
  112. package/packages/gsd-agent-core/dist/index.d.ts +1 -0
  113. package/packages/gsd-agent-core/dist/index.d.ts.map +1 -1
  114. package/packages/gsd-agent-core/dist/index.js +1 -0
  115. package/packages/gsd-agent-core/dist/index.js.map +1 -1
  116. package/packages/gsd-agent-core/dist/session/agent-session-compaction.d.ts +2 -0
  117. package/packages/gsd-agent-core/dist/session/agent-session-compaction.d.ts.map +1 -1
  118. package/packages/gsd-agent-core/dist/session/agent-session-compaction.js +8 -2
  119. package/packages/gsd-agent-core/dist/session/agent-session-compaction.js.map +1 -1
  120. package/packages/gsd-agent-core/dist/session/agent-session-host.d.ts +7 -0
  121. package/packages/gsd-agent-core/dist/session/agent-session-host.d.ts.map +1 -1
  122. package/packages/gsd-agent-core/dist/session/agent-session-host.js.map +1 -1
  123. package/packages/gsd-agent-core/dist/session/agent-session-prompt.d.ts.map +1 -1
  124. package/packages/gsd-agent-core/dist/session/agent-session-prompt.js +69 -1
  125. package/packages/gsd-agent-core/dist/session/agent-session-prompt.js.map +1 -1
  126. package/packages/gsd-agent-core/dist/turn-latency.d.ts +47 -0
  127. package/packages/gsd-agent-core/dist/turn-latency.d.ts.map +1 -0
  128. package/packages/gsd-agent-core/dist/turn-latency.js +123 -0
  129. package/packages/gsd-agent-core/dist/turn-latency.js.map +1 -0
  130. package/packages/gsd-agent-core/package.json +6 -6
  131. package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/gsd-widget-prototype.d.ts +21 -0
  132. package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/gsd-widget-prototype.d.ts.map +1 -0
  133. package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/gsd-widget-prototype.js +213 -0
  134. package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/gsd-widget-prototype.js.map +1 -0
  135. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  136. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js +5 -0
  137. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js.map +1 -1
  138. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  139. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js +20 -0
  140. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  141. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/input-controller.d.ts.map +1 -1
  142. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/input-controller.js +7 -1
  143. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/input-controller.js.map +1 -1
  144. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-command-handlers.d.ts.map +1 -1
  145. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-command-handlers.js +6 -0
  146. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-command-handlers.js.map +1 -1
  147. package/packages/gsd-agent-modes/package.json +7 -7
  148. package/packages/mcp-server/dist/remote-questions.d.ts.map +1 -1
  149. package/packages/mcp-server/dist/remote-questions.js +23 -9
  150. package/packages/mcp-server/dist/remote-questions.js.map +1 -1
  151. package/packages/mcp-server/dist/workflow-tools.js +2 -2
  152. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  153. package/packages/mcp-server/package.json +3 -3
  154. package/packages/native/package.json +1 -1
  155. package/packages/pi-agent-core/dist/agent-loop.js +42 -3
  156. package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
  157. package/packages/pi-agent-core/dist/agent.d.ts +5 -1
  158. package/packages/pi-agent-core/dist/agent.d.ts.map +1 -1
  159. package/packages/pi-agent-core/dist/agent.js +2 -0
  160. package/packages/pi-agent-core/dist/agent.js.map +1 -1
  161. package/packages/pi-agent-core/dist/harness/agent-harness.d.ts.map +1 -1
  162. package/packages/pi-agent-core/dist/harness/agent-harness.js +3 -1
  163. package/packages/pi-agent-core/dist/harness/agent-harness.js.map +1 -1
  164. package/packages/pi-agent-core/dist/harness/types.d.ts +1 -0
  165. package/packages/pi-agent-core/dist/harness/types.d.ts.map +1 -1
  166. package/packages/pi-agent-core/dist/harness/types.js.map +1 -1
  167. package/packages/pi-agent-core/dist/types.d.ts +6 -1
  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/models.generated.d.ts +74 -6
  176. package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
  177. package/packages/pi-ai/dist/models.generated.js +78 -10
  178. package/packages/pi-ai/dist/models.generated.js.map +1 -1
  179. package/packages/pi-ai/dist/stream.js +6 -6
  180. package/packages/pi-ai/dist/stream.js.map +1 -1
  181. package/packages/pi-ai/package.json +1 -1
  182. package/packages/pi-coding-agent/dist/core/extensions/extension-upstream-types.d.ts +3 -0
  183. package/packages/pi-coding-agent/dist/core/extensions/extension-upstream-types.d.ts.map +1 -1
  184. package/packages/pi-coding-agent/dist/core/extensions/extension-upstream-types.js.map +1 -1
  185. package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
  186. package/packages/pi-coding-agent/dist/core/model-registry.js +2 -2
  187. package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
  188. package/packages/pi-coding-agent/dist/core/tools/bash.js +2 -2
  189. package/packages/pi-coding-agent/dist/core/tools/bash.js.map +1 -1
  190. package/packages/pi-coding-agent/dist/core/tools/edit.d.ts.map +1 -1
  191. package/packages/pi-coding-agent/dist/core/tools/edit.js +3 -2
  192. package/packages/pi-coding-agent/dist/core/tools/edit.js.map +1 -1
  193. package/packages/pi-coding-agent/dist/core/tools/render-utils.d.ts +1 -0
  194. package/packages/pi-coding-agent/dist/core/tools/render-utils.d.ts.map +1 -1
  195. package/packages/pi-coding-agent/dist/core/tools/render-utils.js +6 -0
  196. package/packages/pi-coding-agent/dist/core/tools/render-utils.js.map +1 -1
  197. package/packages/pi-coding-agent/dist/core/tools/write.d.ts.map +1 -1
  198. package/packages/pi-coding-agent/dist/core/tools/write.js +3 -2
  199. package/packages/pi-coding-agent/dist/core/tools/write.js.map +1 -1
  200. package/packages/pi-coding-agent/package.json +7 -7
  201. package/packages/pi-tui/package.json +1 -1
  202. package/packages/rpc-client/package.json +2 -2
  203. package/pkg/package.json +1 -1
  204. package/src/resources/extensions/browser-tools/engine/managed-gsd-browser.ts +579 -0
  205. package/src/resources/extensions/browser-tools/engine/selection.ts +19 -0
  206. package/src/resources/extensions/browser-tools/extension-manifest.json +2 -2
  207. package/src/resources/extensions/browser-tools/index.ts +60 -9
  208. package/src/resources/extensions/browser-tools/package.json +5 -1
  209. package/src/resources/extensions/browser-tools/tests/browser-engine-selection.test.mjs +35 -0
  210. package/src/resources/extensions/browser-tools/tests/managed-gsd-browser-tools.test.mjs +33 -0
  211. package/src/resources/extensions/gsd/auto/orchestrator.ts +0 -1
  212. package/src/resources/extensions/gsd/auto-dashboard.ts +82 -14
  213. package/src/resources/extensions/gsd/auto-dispatch.ts +19 -0
  214. package/src/resources/extensions/gsd/auto-post-unit.ts +28 -2
  215. package/src/resources/extensions/gsd/auto-prompts.ts +97 -15
  216. package/src/resources/extensions/gsd/auto-recovery.ts +3 -3
  217. package/src/resources/extensions/gsd/auto-runtime-state.ts +4 -0
  218. package/src/resources/extensions/gsd/auto-tool-tracking.ts +1 -1
  219. package/src/resources/extensions/gsd/auto-unit-tool-scope.ts +43 -74
  220. package/src/resources/extensions/gsd/auto-worktree.ts +23 -5
  221. package/src/resources/extensions/gsd/auto.ts +12 -2
  222. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +20 -14
  223. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +32 -13
  224. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +50 -54
  225. package/src/resources/extensions/gsd/browser-evidence.ts +26 -2
  226. package/src/resources/extensions/gsd/closeout-consistency-gate.ts +137 -0
  227. package/src/resources/extensions/gsd/commands/handlers/ops.ts +2 -2
  228. package/src/resources/extensions/gsd/commands-handlers.ts +76 -11
  229. package/src/resources/extensions/gsd/commands-mcp-status.ts +2 -1
  230. package/src/resources/extensions/gsd/dashboard-overlay.ts +28 -7
  231. package/src/resources/extensions/gsd/docs/preferences-reference.md +8 -0
  232. package/src/resources/extensions/gsd/doctor-runtime-checks.ts +2 -2
  233. package/src/resources/extensions/gsd/escalation.ts +4 -4
  234. package/src/resources/extensions/gsd/forensics.ts +99 -5
  235. package/src/resources/extensions/gsd/gsd-db.ts +5 -2
  236. package/src/resources/extensions/gsd/guided-flow.ts +214 -216
  237. package/src/resources/extensions/gsd/mcp-project-config.ts +13 -78
  238. package/src/resources/extensions/gsd/memory-store.ts +4 -1
  239. package/src/resources/extensions/gsd/milestone-closeout.ts +3 -1
  240. package/src/resources/extensions/gsd/pending-auto-start.ts +0 -2
  241. package/src/resources/extensions/gsd/post-unit-hooks.ts +14 -1
  242. package/src/resources/extensions/gsd/preferences-validation.ts +36 -0
  243. package/src/resources/extensions/gsd/prompt-loader.ts +8 -0
  244. package/src/resources/extensions/gsd/prompts/forensics.md +61 -1
  245. package/src/resources/extensions/gsd/prompts/gate-evaluate.md +3 -1
  246. package/src/resources/extensions/gsd/prompts/parallel-research-slices.md +3 -1
  247. package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  248. package/src/resources/extensions/gsd/prompts/reactive-execute.md +3 -1
  249. package/src/resources/extensions/gsd/prompts/run-uat.md +25 -21
  250. package/src/resources/extensions/gsd/prompts/validate-milestone.md +3 -3
  251. package/src/resources/extensions/gsd/recovery-classification.ts +20 -0
  252. package/src/resources/extensions/gsd/rule-registry.ts +558 -58
  253. package/src/resources/extensions/gsd/rule-types.ts +2 -0
  254. package/src/resources/extensions/gsd/state.ts +2 -2
  255. package/src/resources/extensions/gsd/templates/plan.md +3 -1
  256. package/src/resources/extensions/gsd/tests/auto-dashboard.test.ts +105 -4
  257. package/src/resources/extensions/gsd/tests/auto-orchestrator.test.ts +37 -0
  258. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +10 -2
  259. package/src/resources/extensions/gsd/tests/auto-start-bootstrap-await-3420.test.ts +4 -1
  260. package/src/resources/extensions/gsd/tests/auto-warning-noise-regression.test.ts +12 -2
  261. package/src/resources/extensions/gsd/tests/browser-evidence.test.ts +142 -0
  262. package/src/resources/extensions/gsd/tests/check-auto-start-pending-gate.test.ts +9 -15
  263. package/src/resources/extensions/gsd/tests/check-auto-start-ready-guard.test.ts +26 -16
  264. package/src/resources/extensions/gsd/tests/commands-dispatcher-unmerged-milestone.test.ts +21 -0
  265. package/src/resources/extensions/gsd/tests/complete-milestone-excerpt.test.ts +30 -0
  266. package/src/resources/extensions/gsd/tests/complete-slice-verification-gate.test.ts +42 -0
  267. package/src/resources/extensions/gsd/tests/dashboard-overlay.test.ts +45 -0
  268. package/src/resources/extensions/gsd/tests/deep-planning-mode-dispatch.test.ts +53 -0
  269. package/src/resources/extensions/gsd/tests/discuss-milestone-structured-questions.test.ts +31 -0
  270. package/src/resources/extensions/gsd/tests/dispatch-complete-milestone-guard.test.ts +40 -1
  271. package/src/resources/extensions/gsd/tests/doctor-runtime-checks.test.ts +27 -0
  272. package/src/resources/extensions/gsd/tests/escalation.test.ts +16 -27
  273. package/src/resources/extensions/gsd/tests/forensics-issue-routing.test.ts +20 -0
  274. package/src/resources/extensions/gsd/tests/forensics-prompt-rendering.test.ts +3 -0
  275. package/src/resources/extensions/gsd/tests/forensics-tool-scope.test.ts +69 -0
  276. package/src/resources/extensions/gsd/tests/gate-1b-orphan-discrimination.test.ts +31 -79
  277. package/src/resources/extensions/gsd/tests/guided-discuss-milestone-prompt-rendering.test.ts +40 -1
  278. package/src/resources/extensions/gsd/tests/guided-dispatch-root.test.ts +86 -0
  279. package/src/resources/extensions/gsd/tests/guided-flow-session-isolation.test.ts +5 -3
  280. package/src/resources/extensions/gsd/tests/guided-flow-state-rebuild.test.ts +40 -4
  281. package/src/resources/extensions/gsd/tests/guided-flow.test.ts +12 -9
  282. package/src/resources/extensions/gsd/tests/integration/auto-recovery.test.ts +4 -4
  283. package/src/resources/extensions/gsd/tests/integration/auto-worktree-milestone-merge.test.ts +8 -0
  284. package/src/resources/extensions/gsd/tests/integration/parallel-merge.test.ts +16 -0
  285. package/src/resources/extensions/gsd/tests/integration/run-uat.test.ts +69 -10
  286. package/src/resources/extensions/gsd/tests/mcp-project-config.test.ts +32 -0
  287. package/src/resources/extensions/gsd/tests/mcp-status.test.ts +2 -0
  288. package/src/resources/extensions/gsd/tests/memory-maintenance.test.ts +39 -8
  289. package/src/resources/extensions/gsd/tests/merge-closeout-consistency-gate.test.ts +63 -0
  290. package/src/resources/extensions/gsd/tests/merge-db-cycle.test.ts +10 -1
  291. package/src/resources/extensions/gsd/tests/milestone-closeout.test.ts +9 -1
  292. package/src/resources/extensions/gsd/tests/new-milestone-discuss-routing.test.ts +3 -3
  293. package/src/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +9 -0
  294. package/src/resources/extensions/gsd/tests/post-unit-hooks.test.ts +157 -0
  295. package/src/resources/extensions/gsd/tests/post-unit-retry-on-orchestrator-bridge.test.ts +179 -0
  296. package/src/resources/extensions/gsd/tests/preferences.test.ts +29 -0
  297. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +61 -1
  298. package/src/resources/extensions/gsd/tests/prompt-loader-extension-dir.test.ts +14 -0
  299. package/src/resources/extensions/gsd/tests/queued-discuss-fast-path.test.ts +7 -8
  300. package/src/resources/extensions/gsd/tests/register-hooks-depth-verification.test.ts +44 -0
  301. package/src/resources/extensions/gsd/tests/rule-registry.test.ts +75 -0
  302. package/src/resources/extensions/gsd/tests/run-uat-composer.test.ts +4 -0
  303. package/src/resources/extensions/gsd/tests/runtime-invariant-modules.test.ts +36 -0
  304. package/src/resources/extensions/gsd/tests/session-start-footer.test.ts +100 -0
  305. package/src/resources/extensions/gsd/tests/state-reconciliation-drift.test.ts +139 -0
  306. package/src/resources/extensions/gsd/tests/token-tool-gating.test.ts +4 -4
  307. package/src/resources/extensions/gsd/tests/tool-invocation-error-loop-break.test.ts +19 -0
  308. package/src/resources/extensions/gsd/tests/tool-param-optionality.test.ts +7 -1
  309. package/src/resources/extensions/gsd/tests/validate-milestone-prompt-verification-classes.test.ts +6 -3
  310. package/src/resources/extensions/gsd/tests/validate-milestone-write-order.test.ts +133 -0
  311. package/src/resources/extensions/gsd/tests/verification-gate.test.ts +51 -0
  312. package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +351 -0
  313. package/src/resources/extensions/gsd/tests/write-gate-planning-unit.test.ts +15 -0
  314. package/src/resources/extensions/gsd/tool-contract.ts +6 -0
  315. package/src/resources/extensions/gsd/tool-presentation-plan.ts +38 -8
  316. package/src/resources/extensions/gsd/tools/complete-slice.ts +14 -1
  317. package/src/resources/extensions/gsd/tools/complete-task.ts +20 -2
  318. package/src/resources/extensions/gsd/tools/validate-milestone.ts +46 -15
  319. package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +163 -20
  320. package/src/resources/extensions/gsd/types.ts +69 -5
  321. package/src/resources/extensions/gsd/unit-tool-contracts.ts +186 -0
  322. package/src/resources/extensions/gsd/verdict-parser.ts +54 -13
  323. package/src/resources/extensions/gsd/verification-gate.ts +87 -1
  324. package/src/resources/extensions/gsd/workflow-mcp.ts +3 -75
  325. package/src/resources/extensions/shared/gsd-browser-cli.ts +172 -0
  326. package/src/resources/extensions/gsd/tests/gate-1b-recovery-bound-corrections.test.ts +0 -246
  327. package/src/resources/extensions/gsd/tests/gate-1b-recovery-bound.test.ts +0 -218
  328. /package/dist/web/standalone/.next/static/{eRWf-RI9bzbrwEurm_3uI → 9y3LeeR2uGr2yRj9RjY3D}/_buildManifest.js +0 -0
  329. /package/dist/web/standalone/.next/static/{eRWf-RI9bzbrwEurm_3uI → 9y3LeeR2uGr2yRj9RjY3D}/_ssgManifest.js +0 -0
@@ -1,11 +1,16 @@
1
- /** browser-tools — pi extension: full browser interaction via Playwright. */
1
+ /** browser-tools — Pi Browser Automation Contract adapter. */
2
2
  import { importExtensionModule, type ExtensionAPI } from "@gsd/pi-coding-agent";
3
3
 
4
- let registrationPromise: Promise<void> | null = null;
4
+ import { closeManagedGsdBrowser, registerManagedGsdBrowserTools } from "./engine/managed-gsd-browser.js";
5
+ import { resolveBrowserEngineMode, type BrowserEngineMode } from "./engine/selection.js";
5
6
 
6
- async function registerBrowserTools(pi: ExtensionAPI): Promise<void> {
7
- if (!registrationPromise) {
8
- registrationPromise = (async () => {
7
+ let legacyRegistrationPromise: Promise<void> | null = null;
8
+ let managedRegistrationPromise: Promise<void> | null = null;
9
+ let registeredEngine: Exclude<BrowserEngineMode, "off"> | null = null;
10
+
11
+ async function registerLegacyBrowserTools(pi: ExtensionAPI): Promise<void> {
12
+ if (!legacyRegistrationPromise) {
13
+ legacyRegistrationPromise = (async () => {
9
14
  const [
10
15
  lifecycle,
11
16
  capture,
@@ -136,12 +141,55 @@ async function registerBrowserTools(pi: ExtensionAPI): Promise<void> {
136
141
  injectionDetection.registerInjectionDetectionTools(pi, deps);
137
142
  verify.registerVerifyTools(pi, deps);
138
143
  })().catch((error) => {
139
- registrationPromise = null;
144
+ legacyRegistrationPromise = null;
140
145
  throw error;
141
146
  });
142
147
  }
143
148
 
144
- return registrationPromise;
149
+ return legacyRegistrationPromise;
150
+ }
151
+
152
+ async function registerBrowserTools(pi: ExtensionAPI): Promise<void> {
153
+ const engine = resolveBrowserEngineMode();
154
+ if (engine === "off") return;
155
+ if (registeredEngine && registeredEngine !== engine) {
156
+ throw new Error(
157
+ `Browser tools already registered with GSD_BROWSER_ENGINE=${registeredEngine}. Restart GSD before switching to ${engine}.`,
158
+ );
159
+ }
160
+
161
+ let registration: Promise<void>;
162
+ if (engine === "legacy") {
163
+ registration = registerLegacyBrowserTools(pi);
164
+ } else if (!managedRegistrationPromise) {
165
+ managedRegistrationPromise = Promise.resolve()
166
+ .then(() => {
167
+ registerManagedGsdBrowserTools(pi);
168
+ })
169
+ .catch((error) => {
170
+ managedRegistrationPromise = null;
171
+ throw error;
172
+ });
173
+ registration = managedRegistrationPromise;
174
+ } else {
175
+ registration = managedRegistrationPromise;
176
+ }
177
+
178
+ registeredEngine = engine;
179
+ try {
180
+ await registration;
181
+ } catch (error) {
182
+ if (registeredEngine === engine) registeredEngine = null;
183
+ throw error;
184
+ }
185
+ }
186
+
187
+ async function closeActiveBrowserEngines(): Promise<void> {
188
+ await closeManagedGsdBrowser();
189
+ if (legacyRegistrationPromise) {
190
+ const { closeBrowser } = await importExtensionModule<typeof import("./lifecycle.js")>(import.meta.url, "./lifecycle.js");
191
+ await closeBrowser();
192
+ }
145
193
  }
146
194
 
147
195
  export default function (pi: ExtensionAPI) {
@@ -157,7 +205,10 @@ export default function (pi: ExtensionAPI) {
157
205
  });
158
206
 
159
207
  pi.on("session_shutdown", async () => {
160
- const { closeBrowser } = await importExtensionModule<typeof import("./lifecycle.js")>(import.meta.url, "./lifecycle.js");
161
- await closeBrowser();
208
+ await closeActiveBrowserEngines();
209
+ });
210
+
211
+ pi.on("session_switch", async () => {
212
+ await closeActiveBrowserEngines();
162
213
  });
163
214
  }
@@ -4,16 +4,20 @@
4
4
  "version": "1.0.0",
5
5
  "type": "module",
6
6
  "scripts": {
7
- "test": "node --test tests/*.test.mjs"
7
+ "test": "node --import ../gsd/tests/resolve-ts.mjs --experimental-strip-types --test tests/*.test.mjs"
8
8
  },
9
9
  "pi": {
10
10
  "extensions": ["./index.ts"]
11
11
  },
12
12
  "peerDependencies": {
13
+ "@opengsd/gsd-browser": ">=0.1.27",
13
14
  "playwright": ">=1.40.0",
14
15
  "sharp": ">=0.33.0"
15
16
  },
16
17
  "peerDependenciesMeta": {
18
+ "@opengsd/gsd-browser": {
19
+ "optional": true
20
+ },
17
21
  "playwright": {
18
22
  "optional": true
19
23
  },
@@ -0,0 +1,35 @@
1
+ import { describe, it } from "node:test";
2
+ import assert from "node:assert/strict";
3
+ import { createRequire } from "node:module";
4
+ import { dirname } from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+
7
+ const __dirname = dirname(fileURLToPath(import.meta.url));
8
+ const require = createRequire(import.meta.url);
9
+ const jiti = require("jiti")(__dirname, { interopDefault: true, debug: false });
10
+
11
+ const { resolveBrowserEngineMode } = jiti("../engine/selection.ts");
12
+
13
+ describe("resolveBrowserEngineMode", () => {
14
+ it("defaults to gsd-browser", () => {
15
+ assert.equal(resolveBrowserEngineMode({}), "gsd-browser");
16
+ });
17
+
18
+ it("accepts the explicit engine modes", () => {
19
+ assert.equal(resolveBrowserEngineMode({ GSD_BROWSER_ENGINE: "gsd-browser" }), "gsd-browser");
20
+ assert.equal(resolveBrowserEngineMode({ GSD_BROWSER_ENGINE: "legacy" }), "legacy");
21
+ assert.equal(resolveBrowserEngineMode({ GSD_BROWSER_ENGINE: "off" }), "off");
22
+ });
23
+
24
+ it("accepts compatibility aliases", () => {
25
+ assert.equal(resolveBrowserEngineMode({ GSD_BROWSER_ENGINE: "playwright" }), "legacy");
26
+ assert.equal(resolveBrowserEngineMode({ GSD_BROWSER_ENGINE: "false" }), "off");
27
+ });
28
+
29
+ it("rejects unknown engine modes", () => {
30
+ assert.throws(
31
+ () => resolveBrowserEngineMode({ GSD_BROWSER_ENGINE: "surprise" }),
32
+ /Expected "gsd-browser", "legacy", or "off"/,
33
+ );
34
+ });
35
+ });
@@ -0,0 +1,33 @@
1
+ import { describe, it } from "node:test";
2
+ import assert from "node:assert/strict";
3
+
4
+ const {
5
+ MANAGED_GSD_BROWSER_TOOL_NAMES,
6
+ registerManagedGsdBrowserTools,
7
+ } = await import("../engine/managed-gsd-browser.ts");
8
+
9
+ describe("registerManagedGsdBrowserTools", () => {
10
+ it("registers the curated Pi browser contract", () => {
11
+ const tools = [];
12
+ registerManagedGsdBrowserTools({
13
+ registerTool(tool) {
14
+ tools.push(tool);
15
+ },
16
+ });
17
+
18
+ assert.deepEqual(tools.map((tool) => tool.name), [...MANAGED_GSD_BROWSER_TOOL_NAMES]);
19
+ assert.equal(new Set(tools.map((tool) => tool.name)).size, tools.length);
20
+ });
21
+
22
+ it("keeps screenshots marked as image-producing evidence", () => {
23
+ const tools = [];
24
+ registerManagedGsdBrowserTools({
25
+ registerTool(tool) {
26
+ tools.push(tool);
27
+ },
28
+ });
29
+
30
+ const screenshot = tools.find((tool) => tool.name === "browser_screenshot");
31
+ assert.equal(screenshot?.compatibility?.producesImages, true);
32
+ });
33
+ });
@@ -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({
@@ -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
  /**
@@ -836,29 +871,62 @@ export function updateProgressWidget(
836
871
  return lines;
837
872
  }
838
873
 
839
- // ── Mode: small — header + active work progress ───────────────
874
+ // ── Mode: small — dense horizontal grid ───────────────────────
840
875
  if (widgetMode === "small") {
841
- lines.push("");
842
-
843
- // Action line
844
- const target = task ? `${task.id}: ${task.title}` : unitId;
845
- const actionLeft = `${pad}${theme.fg("accent", "▸")} ${theme.fg("accent", verb)} ${theme.fg("text", target)}`;
846
- lines.push(rightAlign(actionLeft, theme.fg("dim", phaseLabel), width));
876
+ lines.length = 0;
877
+ lines.push(...ui.bar());
847
878
 
848
- // Progress bar
849
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 ?? "--";
850
908
  if (shouldRenderRoadmapProgress(roadmapSlices)) {
851
909
  const { done, total, activeSliceTasks } = roadmapSlices;
852
- 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)));
853
911
  const bar = renderProgressBar(theme, done, total, barWidth);
854
- let meta = `${theme.fg("text", `${done}`)}${theme.fg("dim", `/${total} slices`)}`;
912
+ sliceValue = `${bar} ${theme.fg("text", `${done}/${total}`)}`;
855
913
  if (activeSliceTasks && activeSliceTasks.total > 0) {
856
- const tn = Math.min(activeSliceTasks.done + 1, activeSliceTasks.total);
857
- 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}`)}`;
858
918
  }
859
- lines.push(`${pad}${bar} ${meta}`);
860
919
  }
861
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);
862
930
  lines.push(...ui.bar());
863
931
  cachedLines = lines;
864
932
  cachedWidth = width;
@@ -96,6 +96,10 @@ import { probeGitConflictState } from "./git-conflict-state.js";
96
96
  import { runTurnGitAction } from "./git-service.js";
97
97
  import { parseUnitId } from "./unit-id.js";
98
98
  import { resolveExpectedArtifactPath } from "./auto-artifact-paths.js";
99
+ import {
100
+ checkCloseoutConsistencyGate,
101
+ formatCloseoutConsistencyBlock,
102
+ } from "./closeout-consistency-gate.js";
99
103
 
100
104
  // ─── Types ────────────────────────────────────────────────────────────────
101
105
 
@@ -622,6 +626,7 @@ export const DISPATCH_RULES: DispatchRule[] = [
622
626
  structuredQuestionsAvailable,
623
627
  { headless: !!process.env.GSD_HEADLESS },
624
628
  ),
629
+ pauseAfterDispatch: !process.env.GSD_HEADLESS,
625
630
  };
626
631
  },
627
632
  },
@@ -772,6 +777,7 @@ export const DISPATCH_RULES: DispatchRule[] = [
772
777
  structuredQuestionsAvailable,
773
778
  { headless: !!process.env.GSD_HEADLESS },
774
779
  ),
780
+ pauseAfterDispatch: !process.env.GSD_HEADLESS,
775
781
  };
776
782
  },
777
783
  },
@@ -805,6 +811,7 @@ export const DISPATCH_RULES: DispatchRule[] = [
805
811
  unitType: "discuss-project",
806
812
  unitId: "PROJECT",
807
813
  prompt: await buildDiscussProjectPrompt(basePath, structuredQuestionsAvailable),
814
+ pauseAfterDispatch: !process.env.GSD_HEADLESS,
808
815
  };
809
816
  },
810
817
  },
@@ -826,6 +833,7 @@ export const DISPATCH_RULES: DispatchRule[] = [
826
833
  unitType: "discuss-requirements",
827
834
  unitId: "REQUIREMENTS",
828
835
  prompt: await buildDiscussRequirementsPrompt(basePath, structuredQuestionsAvailable),
836
+ pauseAfterDispatch: !process.env.GSD_HEADLESS,
829
837
  };
830
838
  },
831
839
  },
@@ -940,6 +948,7 @@ export const DISPATCH_RULES: DispatchRule[] = [
940
948
  structuredQuestionsAvailable,
941
949
  { headless: !!process.env.GSD_HEADLESS },
942
950
  ),
951
+ pauseAfterDispatch: !process.env.GSD_HEADLESS,
943
952
  };
944
953
  },
945
954
  },
@@ -1630,6 +1639,16 @@ export const DISPATCH_RULES: DispatchRule[] = [
1630
1639
  prompt: await buildCompleteMilestonePrompt(mid, midTitle, basePath),
1631
1640
  };
1632
1641
  }
1642
+ if (milestone) {
1643
+ const closeoutGate = checkCloseoutConsistencyGate(mid, { refreshFromDisk: true });
1644
+ if (!closeoutGate.ok) {
1645
+ return {
1646
+ action: "stop",
1647
+ reason: formatCloseoutConsistencyBlock(closeoutGate),
1648
+ level: "warning",
1649
+ };
1650
+ }
1651
+ }
1633
1652
  }
1634
1653
  return {
1635
1654
  action: "stop",
@@ -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";
@@ -2227,11 +2229,11 @@ export async function postUnitPostVerification(pctx: PostUnitContext): Promise<"
2227
2229
  // ── Post-unit hooks ──
2228
2230
  if (s.currentUnit && !s.stepMode) {
2229
2231
  const hookUnit = checkPostUnitHooks(s.currentUnit.type, s.currentUnit.id, s.basePath);
2232
+ persistHookState(s.basePath);
2230
2233
  if (hookUnit) {
2231
2234
  if (s.currentUnit) {
2232
2235
  await closeoutUnit(ctx, s.basePath, s.currentUnit.type, s.currentUnit.id, s.currentUnit.startedAt, buildSnapshotOpts(s.currentUnit.type, s.currentUnit.id));
2233
2236
  }
2234
- persistHookState(s.basePath);
2235
2237
 
2236
2238
  return enqueueSidecar(
2237
2239
  s, ctx,
@@ -2240,12 +2242,23 @@ export async function postUnitPostVerification(pctx: PostUnitContext): Promise<"
2240
2242
  );
2241
2243
  }
2242
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
+
2243
2255
  // Check if a hook requested a retry of the trigger unit
2244
2256
  if (isRetryPending()) {
2245
2257
  const trigger = consumeRetryTrigger();
2246
2258
  if (trigger) {
2259
+ persistHookState(s.basePath);
2247
2260
  ctx.ui.notify(
2248
- `Hook requested retry of ${trigger.unitType} ${trigger.unitId} — resetting task state.`,
2261
+ `Hook requested retry of ${trigger.unitType} ${trigger.unitId} — resetting trigger unit state.`,
2249
2262
  "info",
2250
2263
  );
2251
2264
 
@@ -2299,6 +2312,19 @@ export async function postUnitPostVerification(pctx: PostUnitContext): Promise<"
2299
2312
  // Fall through to normal dispatch — deriveState will re-derive the unit
2300
2313
  }
2301
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
+ }
2302
2328
  }
2303
2329
 
2304
2330
  // ── Fast-path stop detection (#3487) ──
@@ -45,6 +45,8 @@ import { classifyProject, type ProjectClassification } from "./detection.js";
45
45
  import { hasBrowserRequiredText } from "./browser-evidence.js";
46
46
  import { debugLog } from "./debug-logger.js";
47
47
  import { buildSkillActivationBlock, buildSkillDiscoveryVars } from "./skill-activation.js";
48
+ import { findMilestoneIds } from "./milestone-ids.js";
49
+ import { buildRunUatResultPresentation, RUN_UAT_TOOL_PRESENTATION_PLAN_ID } from "./tool-presentation-plan.js";
48
50
 
49
51
  export { buildSkillActivationBlock, buildSkillDiscoveryVars };
50
52
 
@@ -1428,7 +1430,7 @@ export async function checkNeedsRunUat(
1428
1430
  // If the UAT file already contains a verdict, UAT has been run — skip
1429
1431
  if (hasVerdict(uatContent)) continue;
1430
1432
  // Also check the ASSESSMENT file — the run-uat prompt writes the verdict
1431
- // there (via gsd_summary_save artifact_type:"ASSESSMENT"), not into the
1433
+ // there (via gsd_uat_result_save), not into the
1432
1434
  // UAT spec file. Without this check the unit re-dispatches indefinitely.
1433
1435
  const assessmentFile = resolveSliceFile(base, mid, sid, "ASSESSMENT");
1434
1436
  if (assessmentFile) {
@@ -1482,21 +1484,84 @@ export async function checkNeedsRunUat(
1482
1484
  * as a seed when present. The discussion agent interviews the user, writes
1483
1485
  * a full CONTEXT.md, and the phase transitions to pre-planning automatically.
1484
1486
  */
1487
+ export interface DiscussMilestonePromptOptions {
1488
+ headless?: boolean;
1489
+ commitInstruction?: string;
1490
+ fastPathInstruction?: string;
1491
+ includeDraftSeed?: boolean;
1492
+ includeContextMode?: boolean;
1493
+ }
1494
+
1495
+ export async function buildDiscussMilestoneInlinedContext(mid: string, base: string): Promise<string> {
1496
+ const inlined: string[] = [];
1497
+
1498
+ const roadmapInline = await inlineFileOptional(
1499
+ resolveMilestoneFile(base, mid, "ROADMAP"),
1500
+ relMilestoneFile(base, mid, "ROADMAP"),
1501
+ "Milestone Roadmap",
1502
+ );
1503
+ if (roadmapInline) inlined.push(roadmapInline);
1504
+
1505
+ const contextInline = await inlineFileOptional(
1506
+ resolveMilestoneFile(base, mid, "CONTEXT"),
1507
+ relMilestoneFile(base, mid, "CONTEXT"),
1508
+ "Milestone Context",
1509
+ );
1510
+ if (contextInline) inlined.push(contextInline);
1511
+
1512
+ const researchInline = await inlineFileOptional(
1513
+ resolveMilestoneFile(base, mid, "RESEARCH"),
1514
+ relMilestoneFile(base, mid, "RESEARCH"),
1515
+ "Milestone Research",
1516
+ );
1517
+ if (researchInline) inlined.push(researchInline);
1518
+
1519
+ const decisionsPath = resolveGsdRootFile(base, "DECISIONS");
1520
+ if (existsSync(decisionsPath)) {
1521
+ const decisionsContent = await loadFile(decisionsPath);
1522
+ if (decisionsContent) {
1523
+ inlined.push(`### Decisions Register\nSource: \`${relGsdRootFile("DECISIONS")}\`\n\n${decisionsContent.trim()}`);
1524
+ }
1525
+ }
1526
+
1527
+ const milestoneIds = findMilestoneIds(base);
1528
+ const currentIndex = milestoneIds.indexOf(mid);
1529
+ const priorMilestoneIds = currentIndex >= 0 ? milestoneIds.slice(0, currentIndex) : milestoneIds;
1530
+ for (const priorMid of priorMilestoneIds) {
1531
+ const summaryInline = await inlineFileOptional(
1532
+ resolveMilestoneFile(base, priorMid, "SUMMARY"),
1533
+ relMilestoneFile(base, priorMid, "SUMMARY"),
1534
+ `${priorMid} Prior Milestone Summary`,
1535
+ );
1536
+ if (summaryInline) inlined.push(summaryInline);
1537
+ }
1538
+
1539
+ return inlined.length > 0
1540
+ ? `## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}`
1541
+ : "## Inlined Context\n\n_(no milestone context files found yet — go in blind and ask broad questions)_";
1542
+ }
1543
+
1485
1544
  export async function buildDiscussMilestonePrompt(
1486
1545
  mid: string,
1487
1546
  midTitle: string,
1488
1547
  base: string,
1489
1548
  structuredQuestionsAvailable = "false",
1490
- { headless = false }: { headless?: boolean } = {},
1549
+ {
1550
+ headless = false,
1551
+ commitInstruction = "Do not commit planning artifacts — .gsd/ is managed externally.",
1552
+ fastPathInstruction = "",
1553
+ includeDraftSeed = true,
1554
+ includeContextMode = true,
1555
+ }: DiscussMilestonePromptOptions = {},
1491
1556
  ): Promise<string> {
1492
- const discussTemplates = inlineTemplate("context", "Context");
1557
+ const contextTemplate = inlineTemplate("context", "Context");
1493
1558
 
1494
1559
  if (headless) {
1495
1560
  const roadmapPath = resolveMilestoneFile(base, mid, "ROADMAP");
1496
1561
  const roadmapContent = roadmapPath ? await loadFile(roadmapPath) : null;
1497
1562
  return loadPrompt("discuss-headless", {
1498
1563
  seedContext: roadmapContent ?? "",
1499
- inlinedTemplates: discussTemplates,
1564
+ inlinedTemplates: contextTemplate,
1500
1565
  workingDirectory: base,
1501
1566
  milestoneId: mid,
1502
1567
  contextPath: relMilestoneFile(base, mid, "CONTEXT"),
@@ -1505,7 +1570,9 @@ export async function buildDiscussMilestonePrompt(
1505
1570
  });
1506
1571
  }
1507
1572
 
1508
- const contextModeInstructions = renderContextModeForPrompt("discuss-milestone", base);
1573
+ const rawInlinedContext = await buildDiscussMilestoneInlinedContext(mid, base);
1574
+ const cappedInlinedContext = capPreamble(rawInlinedContext);
1575
+ const discussTemplates = [cappedInlinedContext, contextTemplate].join("\n\n---\n\n");
1509
1576
 
1510
1577
  const basePrompt = loadPrompt("guided-discuss-milestone", {
1511
1578
  workingDirectory: base,
@@ -1513,20 +1580,22 @@ export async function buildDiscussMilestonePrompt(
1513
1580
  milestoneTitle: midTitle,
1514
1581
  inlinedTemplates: discussTemplates,
1515
1582
  structuredQuestionsAvailable,
1516
- commitInstruction: "Do not commit planning artifacts — .gsd/ is managed externally.",
1517
- fastPathInstruction: "",
1583
+ commitInstruction,
1584
+ fastPathInstruction,
1518
1585
  });
1519
- const promptWithContextMode = prependContextModeToBlock("discuss-milestone", base, basePrompt);
1586
+ const promptWithContextMode = includeContextMode
1587
+ ? prependContextModeToBlock("discuss-milestone", base, basePrompt)
1588
+ : basePrompt;
1520
1589
 
1521
1590
  // If a CONTEXT-DRAFT.md exists, append it as seed material
1522
1591
  const draftPath = resolveMilestoneFile(base, mid, "CONTEXT-DRAFT");
1523
1592
  const draftContent = draftPath ? await loadFile(draftPath) : null;
1524
1593
 
1525
- if (draftContent) {
1594
+ if (includeDraftSeed && draftContent) {
1526
1595
  return `${promptWithContextMode}\n\n## Prior Discussion (Draft Seed)\n\nThe following draft was captured from a prior multi-milestone discussion. Use it as seed material — the user has already provided this context. Start with a brief reflection on what the draft covers, then probe for any gaps or open questions before writing the full CONTEXT.md.\n\n${draftContent}`;
1527
1596
  }
1528
1597
 
1529
- return contextModeInstructions ? promptWithContextMode : basePrompt;
1598
+ return promptWithContextMode;
1530
1599
  }
1531
1600
 
1532
1601
  /**
@@ -2918,13 +2987,23 @@ export async function buildValidateMilestonePrompt(
2918
2987
  if (isDbAvailable()) {
2919
2988
  const milestone = getMilestone(mid);
2920
2989
  if (milestone) {
2990
+ const escapeCell = (value: string) =>
2991
+ value.replace(/[\\|]/g, (char) => `\\${char}`).replace(/\r?\n/g, " ");
2921
2992
  const classes: string[] = [];
2922
- if (milestone.verification_contract) classes.push(`- **Contract:** ${milestone.verification_contract}`);
2923
- if (milestone.verification_integration) classes.push(`- **Integration:** ${milestone.verification_integration}`);
2924
- if (milestone.verification_operational) classes.push(`- **Operational:** ${milestone.verification_operational}`);
2925
- if (milestone.verification_uat) classes.push(`- **UAT:** ${milestone.verification_uat}`);
2993
+ if (milestone.verification_contract) classes.push(`| Contract | ${escapeCell(milestone.verification_contract)} |`);
2994
+ if (milestone.verification_integration) classes.push(`| Integration | ${escapeCell(milestone.verification_integration)} |`);
2995
+ if (milestone.verification_operational) classes.push(`| Operational | ${escapeCell(milestone.verification_operational)} |`);
2996
+ if (milestone.verification_uat) classes.push(`| UAT | ${escapeCell(milestone.verification_uat)} |`);
2926
2997
  if (classes.length > 0) {
2927
- const verificationClasses = `### Verification Classes (from planning)\n\nThese verification tiers were defined during milestone planning. Each non-empty class must be checked for evidence during validation.\n\n${classes.join("\n")}`;
2998
+ const verificationClasses = [
2999
+ "### Verification Classes (from planning)",
3000
+ "",
3001
+ "These verification tiers were defined during milestone planning. Every row in this table must appear in `verificationClasses` with the same canonical class name.",
3002
+ "",
3003
+ "| Class | Planned Check |",
3004
+ "| --- | --- |",
3005
+ ...classes,
3006
+ ].join("\n");
2928
3007
  inlined.push(verificationClasses);
2929
3008
  trackPromptContext(contextTelemetry, "verification-classes", "inline", verificationClasses);
2930
3009
  }
@@ -3306,6 +3385,7 @@ export async function buildRunUatPrompt(
3306
3385
 
3307
3386
  const uatResultPath = join(base, relSliceFile(base, mid, sliceId, "ASSESSMENT"));
3308
3387
  const uatType = resolveEffectiveUatType(uatContent);
3388
+ const canonicalPresentation = JSON.stringify(buildRunUatResultPresentation(), null, 2);
3309
3389
 
3310
3390
  return loadPrompt("run-uat", {
3311
3391
  workingDirectory: base,
@@ -3314,6 +3394,8 @@ export async function buildRunUatPrompt(
3314
3394
  uatPath,
3315
3395
  uatResultPath,
3316
3396
  uatType,
3397
+ toolPresentationPlanId: RUN_UAT_TOOL_PRESENTATION_PLAN_ID,
3398
+ canonicalPresentation,
3317
3399
  inlinedContext,
3318
3400
  skillActivation: buildSkillActivationBlock({
3319
3401
  base,
@@ -54,6 +54,7 @@ import { isGsdWorktreePath } from "./worktree-root.js";
54
54
  import { resolveCanonicalMilestoneRoot } from "./worktree-manager.js";
55
55
  import { hasImplementationArtifacts } from "./milestone-implementation-evidence.js";
56
56
  import { loadAllCaptures, loadPendingCaptures } from "./captures.js";
57
+ import { checkCloseoutConsistencyGate } from "./closeout-consistency-gate.js";
57
58
 
58
59
  // Re-export so existing consumers of auto-recovery.ts keep working.
59
60
  export { resolveExpectedArtifactPath, diagnoseExpectedArtifact };
@@ -626,9 +627,8 @@ export function verifyExpectedArtifact(
626
627
  if (summaryOutcome === "failure") return false;
627
628
  const { milestone: mid } = parseUnitId(unitId);
628
629
  if (mid && isDbAvailable()) {
629
- const dbMilestone = getMilestone(mid);
630
- if (!dbMilestone) return false;
631
- if (!isClosedStatus(dbMilestone.status) && summaryOutcome !== "success") return false;
630
+ const closeoutGate = checkCloseoutConsistencyGate(mid, { refreshFromDisk: true });
631
+ if (!closeoutGate.ok) return false;
632
632
  }
633
633
  if (hasImplementationArtifacts(base, mid) === "absent") return false;
634
634
  }