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

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 +30 -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 +8 -8
  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 +8 -8
  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 -23
  176. package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
  177. package/packages/pi-ai/dist/models.generated.js +82 -31
  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 +72 -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 +73 -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 +410 -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 +63 -7
  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 → h4TGni4xJzlZjGkxaT6uU}/_buildManifest.js +0 -0
  329. /package/dist/web/standalone/.next/static/{eRWf-RI9bzbrwEurm_3uI → h4TGni4xJzlZjGkxaT6uU}/_ssgManifest.js +0 -0
@@ -1774,10 +1774,17 @@ export function createWiredAutoOrchestrationModule(ctx, pi, dispatchBasePath, ru
1774
1774
  async reconcileBeforeDispatch() {
1775
1775
  const activeBasePath = getLiveDispatchBasePath();
1776
1776
  const result = await reconcileBeforeDispatch(activeBasePath);
1777
- if (result.blockers.length > 0) {
1777
+ // Failure-path summaries written by gsd_summary_save create
1778
+ // artifact-db-status-divergence blockers for tasks that are still
1779
+ // pending (gsd_task_complete never ran). These tasks can still be
1780
+ // dispatched and the drift self-heals once they complete successfully.
1781
+ const hardBlockers = result.blockers.filter((b) => !b.includes("has SUMMARY artifact while DB status is") &&
1782
+ !b.includes("has SUMMARY on disk while DB status is") &&
1783
+ !b.includes("has task SUMMARY artifacts but no DB tasks"));
1784
+ if (hardBlockers.length > 0) {
1778
1785
  return {
1779
1786
  ok: false,
1780
- reason: result.blockers[0],
1787
+ reason: hardBlockers[0],
1781
1788
  stateSnapshot: result.stateSnapshot,
1782
1789
  };
1783
1790
  }
@@ -65,6 +65,9 @@ function readDetails(result) {
65
65
  }
66
66
  // eslint-disable-next-line @typescript-eslint/no-explicit-any -- result shape varies by tool
67
67
  function formatToolErrorText(result, details) {
68
+ if (typeof details?.displayReason === "string" && details.displayReason) {
69
+ return details.displayReason;
70
+ }
68
71
  const message = details?.error
69
72
  ?? result?.content?.find((entry) => entry.type === "text")?.text
70
73
  ?? "unknown";
@@ -405,6 +408,9 @@ export function registerDbTools(pi) {
405
408
  kind: StringEnum(["gsd_uat_exec", "gsd_exec", "screenshot", "log", "url", "browser"], { description: "Evidence kind" }),
406
409
  ref: Type.String({ description: "Evidence ID, approved .gsd path, or URL" }),
407
410
  note: Type.Optional(Type.String({ description: "Short evidence note" })),
411
+ unitType: Type.Optional(Type.String({ description: "Unit that produced the evidence" })),
412
+ tool: Type.Optional(Type.String({ description: "Tool that produced the evidence" })),
413
+ executionId: Type.Optional(Type.String({ description: "Stable execution or artifact id" })),
408
414
  });
409
415
  const uatCheck = Type.Object({
410
416
  id: Type.String({ description: "Stable check ID from the UAT spec" }),
@@ -416,17 +422,17 @@ export function registerDbTools(pi) {
416
422
  nonAutomatable: Type.Optional(Type.Boolean({ description: "True when the check is explicitly non-automatable" })),
417
423
  });
418
424
  const toolPresentationBlock = Type.Object({
419
- surface: StringEnum(["provider-tools", "claude-code-sdk", "mcp", "hybrid"], { description: "Tool presentation surface" }),
425
+ surface: Type.Optional(StringEnum(["provider-tools", "claude-code-sdk", "mcp", "hybrid"], { description: "Tool presentation surface" })),
420
426
  model: Type.Optional(Type.Object({
421
427
  provider: Type.Optional(Type.String()),
422
428
  api: Type.Optional(Type.String()),
423
429
  id: Type.Optional(Type.String()),
424
430
  })),
425
- presentedTools: Type.Array(Type.String(), { description: "Tool names actually presented to the model" }),
426
- blockedTools: Type.Array(Type.Object({
431
+ presentedTools: Type.Optional(Type.Array(Type.String(), { description: "Tool names actually presented to the model" })),
432
+ blockedTools: Type.Optional(Type.Array(Type.Object({
427
433
  name: Type.String(),
428
434
  reason: Type.String(),
429
- }), { description: "Tool names blocked from the model with reasons" }),
435
+ }), { description: "Tool names blocked from the model with reasons" })),
430
436
  aliases: Type.Optional(Type.Array(Type.Object({
431
437
  requested: Type.String(),
432
438
  canonical: Type.String(),
@@ -448,12 +454,12 @@ export function registerDbTools(pi) {
448
454
  "Do not use raw gsd_summary_save as a substitute for UAT results.",
449
455
  ],
450
456
  parameters: Type.Object({
451
- milestoneId: Type.String({ description: "Milestone ID (e.g. M001)" }),
452
- sliceId: Type.String({ description: "Slice ID (e.g. S01)" }),
453
- uatType: StringEnum(["artifact-driven", "browser-executable", "runtime-executable", "live-runtime", "mixed", "human-experience"], { description: "Declared UAT mode" }),
454
- verdict: StringEnum(["PASS", "FAIL", "PARTIAL"], { description: "Overall UAT verdict" }),
455
- checks: Type.Array(uatCheck, { description: "Structured check results" }),
456
- presentation: toolPresentationBlock,
457
+ milestoneId: Type.Optional(Type.String({ description: "Milestone ID (e.g. M001)" })),
458
+ sliceId: Type.Optional(Type.String({ description: "Slice ID (e.g. S01)" })),
459
+ uatType: Type.Optional(Type.String({ description: "Declared UAT mode" })),
460
+ verdict: Type.Optional(Type.String({ description: "Overall UAT verdict: PASS, FAIL, or PARTIAL" })),
461
+ checks: Type.Optional(Type.Array(uatCheck, { description: "Structured check results" })),
462
+ presentation: Type.Optional(toolPresentationBlock),
457
463
  notes: Type.Optional(Type.String({ description: "Overall verdict rationale" })),
458
464
  attempt: Type.Optional(Type.String({ description: "Attempt number or auto" })),
459
465
  previousAttemptId: Type.Optional(Type.String({ description: "Prior attempt ID, when retrying" })),
@@ -788,7 +794,7 @@ export function registerDbTools(pi) {
788
794
  recommendation: Type.String({ description: "Option id the executor recommends." }),
789
795
  recommendationRationale: Type.String({ description: "Why the recommendation — 1–2 sentences." }),
790
796
  continueWithDefault: Type.Boolean({
791
- description: "When true, loop continues (artifact logged for later review). When false, auto-mode pauses until the user resolves via /gsd escalate resolve.",
797
+ description: "When true, the recommendation is recorded as the default, but auto-mode still pauses until the user resolves via /gsd escalate resolve.",
792
798
  }),
793
799
  }, { description: "ADR-011 Phase 2: optional escalation payload. Only honored when phases.mid_execution_escalation is true." })),
794
800
  verificationEvidence: Type.Optional(Type.Array(Type.Object({
@@ -829,7 +835,7 @@ export function registerDbTools(pi) {
829
835
  sliceTitle: Type.String({ description: "Title of the slice" }),
830
836
  oneLiner: Type.String({ description: "One-line summary of what the slice accomplished" }),
831
837
  narrative: Type.String({ description: "Detailed narrative of what happened across all tasks" }),
832
- verification: Type.String({ description: "What was verified across all tasks" }),
838
+ verification: Type.Optional(Type.String({ description: "What was verified across all tasks — if omitted, summary records verification as passed without detail." })),
833
839
  uatContent: Type.String({ description: "UAT test content (markdown body)" }),
834
840
  // ── Enrichment metadata (optional — defaults to empty) ────────────
835
841
  deviations: Type.Optional(Type.String({ description: "Deviations from the slice plan, or 'None.'" })),
@@ -1010,7 +1016,7 @@ export function registerDbTools(pi) {
1010
1016
  promptGuidelines: [
1011
1017
  "Use gsd_validate_milestone when all slices are done and the milestone needs validation before completion.",
1012
1018
  "Parameters: milestoneId, verdict, remediationRound, successCriteriaChecklist, sliceDeliveryAudit, crossSliceIntegration, requirementCoverage, verificationClasses (optional), verdictRationale, remediationPlan (optional).",
1013
- "If verification classes were planned, verificationClasses must include canonical class rows using the exact class names Contract, Integration, Operational, and UAT when present in planning.",
1019
+ "If verification classes were planned, verificationClasses must be a complete canonical table with one row for every applicable planned class using the exact class names Contract, Integration, Operational, and UAT. Do not submit a partial table.",
1014
1020
  "Planned verification text marked as none/not required/not applicable/N/A (including suffixed variants such as 'not required - backend-only') is treated as not applicable and does not require a class row.",
1015
1021
  "If verdict is 'needs-remediation', also provide remediationPlan and use gsd_reassess_roadmap to add remediation slices to the roadmap.",
1016
1022
  "On success, returns validationPath where VALIDATION.md was written.",
@@ -1023,7 +1029,7 @@ export function registerDbTools(pi) {
1023
1029
  sliceDeliveryAudit: Type.String({ description: "Markdown table auditing each slice's claimed vs delivered output" }),
1024
1030
  crossSliceIntegration: Type.String({ description: "Markdown describing any cross-slice boundary mismatches" }),
1025
1031
  requirementCoverage: Type.String({ description: "Markdown describing any unaddressed requirements" }),
1026
- verificationClasses: Type.Optional(Type.String({ description: "Markdown describing verification class compliance and gaps using canonical class names (Contract, Integration, Operational, UAT) for each applicable planned class" })),
1032
+ verificationClasses: Type.Optional(Type.String({ description: "Complete markdown table describing verification class compliance and gaps; include one canonical row for every applicable planned class (Contract, Integration, Operational, UAT)" })),
1027
1033
  verdictRationale: Type.String({ description: "Why this verdict was chosen" }),
1028
1034
  remediationPlan: Type.Optional(Type.String({ description: "Remediation plan (required if verdict is needs-remediation)" })),
1029
1035
  }),
@@ -11,7 +11,7 @@ import { canonicalToolName, clearDiscussionFlowState, isDepthConfirmationAnswer,
11
11
  import { resolveManifest } from "../unit-context-manifest.js";
12
12
  import { isBlockedStateFile, isBashWriteToStateFile, BLOCKED_WRITE_ERROR } from "../write-intercept.js";
13
13
  import { loadFile, saveFile, formatContinue } from "../files.js";
14
- import { clearToolInvocationError, getAutoRuntimeSnapshot, isAutoActive, isAutoPaused, markToolEnd, markToolStart, recordToolInvocationError } from "../auto-runtime-state.js";
14
+ import { clearToolInvocationError, getAutoRuntimeSnapshot, isAutoActive, isAutoCompletionStopInProgress, isAutoPaused, markToolEnd, markToolStart, recordToolInvocationError } from "../auto-runtime-state.js";
15
15
  import { checkToolCallLoop, resetToolCallLoopGuard } from "./tool-call-loop-guard.js";
16
16
  import { maybePauseAutoForApprovalGate, resetPendingGatePauseGuard } from "./pending-gate-pause.js";
17
17
  import { saveActivityLog } from "../activity-log.js";
@@ -30,7 +30,7 @@ import { getGuidedUnitContext } from "../guided-unit-context.js";
30
30
  import { registerPlanMilestoneSchemaRecovery } from "./plan-milestone-schema-recovery.js";
31
31
  import { AUTO_UNIT_SCOPED_TOOLS, RUN_UAT_BROWSER_TOOL_NAMES, isWorkflowAliasTool } from "../auto-unit-tool-scope.js";
32
32
  import { filterToolsForProvider } from "../model-router.js";
33
- import { RUN_UAT_WORKFLOW_TOOL_NAMES } from "../tool-presentation-plan.js";
33
+ import { RUN_UAT_READ_ONLY_TOOL_NAMES, RUN_UAT_WORKFLOW_TOOL_NAMES } from "../tool-presentation-plan.js";
34
34
  let approvalQuestionAbortInFlight = false;
35
35
  async function loadWelcomeScreenModule() {
36
36
  const candidates = [];
@@ -204,7 +204,12 @@ export function buildMinimalAutoGsdToolSet(activeToolNames, unitType, registered
204
204
  return withPreservedShimTools([...new Set([...preserved, ...scoped])]);
205
205
  }
206
206
  export function buildRunUatGsdToolSet(activeToolNames, registeredToolNames = activeToolNames) {
207
- const scoped = resolveScopedToolNames([...activeToolNames, ...registeredToolNames], [...RUN_UAT_WORKFLOW_TOOL_NAMES, "subagent", ...RUN_UAT_BROWSER_TOOL_NAMES]);
207
+ const scoped = resolveScopedToolNames([...activeToolNames, ...registeredToolNames], [
208
+ ...RUN_UAT_WORKFLOW_TOOL_NAMES,
209
+ ...RUN_UAT_READ_ONLY_TOOL_NAMES,
210
+ "subagent",
211
+ ...RUN_UAT_BROWSER_TOOL_NAMES,
212
+ ]);
208
213
  return [...new Set(scoped)];
209
214
  }
210
215
  export function buildMinimalGsdWorkflowToolSet(activeToolNames, registeredToolNames = activeToolNames) {
@@ -382,6 +387,11 @@ function isContextDraftSummarySave(toolName, input) {
382
387
  return false;
383
388
  return input.artifact_type === "CONTEXT-DRAFT";
384
389
  }
390
+ function withDepthGateDisplayReason(result, displayReason = "Depth confirmation is waiting for your answer.") {
391
+ if (!result.block)
392
+ return result;
393
+ return { ...result, displayReason };
394
+ }
385
395
  function shouldBlockDeferredApprovalTool(toolName, input, basePath) {
386
396
  if (deferredApprovalGate?.basePath !== basePath)
387
397
  return { block: false };
@@ -389,14 +399,14 @@ function shouldBlockDeferredApprovalTool(toolName, input, basePath) {
389
399
  return { block: false };
390
400
  if (isContextDraftSummarySave(toolName, input))
391
401
  return { block: false };
392
- return {
402
+ return withDepthGateDisplayReason({
393
403
  block: true,
394
404
  reason: [
395
405
  `HARD BLOCK: Approval question "${deferredApprovalGate.gateId}" has been shown to the user.`,
396
406
  `Only CONTEXT-DRAFT persistence may finish in this same assistant turn.`,
397
407
  `Wait for the user's answer before calling additional tools.`,
398
408
  ].join(" "),
399
- };
409
+ });
400
410
  }
401
411
  export function resolveNotificationStoreBasePath(basePath) {
402
412
  return resolveWorktreeProjectRoot(basePath);
@@ -424,8 +434,9 @@ export function registerHooks(pi, ecosystemHandlers) {
424
434
  registerPlanMilestoneSchemaRecovery(pi);
425
435
  pi.on("session_start", async (_event, ctx) => {
426
436
  const basePath = contextBasePath(ctx);
437
+ const preserveCloseoutSurface = isAutoCompletionStopInProgress();
427
438
  initSessionNotifications(ctx);
428
- if (!isAutoActive()) {
439
+ if (!isAutoActive() && !preserveCloseoutSurface) {
429
440
  const { initHealthWidget } = await import("../health-widget.js");
430
441
  initHealthWidget(ctx);
431
442
  }
@@ -445,14 +456,17 @@ export function registerHooks(pi, ecosystemHandlers) {
445
456
  process.env.GSD_SHOW_TOKEN_COST = prefs?.preferences.show_token_cost ? "1" : "";
446
457
  }
447
458
  catch { /* non-fatal */ }
448
- await installWelcomeHeader(ctx);
459
+ if (!preserveCloseoutSurface) {
460
+ await installWelcomeHeader(ctx);
461
+ }
449
462
  await loadToolApiKeysForSession();
450
- if (isAutoActive()) {
463
+ if (isAutoActive() || preserveCloseoutSurface) {
451
464
  ctx.ui.setWidget("gsd-health", undefined);
452
465
  }
453
466
  });
454
467
  pi.on("session_switch", async (_event, ctx) => {
455
468
  const basePath = contextBasePath(ctx);
469
+ const preserveCloseoutSurface = isAutoCompletionStopInProgress();
456
470
  initSessionNotifications(ctx);
457
471
  resetWriteGateState(basePath);
458
472
  resetToolCallLoopGuard();
@@ -464,7 +478,7 @@ export function registerHooks(pi, ecosystemHandlers) {
464
478
  await applyCompactionThresholdOverride(ctx);
465
479
  await prepareWorkflowMcpForHookContext(ctx, basePath);
466
480
  await loadToolApiKeysForSession();
467
- if (!isAutoActive()) {
481
+ if (!isAutoActive() && !preserveCloseoutSurface) {
468
482
  ctx.ui.setWidget("gsd-progress", undefined);
469
483
  ctx.ui.setWidget("gsd-outcome", undefined);
470
484
  const { initHealthWidget } = await import("../health-widget.js");
@@ -750,7 +764,7 @@ export function registerHooks(pi, ecosystemHandlers) {
750
764
  if (ctx) {
751
765
  await maybePauseAutoForApprovalGate(ctx, pi, true, "Depth confirmation is waiting for your answer — pausing auto-mode.");
752
766
  }
753
- return bashGuard;
767
+ return withDepthGateDisplayReason(bashGuard);
754
768
  }
755
769
  }
756
770
  else {
@@ -759,7 +773,7 @@ export function registerHooks(pi, ecosystemHandlers) {
759
773
  if (ctx) {
760
774
  await maybePauseAutoForApprovalGate(ctx, pi, true, "Depth confirmation is waiting for your answer — pausing auto-mode.");
761
775
  }
762
- return gateGuard;
776
+ return withDepthGateDisplayReason(gateGuard);
763
777
  }
764
778
  }
765
779
  }
@@ -843,8 +857,9 @@ export function registerHooks(pi, ecosystemHandlers) {
843
857
  if (!isToolCallEventType("write", event))
844
858
  return;
845
859
  const result = shouldBlockContextWrite(event.toolName, event.input.path, await getDiscussionMilestoneIdFor(discussionBasePath), isQueuePhaseActive(discussionBasePath), discussionBasePath);
846
- if (result.block)
847
- return result;
860
+ if (result.block) {
861
+ return withDepthGateDisplayReason(result, "Depth check required before writing milestone context.");
862
+ }
848
863
  });
849
864
  // ── Safety harness: evidence collection + destructive command blocking ──
850
865
  pi.on("tool_call", async (event, ctx) => {
@@ -2,7 +2,7 @@
2
2
  import { copyFileSync, existsSync, lstatSync, mkdirSync, readFileSync, readlinkSync, realpathSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
3
3
  import { isAbsolute, join, relative, resolve, sep } from "node:path";
4
4
  import { minimatch } from "minimatch";
5
- import { shouldBlockAutoUnitToolCall } from "../auto-unit-tool-scope.js";
5
+ import { GSD_PHASE_SCOPE_DISPLAY_REASON, shouldBlockAutoUnitToolCall } from "../auto-unit-tool-scope.js";
6
6
  import { getIsolationMode } from "../preferences.js";
7
7
  import { compileSubagentPermissionContract } from "../unit-context-manifest.js";
8
8
  import { logWarning } from "../workflow-logger.js";
@@ -643,6 +643,13 @@ function blockReason(unitType, mode, what) {
643
643
  `the work belongs in execute-task, not in a planning unit.`,
644
644
  ].join(" ");
645
645
  }
646
+ function planningBlock(unitType, mode, what) {
647
+ return {
648
+ block: true,
649
+ reason: blockReason(unitType, mode, what),
650
+ displayReason: GSD_PHASE_SCOPE_DISPLAY_REASON,
651
+ };
652
+ }
646
653
  /**
647
654
  * Planning-unit tool-policy enforcement. Returns { block } per the policy
648
655
  * resolved from the active unit's manifest:
@@ -690,10 +697,10 @@ export function shouldBlockPlanningUnit(toolName, pathOrCommand, basePath, unitT
690
697
  if (tool.startsWith("gsd_"))
691
698
  return { block: false };
692
699
  if (PLANNING_WRITE_TOOLS.has(tool) || tool === "bash" || PLANNING_SUBAGENT_TOOLS.has(tool)) {
693
- return { block: true, reason: blockReason(unitType, policy.mode, `${tool} is not permitted (read-only)`) };
700
+ return planningBlock(unitType, policy.mode, `${tool} is not permitted (read-only)`);
694
701
  }
695
702
  // Unknown tool in read-only mode — block by default.
696
- return { block: true, reason: blockReason(unitType, policy.mode, `tool "${tool}" is not on the read-only allowlist`) };
703
+ return planningBlock(unitType, policy.mode, `tool "${tool}" is not on the read-only allowlist`);
697
704
  }
698
705
  // planning / planning-dispatch / docs / verification modes share the same surface for safe tools, bash, and subagent.
699
706
  if (PLANNING_SAFE_TOOLS.has(tool))
@@ -711,10 +718,7 @@ export function shouldBlockPlanningUnit(toolName, pathOrCommand, basePath, unitT
711
718
  // instead of silently bypassing the gate.
712
719
  if (agentClasses === undefined) {
713
720
  warnMissingControlledDispatchAgentClasses(unitType, policy.mode, tool);
714
- return {
715
- block: true,
716
- reason: blockReason(unitType, policy.mode, `subagent dispatch blocked: stale caller did not supply agent identities for "${tool}"; update extractSubagentAgentClasses to handle this input shape`),
717
- };
721
+ return planningBlock(unitType, policy.mode, `subagent dispatch blocked: stale caller did not supply agent identities for "${tool}"; update extractSubagentAgentClasses to handle this input shape`);
718
722
  }
719
723
  // agentClasses was explicitly provided but resolved to an empty list (for
720
724
  // example, a bare tool call with no agent field). Pass through; no agents
@@ -724,41 +728,29 @@ export function shouldBlockPlanningUnit(toolName, pathOrCommand, basePath, unitT
724
728
  }
725
729
  const globallyDisallowed = requested.find(a => !isReadOnlySpecialist(a));
726
730
  if (globallyDisallowed) {
727
- return {
728
- block: true,
729
- reason: blockReason(unitType, policy.mode, `subagent dispatch of "${globallyDisallowed}" not permitted; only read-only specialists (${allowedPlanningDispatchAgentsList()}) may be dispatched from ${policy.mode} units`),
730
- };
731
+ return planningBlock(unitType, policy.mode, `subagent dispatch of "${globallyDisallowed}" not permitted; only read-only specialists (${allowedPlanningDispatchAgentsList()}) may be dispatched from ${policy.mode} units`);
731
732
  }
732
733
  const disallowedByPolicy = requested.find(a => !allowed.has(a));
733
734
  if (disallowedByPolicy) {
734
- return {
735
- block: true,
736
- reason: blockReason(unitType, policy.mode, `subagent dispatch of "${disallowedByPolicy}" not permitted by ToolsPolicy.allowedSubagents; permitted agents for this unit: ${allowedSubagents.join(", ")}`),
737
- };
735
+ return planningBlock(unitType, policy.mode, `subagent dispatch of "${disallowedByPolicy}" not permitted by ToolsPolicy.allowedSubagents; permitted agents for this unit: ${allowedSubagents.join(", ")}`);
738
736
  }
739
737
  return { block: false };
740
738
  }
741
- return { block: true, reason: blockReason(unitType, policy.mode, `subagent dispatch is not permitted in planning units`) };
739
+ return planningBlock(unitType, policy.mode, "subagent dispatch is not permitted in planning units");
742
740
  }
743
741
  if (tool === "bash") {
744
742
  if (policy.mode === "verification") {
745
743
  if (BASH_VERIFICATION_RE.test(pathOrCommand) || BASH_READ_ONLY_RE.test(pathOrCommand))
746
744
  return { block: false };
747
- return {
748
- block: true,
749
- reason: blockReason(unitType, policy.mode, `bash is restricted to build/test verification commands (npm run build, npm test, etc.); cannot run "${pathOrCommand.slice(0, 80)}${pathOrCommand.length > 80 ? "…" : ""}"`),
750
- };
745
+ return planningBlock(unitType, policy.mode, `bash is restricted to build/test verification commands (npm run build, npm test, etc.); cannot run "${pathOrCommand.slice(0, 80)}${pathOrCommand.length > 80 ? "…" : ""}"`);
751
746
  }
752
747
  if (BASH_READ_ONLY_RE.test(pathOrCommand))
753
748
  return { block: false };
754
- return {
755
- block: true,
756
- reason: blockReason(unitType, policy.mode, `bash is restricted to read-only commands (cat/grep/git log/etc); cannot run "${pathOrCommand.slice(0, 80)}${pathOrCommand.length > 80 ? "…" : ""}"`),
757
- };
749
+ return planningBlock(unitType, policy.mode, `bash is restricted to read-only commands (cat/grep/git log/etc); cannot run "${pathOrCommand.slice(0, 80)}${pathOrCommand.length > 80 ? "…" : ""}"`);
758
750
  }
759
751
  if (PLANNING_WRITE_TOOLS.has(tool)) {
760
752
  if (!pathOrCommand) {
761
- return { block: true, reason: blockReason(unitType, policy.mode, `${tool} called with empty path`) };
753
+ return planningBlock(unitType, policy.mode, `${tool} called with empty path`);
762
754
  }
763
755
  const absPath = isAbsolute(pathOrCommand) ? pathOrCommand : resolve(basePath, pathOrCommand);
764
756
  // Always allow .gsd/ writes — that's where planning artifacts live.
@@ -768,10 +760,7 @@ export function shouldBlockPlanningUnit(toolName, pathOrCommand, basePath, unitT
768
760
  if (policy.mode === "docs" && matchesAllowedGlob(absPath, basePath, policy.allowedPathGlobs)) {
769
761
  return { block: false };
770
762
  }
771
- return {
772
- block: true,
773
- reason: blockReason(unitType, policy.mode, `cannot ${tool} "${pathOrCommand}" — writes are restricted to .gsd/${policy.mode === "docs" ? " and " + policy.allowedPathGlobs.join(", ") : ""}`),
774
- };
763
+ return planningBlock(unitType, policy.mode, `cannot ${tool} "${pathOrCommand}" — writes are restricted to .gsd/${policy.mode === "docs" ? " and " + policy.allowedPathGlobs.join(", ") : ""}`);
775
764
  }
776
765
  // Unknown tool name — pass through. Other layers (queue, pending-gate,
777
766
  // CONTEXT.md write) catch known mutating shapes; defaulting to allow here
@@ -1,17 +1,44 @@
1
1
  // Project/App: gsd-pi
2
2
  // File Purpose: Shared browser-observable UAT requirement and evidence detection.
3
- export const BROWSER_REQUIREMENT_RE = /\b(?:browser|file:\/\/|localhost|dom|localstorage|click(?:ing|ed)?|button|screenshot|snapshot|reload(?:ed)?|page refresh|user-visible|strikethrough|search box)\b/i;
3
+ export const BROWSER_REQUIREMENT_RE = /\b(?:file:\/\/|localhost|playwright|chrome|screenshot|snapshot|browser_(?:assert|batch|find|verify|snapshot_refs))\b|\b(?:open|launch|navigate|load|visit|serve|start)\b.{0,80}\b(?:browser|page|localhost|file:\/\/)\b|\bbrowser\s+(?:check|session|test|uat|tool|automation|interaction|flow)\b/i;
4
4
  export const NO_BROWSER_EVIDENCE_RE = /\b(?:no|without|not|wasn'?t|isn'?t)\s+(?:automated\s+)?(?:live\s+)?browser(?:\s+(?:session|test|uat))?|\bno\s+automated\s+browser\b|\bnot\s+conducted\b/i;
5
5
  export const BROWSER_RUNTIME_RE = /\b(?:browser|playwright|chrome|camoufox|browser_(?:assert|batch|find|verify|snapshot_refs)|screenshot|snapshot|file:\/\/|localhost)\b/i;
6
6
  export const BROWSER_ACTION_RE = /\b(?:open(?:ed)?|navigate(?:d)?|click(?:ed)?|type(?:d)?|reload(?:ed)?|capture(?:d)?|screenshot|snapshot)\b/i;
7
7
  export const BROWSER_ASSERTION_RE = /\b(?:assert(?:ed|ion)?|observed|confirmed|verified|expected|visible|text|count|label|strikethrough|localstorage|screenshot|snapshot|passed)\b/i;
8
+ const NON_REQUIREMENT_BROWSER_HEADING_RE = /^(?:not\s+proven|not\s+covered|out\s+of\s+scope|deferred|follow-?ups?|known\s+limitations|notes\s+for\s+tester)\b/i;
9
+ const NON_REQUIREMENT_BROWSER_LINE_RE = /\b(?:deferred|not\s+proven|not\s+covered|out\s+of\s+scope|future\s+slice|follow-?up|no\s+(?:live\s+)?browser|without\s+(?:a\s+)?browser|not\s+(?:a\s+)?browser)\b/i;
8
10
  export function compactTextParts(parts) {
9
11
  return parts.flatMap((part) => Array.isArray(part) ? part : [part])
10
12
  .filter((part) => typeof part === "string" && part.trim().length > 0)
11
13
  .join("\n");
12
14
  }
13
15
  export function hasBrowserRequiredText(text) {
14
- return BROWSER_REQUIREMENT_RE.test(text);
16
+ let inNonRequirementSection = false;
17
+ let nonRequirementDepth = 0;
18
+ for (const line of text.split(/\r?\n/)) {
19
+ const headingMatch = line.match(/^(#{1,6})\s+(.+?)\s*$/);
20
+ if (headingMatch) {
21
+ const depth = headingMatch[1].length;
22
+ const title = headingMatch[2] ?? "";
23
+ // Only update section context when at the same or higher level than the
24
+ // heading that opened the non-requirement zone. A sub-heading deeper than
25
+ // the opening heading must not escape or re-enter the zone on its own.
26
+ if (!inNonRequirementSection || depth <= nonRequirementDepth) {
27
+ inNonRequirementSection = NON_REQUIREMENT_BROWSER_HEADING_RE.test(title);
28
+ nonRequirementDepth = inNonRequirementSection ? depth : 0;
29
+ }
30
+ // Check the heading title itself — section state is already updated, so
31
+ // we correctly skip headings that opened a non-requirement zone.
32
+ if (!inNonRequirementSection && BROWSER_REQUIREMENT_RE.test(title))
33
+ return true;
34
+ continue;
35
+ }
36
+ if (inNonRequirementSection || NON_REQUIREMENT_BROWSER_LINE_RE.test(line))
37
+ continue;
38
+ if (BROWSER_REQUIREMENT_RE.test(line))
39
+ return true;
40
+ }
41
+ return false;
15
42
  }
16
43
  export function hasBrowserEvidenceText(text) {
17
44
  if (!text.trim())
@@ -0,0 +1,61 @@
1
+ // Project/App: gsd-pi
2
+ // File Purpose: Shared DB-backed guard for milestone closeout finalization.
3
+ import { getDbPath, getLatestAssessmentByScope, getMilestone, getMilestoneSlices, getPendingGates, getSliceTasks, isDbAvailable, refreshOpenDatabaseFromDisk, } from "./gsd-db.js";
4
+ import { isClosedStatus } from "./status-guards.js";
5
+ export const CLOSEOUT_CONSISTENCY_BLOCKED_REASON = "closeout-consistency-blocked";
6
+ function blocked(reason, message) {
7
+ return {
8
+ ok: false,
9
+ reason,
10
+ recoveryReason: CLOSEOUT_CONSISTENCY_BLOCKED_REASON,
11
+ message,
12
+ };
13
+ }
14
+ function isFileBackedDbPath(path) {
15
+ return Boolean(path && path !== ":memory:");
16
+ }
17
+ export function checkCloseoutConsistencyGate(milestoneId, options = {}) {
18
+ if (!isDbAvailable()) {
19
+ return blocked("db-unavailable", `Closeout consistency blocked for ${milestoneId}: canonical DB is unavailable.`);
20
+ }
21
+ if (options.refreshFromDisk && isFileBackedDbPath(getDbPath()) && !refreshOpenDatabaseFromDisk()) {
22
+ return blocked("db-refresh-failed", `Closeout consistency blocked for ${milestoneId}: canonical DB refresh failed.`);
23
+ }
24
+ const milestone = getMilestone(milestoneId);
25
+ if (!milestone) {
26
+ return blocked("milestone-missing", `Closeout consistency blocked for ${milestoneId}: milestone is missing from canonical DB.`);
27
+ }
28
+ if (!isClosedStatus(milestone.status)) {
29
+ return blocked("milestone-open", `Closeout consistency blocked for ${milestoneId}: canonical DB milestone status is "${milestone.status}".`);
30
+ }
31
+ if (milestone.status !== "skipped") {
32
+ const validation = getLatestAssessmentByScope(milestoneId, "milestone-validation");
33
+ if (validation?.status !== "pass") {
34
+ return blocked("validation-not-pass", `Closeout consistency blocked for ${milestoneId}: latest milestone validation is "${validation?.status ?? "absent"}".`);
35
+ }
36
+ }
37
+ const slices = getMilestoneSlices(milestoneId);
38
+ if (slices.length === 0 && milestone.status !== "skipped") {
39
+ return blocked("slice-missing", `Closeout consistency blocked for ${milestoneId}: no slices exist in canonical DB.`);
40
+ }
41
+ for (const slice of slices) {
42
+ if (!isClosedStatus(slice.status)) {
43
+ return blocked("slice-open", `Closeout consistency blocked for ${milestoneId}: slice ${slice.id} status is "${slice.status}".`);
44
+ }
45
+ for (const task of getSliceTasks(milestoneId, slice.id)) {
46
+ if (!isClosedStatus(task.status)) {
47
+ return blocked("task-open", `Closeout consistency blocked for ${milestoneId}: task ${slice.id}/${task.id} status is "${task.status}".`);
48
+ }
49
+ }
50
+ const pendingGate = getPendingGates(milestoneId, slice.id)[0];
51
+ if (pendingGate) {
52
+ return blocked("quality-gate-pending", `Closeout consistency blocked for ${milestoneId}: quality gate ${pendingGate.gate_id} is still pending for ${slice.id}.`);
53
+ }
54
+ }
55
+ return { ok: true };
56
+ }
57
+ export function formatCloseoutConsistencyBlock(result) {
58
+ if (result.ok)
59
+ return "";
60
+ return `${result.message} Recovery reason: ${result.recoveryReason}. Resolve the canonical DB state and run /gsd auto to retry.`;
61
+ }
@@ -266,8 +266,8 @@ Examples:
266
266
  await handleInspect(ctx);
267
267
  return true;
268
268
  }
269
- if (trimmed === "update" || trimmed === "upgrade") {
270
- await handleUpdate(ctx);
269
+ if (trimmed === "update" || trimmed.startsWith("update ") || trimmed === "upgrade" || trimmed.startsWith("upgrade ")) {
270
+ await handleUpdate(ctx, trimmed.replace(/^(?:update|upgrade)\s*/, "").trim());
271
271
  return true;
272
272
  }
273
273
  if (trimmed === "fast" || trimmed.startsWith("fast ")) {
@@ -5,6 +5,8 @@
5
5
  * handleRunHook, handleUpdate, handleSkillHealth
6
6
  */
7
7
  import { existsSync, readFileSync, mkdirSync } from "node:fs";
8
+ import { execFileSync } from "node:child_process";
9
+ import { createRequire } from "node:module";
8
10
  import { join, resolve as resolvePath, sep } from "node:path";
9
11
  import { homedir } from "node:os";
10
12
  import { deriveState } from "./state.js";
@@ -20,7 +22,10 @@ import { loadPrompt } from "./prompt-loader.js";
20
22
  import { isPnpmInstall } from "../../shared/package-manager-detection.js";
21
23
  import { buildDoctorHealIssuePayload, buildDoctorHealSummary, buildWorkflowDispatchContent, } from "./workflow-protocol.js";
22
24
  import { restoreGsdWorkflowTools, scopeGsdWorkflowToolsForDispatch, } from "./bootstrap/register-hooks.js";
25
+ const GSD_PI_PACKAGE = "@opengsd/gsd-pi";
26
+ const GSD_BROWSER_PACKAGE = "@opengsd/gsd-browser";
23
27
  const UPDATE_REGISTRY_URL = "https://registry.npmjs.org/@opengsd%2fgsd-pi/latest";
28
+ const BROWSER_UPDATE_REGISTRY_URL = "https://registry.npmjs.org/@opengsd%2fgsd-browser/latest";
24
29
  const UPDATE_FETCH_TIMEOUT_MS = 5000;
25
30
  // Detects a bun-installed gsd via `process.argv[1]`. Mirrors isBunInstall in
26
31
  // src/update-check.ts — duplicated because tsconfig.resources.json rootDir
@@ -47,11 +52,11 @@ function resolveInstallCommand(pkg) {
47
52
  return `pnpm add -g ${pkg}`;
48
53
  return `npm install -g ${pkg}`;
49
54
  }
50
- async function fetchLatestVersionForCommand() {
55
+ async function fetchLatestVersionForCommand(registryUrl = UPDATE_REGISTRY_URL) {
51
56
  const controller = new AbortController();
52
57
  const timeout = setTimeout(() => controller.abort(), UPDATE_FETCH_TIMEOUT_MS);
53
58
  try {
54
- const res = await fetch(UPDATE_REGISTRY_URL, { signal: controller.signal });
59
+ const res = await fetch(registryUrl, { signal: controller.signal });
55
60
  if (!res.ok)
56
61
  return null;
57
62
  const data = (await res.json());
@@ -65,6 +70,19 @@ async function fetchLatestVersionForCommand() {
65
70
  clearTimeout(timeout);
66
71
  }
67
72
  }
73
+ function resolveInstalledPackageVersionForCommand(packageName) {
74
+ try {
75
+ const requireFromHere = createRequire(import.meta.url);
76
+ const packageJsonPath = requireFromHere.resolve(`${packageName}/package.json`);
77
+ const pkg = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
78
+ return typeof pkg.version === "string" && pkg.version.trim().length > 0
79
+ ? pkg.version.trim().replace(/^v/, "")
80
+ : null;
81
+ }
82
+ catch {
83
+ return null;
84
+ }
85
+ }
68
86
  export function dispatchDoctorHeal(pi, scope, reportText, structuredIssues) {
69
87
  const workflowPath = process.env.GSD_WORKFLOW_PATH ?? join(gsdHome(), "agent", "GSD-WORKFLOW.md");
70
88
  const workflow = readFileSync(workflowPath, "utf-8");
@@ -386,27 +404,74 @@ function compareSemverLocal(a, b) {
386
404
  }
387
405
  return 0;
388
406
  }
389
- export async function handleUpdate(ctx) {
407
+ function formatCommandVersion(version) {
408
+ return version ? `v${version}` : "unknown";
409
+ }
410
+ function pickHigherVersionForCommand(a, b) {
411
+ if (!a)
412
+ return b;
413
+ if (!b)
414
+ return a;
415
+ return compareSemverLocal(a, b) >= 0 ? a : b;
416
+ }
417
+ // Mirrors resolveGsdBrowserPathVersion in src/update-check.ts — duplicated because
418
+ // tsconfig.resources.json rootDir prevents importing from src/.
419
+ function resolveGsdBrowserPathVersionForCommand(env = process.env) {
420
+ const explicit = env.GSD_BROWSER_PATH_VERSION?.trim();
421
+ if (explicit)
422
+ return explicit.match(/\b(\d+\.\d+\.\d+)\b/)?.[1] ?? null;
423
+ try {
424
+ const out = execFileSync("gsd-browser", ["--version"], {
425
+ encoding: "utf-8",
426
+ env,
427
+ stdio: ["ignore", "pipe", "ignore"],
428
+ timeout: 2000,
429
+ });
430
+ return out.match(/\b(\d+\.\d+\.\d+)\b/)?.[1] ?? null;
431
+ }
432
+ catch {
433
+ return null;
434
+ }
435
+ }
436
+ export async function handleUpdate(ctx, args = "") {
390
437
  const { execSync } = await import("node:child_process");
391
- const NPM_PACKAGE = "@opengsd/gsd-pi";
392
- const current = process.env.GSD_VERSION || "0.0.0";
393
- ctx.ui.notify(`Current version: v${current}\nChecking npm registry...`, "info");
394
- const latest = await fetchLatestVersionForCommand();
438
+ const target = args.trim();
439
+ const browserUpdate = target === "browser" || target === "gsd-browser";
440
+ if (target && !browserUpdate) {
441
+ ctx.ui.notify("Usage: /gsd update [browser]", "warning");
442
+ return;
443
+ }
444
+ const NPM_PACKAGE = browserUpdate ? GSD_BROWSER_PACKAGE : GSD_PI_PACKAGE;
445
+ const registryUrl = browserUpdate ? BROWSER_UPDATE_REGISTRY_URL : UPDATE_REGISTRY_URL;
446
+ const bundledVersion = browserUpdate
447
+ ? resolveInstalledPackageVersionForCommand(GSD_BROWSER_PACKAGE)
448
+ : null;
449
+ const current = browserUpdate
450
+ ? pickHigherVersionForCommand(bundledVersion, resolveGsdBrowserPathVersionForCommand())
451
+ : process.env.GSD_VERSION || "0.0.0";
452
+ const label = browserUpdate ? "gsd-browser version" : "version";
453
+ ctx.ui.notify(`Current ${label}: ${formatCommandVersion(current)}\nChecking npm registry...`, "info");
454
+ const latest = await fetchLatestVersionForCommand(registryUrl);
395
455
  if (!latest) {
396
456
  ctx.ui.notify("Failed to reach npm registry. Check your network connection.", "error");
397
457
  return;
398
458
  }
399
- if (compareSemverLocal(latest, current) <= 0) {
400
- ctx.ui.notify(`Already up to date (v${current}).`, "info");
459
+ if (current && compareSemverLocal(latest, current) <= 0) {
460
+ ctx.ui.notify(`Already up to date (${formatCommandVersion(current)}).`, "info");
401
461
  return;
402
462
  }
403
- ctx.ui.notify(`Updating: v${current} → v${latest}...`, "info");
463
+ ctx.ui.notify(`Updating: ${formatCommandVersion(current)} → v${latest}...`, "info");
404
464
  const installCmd = resolveInstallCommand(`${NPM_PACKAGE}@latest`);
405
465
  try {
406
466
  execSync(installCmd, {
407
467
  stdio: ["ignore", "pipe", "ignore"],
408
468
  });
409
- ctx.ui.notify(`Updated to v${latest}. Restart your GSD session to use the new version.`, "info");
469
+ const newPathVersion = browserUpdate ? resolveGsdBrowserPathVersionForCommand() : null;
470
+ const pathReady = !browserUpdate || (!!newPathVersion && compareSemverLocal(newPathVersion, latest) >= 0);
471
+ ctx.ui.notify(browserUpdate
472
+ ? `Updated gsd-browser to v${latest}. Restart your GSD session to use the new browser automation version.` +
473
+ (pathReady ? "" : "\nNote: Ensure the npm global bin directory is on your PATH so MCP automation uses the updated binary.")
474
+ : `Updated to v${latest}. Restart your GSD session to use the new version.`, "info");
410
475
  }
411
476
  catch {
412
477
  ctx.ui.notify(`Update failed. Try manually: ${installCmd}`, "error");
@@ -33,7 +33,8 @@ export function formatMcpInitResult(status, configPath, targetPath) {
33
33
  `Project: ${targetPath}`,
34
34
  `Config: ${configPath}`,
35
35
  "",
36
- "MCP-capable clients can now load the GSD workflow MCP server from this folder.",
36
+ "MCP-capable clients can now load the GSD workflow and gsd-browser MCP servers from this folder.",
37
+ "Pi Providers use the managed gsd-browser engine directly; this project config is for External MCP Clients.",
37
38
  "Restart or reconnect any client that already has this project open.",
38
39
  ].join("\n");
39
40
  }