@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
@@ -1,12 +1,7 @@
1
1
  // Project/App: gsd-pi
2
2
  // File Purpose: Resolve phase-aware tool surfaces for GSD model presentations.
3
- export const RUN_UAT_WORKFLOW_TOOL_NAMES = [
4
- "gsd_uat_exec",
5
- "gsd_uat_result_save",
6
- "gsd_resume",
7
- "gsd_milestone_status",
8
- "gsd_journal_query",
9
- ];
3
+ import { RUN_UAT_BROWSER_TOOL_NAMES, RUN_UAT_READ_ONLY_TOOL_NAMES, RUN_UAT_TOOL_PRESENTATION_PLAN_ID, RUN_UAT_WORKFLOW_TOOL_NAMES, } from "./unit-tool-contracts.js";
4
+ export { RUN_UAT_BROWSER_TOOL_NAMES, RUN_UAT_READ_ONLY_TOOL_NAMES, RUN_UAT_TOOL_PRESENTATION_PLAN_ID, RUN_UAT_WORKFLOW_TOOL_NAMES, } from "./unit-tool-contracts.js";
10
5
  export const RUN_UAT_FORBIDDEN_TOOL_NAMES = [
11
6
  "edit",
12
7
  "write",
@@ -74,9 +69,37 @@ function addBlockedTool(blocked, name, reason) {
74
69
  export function buildRunUatCanonicalToolNames(options = {}) {
75
70
  return dedupe([
76
71
  ...RUN_UAT_WORKFLOW_TOOL_NAMES,
72
+ ...RUN_UAT_READ_ONLY_TOOL_NAMES,
77
73
  ...(options.includeBrowserTools ?? []),
78
74
  ]);
79
75
  }
76
+ export function runUatBrowserToolsForType(uatType) {
77
+ return uatType === "browser-executable" ? RUN_UAT_BROWSER_TOOL_NAMES : [];
78
+ }
79
+ export function runUatPresentationSurfaceForType(uatType) {
80
+ return uatType === "browser-executable" ? "hybrid" : "mcp";
81
+ }
82
+ export function buildRunUatPresentationForType(uatType, options = {}) {
83
+ return buildRunUatResultPresentation({
84
+ ...options,
85
+ surface: options.surface ?? runUatPresentationSurfaceForType(uatType),
86
+ includeBrowserTools: runUatBrowserToolsForType(uatType),
87
+ });
88
+ }
89
+ export function buildRunUatResultPresentation(options = {}) {
90
+ const presentedTools = options.presentedTools
91
+ ? dedupe(options.presentedTools)
92
+ : buildRunUatCanonicalToolNames({ includeBrowserTools: options.includeBrowserTools });
93
+ const blockedTools = RUN_UAT_FORBIDDEN_TOOL_NAMES
94
+ .filter((toolName) => !toolName.includes("*"))
95
+ .map((name) => ({ name, reason: "forbidden during run-uat" }));
96
+ return {
97
+ surface: options.surface ?? "mcp",
98
+ presentedTools,
99
+ blockedTools,
100
+ toolPresentationPlanId: RUN_UAT_TOOL_PRESENTATION_PLAN_ID,
101
+ };
102
+ }
80
103
  export function resolveToolPresentationPlan(options) {
81
104
  const requested = options.requestedToolNames ?? (options.phase === "run-uat"
82
105
  ? buildRunUatCanonicalToolNames({ includeBrowserTools: options.includeBrowserTools })
@@ -158,7 +158,7 @@ ${params.narrative}
158
158
 
159
159
  ## Verification
160
160
 
161
- ${params.verification}
161
+ ${params.verification ?? ""}
162
162
 
163
163
  ## Requirements Advanced
164
164
 
@@ -351,6 +351,20 @@ export async function handleCompleteSlice(params, basePath) {
351
351
  if (parsed.length > 0)
352
352
  effectiveParams.requirementsInvalidated = parsed;
353
353
  }
354
+ if (effectiveParams.verification === undefined) {
355
+ const headingLine = "## Verification\n\n";
356
+ const start = existingSummaryMd.indexOf(headingLine);
357
+ if (start !== -1) {
358
+ const contentStart = start + headingLine.length;
359
+ const nextHeading = existingSummaryMd.indexOf("\n\n## ", contentStart);
360
+ const prior = nextHeading === -1
361
+ ? existingSummaryMd.slice(contentStart)
362
+ : existingSummaryMd.slice(contentStart, nextHeading);
363
+ const trimmed = prior.trim();
364
+ if (trimmed)
365
+ effectiveParams.verification = trimmed;
366
+ }
367
+ }
354
368
  }
355
369
  // Render summary markdown
356
370
  const summaryMd = renderSliceSummaryMarkdown(effectiveParams);
@@ -314,9 +314,18 @@ export async function handleCompleteTask(params, basePath) {
314
314
  // overwrite it; gate rows are UPSERT-keyed per task and will also be
315
315
  // overwritten. This restores the invariant that deriveState() sees a
316
316
  // consistent "task not done" view so the loop re-dispatches the task.
317
+ let escalationMetadata;
317
318
  if (validatedEscalationArtifact) {
318
319
  try {
319
- writeEscalationArtifact(artifactBasePath, validatedEscalationArtifact);
320
+ const escalationPath = writeEscalationArtifact(artifactBasePath, validatedEscalationArtifact);
321
+ escalationMetadata = {
322
+ artifactPath: escalationPath,
323
+ question: validatedEscalationArtifact.question,
324
+ options: validatedEscalationArtifact.options,
325
+ recommendation: validatedEscalationArtifact.recommendation,
326
+ recommendationRationale: validatedEscalationArtifact.recommendationRationale,
327
+ continueWithDefault: validatedEscalationArtifact.continueWithDefault,
328
+ };
320
329
  }
321
330
  catch (escalationErr) {
322
331
  const msg = `complete-task escalation write failed for ${params.milestoneId}/${params.sliceId}/${params.taskId}: ${escalationErr.message}`;
@@ -378,6 +387,7 @@ export async function handleCompleteTask(params, basePath) {
378
387
  sliceId: params.sliceId,
379
388
  milestoneId: params.milestoneId,
380
389
  summaryPath,
390
+ ...(escalationMetadata ? { escalation: escalationMetadata } : {}),
381
391
  ...(projectionStale ? { stale: true } : {}),
382
392
  };
383
393
  }
@@ -44,19 +44,12 @@ function getRequiredVerificationClasses(milestoneId) {
44
44
  required.push("UAT");
45
45
  return required;
46
46
  }
47
- async function collectPersistedBrowserEvidence(basePath, milestoneId) {
48
- const chunks = [];
49
- for (const slice of getMilestoneSlices(milestoneId)) {
50
- const artifactPath = `milestones/${milestoneId}/slices/${slice.id}/${slice.id}-ASSESSMENT.md`;
51
- const artifact = getArtifact(artifactPath);
52
- if (artifact?.full_content)
53
- chunks.push(artifact.full_content);
54
- const assessmentPath = resolveSliceFile(basePath, milestoneId, slice.id, "ASSESSMENT");
55
- const assessmentContent = assessmentPath ? await loadFile(assessmentPath) : null;
56
- if (assessmentContent)
57
- chunks.push(assessmentContent);
58
- }
59
- return chunks.join("\n\n");
47
+ function hasRuntimeExecutableUatEvidenceText(text) {
48
+ if (!/\buatType:\s*runtime-executable\b/i.test(text))
49
+ return false;
50
+ if (!/\bverdict:\s*PASS\b/i.test(text))
51
+ return false;
52
+ return /^\|\s*[^|\n]+\s*\|\s*runtime\s*\|\s*PASS\s*\|[^|\n]*\bgsd_uat_exec\b/mi.test(text);
60
53
  }
61
54
  async function browserEvidenceGateRequiresAttention(params, basePath) {
62
55
  if (params.verdict !== "pass")
@@ -77,7 +70,36 @@ async function browserEvidenceGateRequiresAttention(params, basePath) {
77
70
  ]);
78
71
  if (!hasBrowserRequiredText(requirementText))
79
72
  return false;
80
- const persistedEvidence = await collectPersistedBrowserEvidence(basePath, params.milestoneId);
73
+ // Collect per-slice evidence so the runtime bypass is checked independently
74
+ // for each slice. Concatenating all slices before checking would allow runtime
75
+ // evidence from one slice to cover another slice's browser requirements.
76
+ const sliceEvidencePairs = [];
77
+ for (const slice of slices) {
78
+ const chunks = [];
79
+ const artifactPath = `milestones/${params.milestoneId}/slices/${slice.id}/${slice.id}-ASSESSMENT.md`;
80
+ const artifact = getArtifact(artifactPath);
81
+ if (artifact?.full_content)
82
+ chunks.push(artifact.full_content);
83
+ const assessmentPath = resolveSliceFile(basePath, params.milestoneId, slice.id, "ASSESSMENT");
84
+ const assessmentContent = assessmentPath ? await loadFile(assessmentPath) : null;
85
+ if (assessmentContent)
86
+ chunks.push(assessmentContent);
87
+ sliceEvidencePairs.push({
88
+ sliceRequirementText: compactTextParts([slice.demo, slice.goal, slice.success_criteria]),
89
+ evidenceText: chunks.join("\n\n"),
90
+ });
91
+ }
92
+ const persistedEvidence = sliceEvidencePairs.map((s) => s.evidenceText).join("\n\n");
93
+ // Runtime bypass: each slice whose own requirement text has browser-observable
94
+ // criteria must have its own runtime-executable UAT evidence. When no individual
95
+ // slice has slice-level browser requirements (e.g., they come from milestone-level
96
+ // fields only), fall back to checking whether any slice has runtime evidence.
97
+ const browserRequiringSlices = sliceEvidencePairs.filter((s) => hasBrowserRequiredText(s.sliceRequirementText));
98
+ const runtimeBypasses = browserRequiringSlices.length > 0
99
+ ? browserRequiringSlices.every((s) => hasRuntimeExecutableUatEvidenceText(s.evidenceText))
100
+ : sliceEvidencePairs.some((s) => hasRuntimeExecutableUatEvidenceText(s.evidenceText));
101
+ if (runtimeBypasses)
102
+ return false;
81
103
  const validationEvidence = compactTextParts([
82
104
  params.successCriteriaChecklist,
83
105
  params.verificationClasses,
@@ -138,12 +160,20 @@ export async function handleValidateMilestone(params, basePath, opts) {
138
160
  const requiredClasses = getRequiredVerificationClasses(params.milestoneId);
139
161
  if (requiredClasses.length > 0) {
140
162
  const verificationClasses = params.verificationClasses ?? "";
141
- const missingClass = requiredClasses.find((className) => !new RegExp(`\\b${className}\\b`, "i").test(verificationClasses));
142
- if (missingClass) {
163
+ const missingClasses = requiredClasses.filter((className) => !new RegExp(`\\b${className}\\b`, "i").test(verificationClasses));
164
+ if (missingClasses.length === 1) {
165
+ const missingClass = missingClasses[0];
143
166
  return {
144
167
  error: `verificationClasses must include canonical row "${missingClass}" because this milestone planned ${missingClass.toLowerCase()} verification`,
145
168
  };
146
169
  }
170
+ if (missingClasses.length > 1) {
171
+ const quotedClasses = missingClasses.map((className) => `"${className}"`).join(", ");
172
+ const plannedClasses = missingClasses.map((className) => className.toLowerCase()).join(", ");
173
+ return {
174
+ error: `verificationClasses must include canonical rows ${quotedClasses} because this milestone planned ${plannedClasses} verification`,
175
+ };
176
+ }
147
177
  }
148
178
  const artifactBasePath = resolveCanonicalMilestoneRoot(basePath, params.milestoneId);
149
179
  const shouldApplyBrowserEvidenceGate = !opts?.skipBrowserEvidenceGate &&
@@ -26,7 +26,7 @@ import { invalidateStateCache } from "../state.js";
26
26
  import { loadEffectiveGSDPreferences } from "../preferences.js";
27
27
  import { parseProject } from "../schemas/parsers.js";
28
28
  import { getAutoRuntimeSnapshot } from "../auto-runtime-state.js";
29
- import { canonicalWorkflowToolName, parseMcpToolName, RUN_UAT_FORBIDDEN_TOOL_NAMES, RUN_UAT_WORKFLOW_TOOL_NAMES, } from "../tool-presentation-plan.js";
29
+ import { buildRunUatPresentationForType, canonicalWorkflowToolName, parseMcpToolName, RUN_UAT_FORBIDDEN_TOOL_NAMES, RUN_UAT_TOOL_PRESENTATION_PLAN_ID, RUN_UAT_WORKFLOW_TOOL_NAMES, } from "../tool-presentation-plan.js";
30
30
  export const SUPPORTED_SUMMARY_ARTIFACT_TYPES = [
31
31
  "SUMMARY",
32
32
  "RESEARCH",
@@ -53,7 +53,7 @@ function blockIfWrongAutoUnit(requiredUnitType, operation) {
53
53
  return null;
54
54
  if (snapshot.currentUnit.type === requiredUnitType)
55
55
  return null;
56
- const error = `HARD BLOCK: ${operation} may only run from ${requiredUnitType}; active unit is ${snapshot.currentUnit.type}. The orchestrator owns phase transitions.`;
56
+ const error = `HARD BLOCK: Tool Contract failure: ${operation} may only run from ${requiredUnitType}; active unit is ${snapshot.currentUnit.type}. Fix unit-tool-contracts.ts or the active Unit prompt. The orchestrator owns phase transitions.`;
57
57
  return {
58
58
  content: [{ type: "text", text: error }],
59
59
  details: { operation, error },
@@ -121,7 +121,11 @@ export async function executeSummarySave(params, basePath = process.cwd()) {
121
121
  if (rootArtifactGuard.block) {
122
122
  return {
123
123
  content: [{ type: "text", text: `Error saving artifact: ${rootArtifactGuard.reason ?? "root artifact write blocked"}` }],
124
- details: { operation: "save_summary", error: "root_artifact_write_blocked" },
124
+ details: {
125
+ operation: "save_summary",
126
+ error: "root_artifact_write_blocked",
127
+ displayReason: "Approval confirmation required before saving final project setup artifacts.",
128
+ },
125
129
  isError: true,
126
130
  };
127
131
  }
@@ -129,7 +133,11 @@ export async function executeSummarySave(params, basePath = process.cwd()) {
129
133
  if (contextGuard.block) {
130
134
  return {
131
135
  content: [{ type: "text", text: `Error saving artifact: ${contextGuard.reason ?? "context write blocked"}` }],
132
- details: { operation: "save_summary", error: "context_write_blocked" },
136
+ details: {
137
+ operation: "save_summary",
138
+ error: "context_write_blocked",
139
+ displayReason: "Depth check required before writing milestone context.",
140
+ },
133
141
  isError: true,
134
142
  };
135
143
  }
@@ -334,6 +342,28 @@ export async function executeTaskComplete(params, basePath = process.cwd()) {
334
342
  isError: true,
335
343
  };
336
344
  }
345
+ if (result.escalation) {
346
+ const recommended = result.escalation.options.find((option) => option.id === result.escalation?.recommendation);
347
+ const optionIds = result.escalation.options.map((option) => option.id).join("|");
348
+ return {
349
+ content: [{
350
+ type: "text",
351
+ text: [
352
+ `Task completed with escalation decision required: ${result.escalation.question}`,
353
+ `Recommendation: ${result.escalation.recommendation}${recommended ? ` (${recommended.label})` : ""} — ${result.escalation.recommendationRationale}`,
354
+ `Resolve with: /gsd escalate resolve ${result.taskId} <${optionIds}|accept|reject-blocker> [rationale...]`,
355
+ ].join("\n"),
356
+ }],
357
+ details: {
358
+ operation: "complete_task",
359
+ taskId: result.taskId,
360
+ sliceId: result.sliceId,
361
+ milestoneId: result.milestoneId,
362
+ summaryPath: result.summaryPath,
363
+ escalation: result.escalation,
364
+ },
365
+ };
366
+ }
337
367
  return {
338
368
  content: [{ type: "text", text: `Completed task ${result.taskId} (${result.sliceId}/${result.milestoneId})` }],
339
369
  details: {
@@ -786,6 +816,52 @@ function errorResult(operation, message, error) {
786
816
  function isNonEmptyString(value) {
787
817
  return typeof value === "string" && value.trim().length > 0;
788
818
  }
819
+ function mergeBlockedTools(current, canonical) {
820
+ const merged = new Map();
821
+ for (const entry of [...(current ?? []), ...canonical]) {
822
+ merged.set(canonicalWorkflowToolName(parseMcpToolName(entry.name)?.tool ?? entry.name), entry);
823
+ }
824
+ return [...merged.values()];
825
+ }
826
+ function mergePresentedTools(current, canonical) {
827
+ return [...new Set([...(current ?? []), ...canonical])];
828
+ }
829
+ function normalizeUatVerdict(params) {
830
+ const raw = params;
831
+ if (typeof raw.verdict === "string") {
832
+ return { ...params, verdict: raw.verdict.toUpperCase() };
833
+ }
834
+ return params;
835
+ }
836
+ function supplyDefaultPresentation(params) {
837
+ const raw = params;
838
+ if (!raw.presentation) {
839
+ return { ...params, presentation: buildRunUatPresentationForType(params.uatType) };
840
+ }
841
+ return params;
842
+ }
843
+ function mergeCanonicalPresentation(params) {
844
+ const canonicalPresentation = buildRunUatPresentationForType(params.uatType);
845
+ const providedPresentation = params.presentation;
846
+ return {
847
+ ...params,
848
+ presentation: {
849
+ ...providedPresentation,
850
+ surface: providedPresentation.surface ?? canonicalPresentation.surface,
851
+ presentedTools: mergePresentedTools(providedPresentation.presentedTools, canonicalPresentation.presentedTools),
852
+ blockedTools: mergeBlockedTools(providedPresentation.blockedTools, canonicalPresentation.blockedTools),
853
+ toolPresentationPlanId: RUN_UAT_TOOL_PRESENTATION_PLAN_ID,
854
+ },
855
+ };
856
+ }
857
+ const VALID_UAT_TYPES = [
858
+ "artifact-driven",
859
+ "browser-executable",
860
+ "runtime-executable",
861
+ "live-runtime",
862
+ "mixed",
863
+ "human-experience",
864
+ ];
789
865
  function ensureUatRequiredFields(params) {
790
866
  if (!isNonEmptyString(params.milestoneId))
791
867
  return "milestoneId is required";
@@ -793,6 +869,9 @@ function ensureUatRequiredFields(params) {
793
869
  return "sliceId is required";
794
870
  if (!isNonEmptyString(params.uatType))
795
871
  return "uatType is required";
872
+ if (!VALID_UAT_TYPES.includes(params.uatType)) {
873
+ return `uatType must be one of: ${VALID_UAT_TYPES.join(", ")}`;
874
+ }
796
875
  if (!["PASS", "FAIL", "PARTIAL"].includes(params.verdict))
797
876
  return "verdict must be PASS, FAIL, or PARTIAL";
798
877
  if (!Array.isArray(params.checks) || params.checks.length === 0)
@@ -929,9 +1008,18 @@ function validateUatChecks(basePath, params) {
929
1008
  }
930
1009
  return null;
931
1010
  }
1011
+ function validateFreshUatOwnedEvidence(params) {
1012
+ const hasFreshUatEvidence = params.checks.some((check) => (check.evidence ?? []).some((evidence) => evidence.kind === "gsd_uat_exec"));
1013
+ return hasFreshUatEvidence
1014
+ ? null
1015
+ : "UAT Assessment requires at least one fresh gsd_uat_exec evidence reference from run-uat";
1016
+ }
932
1017
  function validateUatMode(params) {
933
1018
  const modes = new Set(params.checks.map((check) => check.mode));
934
1019
  const hasHuman = params.checks.some((check) => check.result === "NEEDS-HUMAN");
1020
+ if (params.uatType === "artifact-driven" && hasHuman && params.verdict === "PASS") {
1021
+ return "artifact-driven UAT cannot PASS with human-only checks";
1022
+ }
935
1023
  if (hasHuman &&
936
1024
  params.verdict === "PASS" &&
937
1025
  !["human-experience", "mixed", "live-runtime"].includes(params.uatType) &&
@@ -947,11 +1035,11 @@ function validateUatMode(params) {
947
1035
  if (params.uatType === "live-runtime" && !modes.has("runtime") && !modes.has("browser")) {
948
1036
  return "live-runtime UAT requires runtime or browser evidence";
949
1037
  }
950
- if (params.uatType === "artifact-driven" && hasHuman && params.verdict === "PASS") {
951
- return "artifact-driven UAT cannot PASS with human-only checks";
952
- }
953
1038
  return null;
954
1039
  }
1040
+ function quoteToolNames(toolNames) {
1041
+ return toolNames.map((toolName) => `"${toolName}"`).join(", ");
1042
+ }
955
1043
  function validateCanonicalPresentation(params) {
956
1044
  const aliasHints = {
957
1045
  gsd_save_summary: "gsd_summary_save",
@@ -959,34 +1047,46 @@ function validateCanonicalPresentation(params) {
959
1047
  gsd_complete_slice: "gsd_slice_complete",
960
1048
  gsd_milestone_complete: "gsd_complete_milestone",
961
1049
  };
1050
+ const errors = [];
962
1051
  for (const toolName of params.presentation.presentedTools) {
963
1052
  const baseName = parseMcpToolName(toolName)?.tool ?? toolName;
964
1053
  const canonical = aliasHints[baseName];
965
1054
  if (canonical)
966
- return `presentation tool "${toolName}" uses an alias; use canonical "${canonical}"`;
1055
+ errors.push(`presentation tool "${toolName}" uses an alias; use canonical "${canonical}"`);
967
1056
  }
968
1057
  const presentedCanonical = new Set(params.presentation.presentedTools.map((toolName) => canonicalWorkflowToolName(parseMcpToolName(toolName)?.tool ?? toolName)));
969
- for (const requiredTool of RUN_UAT_WORKFLOW_TOOL_NAMES) {
970
- if (!presentedCanonical.has(requiredTool)) {
971
- return `presentation is missing required UAT tool "${requiredTool}"`;
972
- }
1058
+ const missingRequiredTools = RUN_UAT_WORKFLOW_TOOL_NAMES.filter((requiredTool) => !presentedCanonical.has(requiredTool));
1059
+ if (missingRequiredTools.length === 1) {
1060
+ errors.push(`presentation is missing required UAT tool "${missingRequiredTools[0]}"`);
1061
+ }
1062
+ else if (missingRequiredTools.length > 1) {
1063
+ errors.push(`presentation is missing required UAT tools ${quoteToolNames(missingRequiredTools)}`);
973
1064
  }
974
1065
  const forbiddenCanonical = new Set(RUN_UAT_FORBIDDEN_TOOL_NAMES
975
1066
  .filter((toolName) => !toolName.includes("*"))
976
1067
  .map((toolName) => canonicalWorkflowToolName(parseMcpToolName(toolName)?.tool ?? toolName)));
1068
+ const forbiddenPresentedTools = [];
977
1069
  for (const toolName of params.presentation.presentedTools) {
978
1070
  const canonical = canonicalWorkflowToolName(parseMcpToolName(toolName)?.tool ?? toolName);
979
1071
  if (toolName === "mcp__gsd-workflow__*" || forbiddenCanonical.has(canonical)) {
980
- return `presentation includes forbidden run-uat tool "${toolName}"`;
1072
+ forbiddenPresentedTools.push(toolName);
981
1073
  }
982
1074
  }
1075
+ if (forbiddenPresentedTools.length === 1) {
1076
+ errors.push(`presentation includes forbidden run-uat tool "${forbiddenPresentedTools[0]}"`);
1077
+ }
1078
+ else if (forbiddenPresentedTools.length > 1) {
1079
+ errors.push(`presentation includes forbidden run-uat tools ${quoteToolNames(forbiddenPresentedTools)}`);
1080
+ }
983
1081
  const blockedCanonical = new Set(params.presentation.blockedTools.map((entry) => canonicalWorkflowToolName(parseMcpToolName(entry.name)?.tool ?? entry.name)));
984
- for (const blockedTool of ["gsd_exec", "gsd_summary_save", "gsd_save_gate_result"]) {
985
- if (!blockedCanonical.has(blockedTool)) {
986
- return `presentation must record "${blockedTool}" as blocked during run-uat`;
987
- }
1082
+ const missingBlockedTools = ["gsd_exec", "gsd_summary_save", "gsd_save_gate_result"].filter((blockedTool) => !blockedCanonical.has(blockedTool));
1083
+ if (missingBlockedTools.length === 1) {
1084
+ errors.push(`presentation must record "${missingBlockedTools[0]}" as blocked during run-uat`);
988
1085
  }
989
- return null;
1086
+ else if (missingBlockedTools.length > 1) {
1087
+ errors.push(`presentation must record ${quoteToolNames(missingBlockedTools)} as blocked during run-uat`);
1088
+ }
1089
+ return errors.length > 0 ? errors.join("; ") : null;
990
1090
  }
991
1091
  function nextUatAttempt(basePath, milestoneId, sliceId) {
992
1092
  const contract = resolveGsdPathContract(basePath);
@@ -1050,18 +1150,32 @@ async function saveUatAttemptArtifact(basePath, params, attempt) {
1050
1150
  return relativePath;
1051
1151
  }
1052
1152
  export async function executeUatResultSave(params, basePath = process.cwd()) {
1153
+ const unitGuard = blockIfWrongAutoUnit("run-uat", "save_uat_result");
1154
+ if (unitGuard)
1155
+ return unitGuard;
1156
+ // Phase 1: normalize verdict and supply the canonical presentation when none was provided.
1157
+ params = normalizeUatVerdict(params);
1158
+ params = supplyDefaultPresentation(params);
1053
1159
  const dbAvailable = await ensureDbOpen(basePath);
1054
1160
  if (!dbAvailable)
1055
1161
  return errorResult("save_uat_result", "GSD database is not available.", "db_unavailable");
1162
+ // Phase 2: validate the submitted presentation before the canonical merge so that
1163
+ // presentations missing required workflow tools are rejected rather than silently patched.
1056
1164
  const requiredError = ensureUatRequiredFields(params);
1057
1165
  if (requiredError)
1058
1166
  return errorResult("save_uat_result", requiredError, "invalid_params");
1059
1167
  const presentationError = validateCanonicalPresentation(params);
1060
1168
  if (presentationError)
1061
1169
  return errorResult("save_uat_result", presentationError, "alias_tool_name");
1170
+ // Phase 3: merge in the canonical plan ID and read-only audit tools so the persisted
1171
+ // artifact always carries the full audit surface even when the provider omitted them.
1172
+ params = mergeCanonicalPresentation(params);
1062
1173
  const checkError = validateUatChecks(basePath, params);
1063
1174
  if (checkError)
1064
1175
  return errorResult("save_uat_result", checkError, "invalid_evidence");
1176
+ const freshEvidenceError = validateFreshUatOwnedEvidence(params);
1177
+ if (freshEvidenceError)
1178
+ return errorResult("save_uat_result", freshEvidenceError, "missing_fresh_uat_evidence");
1065
1179
  const modeError = validateUatMode(params);
1066
1180
  if (modeError)
1067
1181
  return errorResult("save_uat_result", modeError, "uat_mode_mismatch");
@@ -0,0 +1,169 @@
1
+ // Project/App: gsd-pi
2
+ // File Purpose: Central Unit-to-tool contracts for phase-aware GSD tool surfaces.
3
+ export const RUN_UAT_WORKFLOW_TOOL_NAMES = [
4
+ "gsd_uat_exec",
5
+ "gsd_uat_result_save",
6
+ "gsd_resume",
7
+ "gsd_milestone_status",
8
+ "gsd_journal_query",
9
+ ];
10
+ export const RUN_UAT_READ_ONLY_TOOL_NAMES = [
11
+ "find",
12
+ "glob",
13
+ "grep",
14
+ "ls",
15
+ "read",
16
+ ];
17
+ export const RUN_UAT_BROWSER_TOOL_NAMES = [
18
+ "browser_navigate",
19
+ "browser_click",
20
+ "browser_type",
21
+ "browser_fill_form",
22
+ "browser_click_ref",
23
+ "browser_fill_ref",
24
+ "browser_wait_for",
25
+ "browser_assert",
26
+ "browser_verify",
27
+ "browser_screenshot",
28
+ "browser_snapshot_refs",
29
+ "browser_find",
30
+ "browser_get_console_logs",
31
+ "browser_get_network_logs",
32
+ "browser_evaluate",
33
+ "browser_reload",
34
+ "browser_batch",
35
+ "browser_act",
36
+ ];
37
+ export const RUN_UAT_TOOL_PRESENTATION_PLAN_ID = "run-uat/default-v1";
38
+ export const UNIT_TOOL_CONTRACTS = {
39
+ "research-milestone": {
40
+ allowedGsdTools: ["gsd_summary_save", "gsd_decision_save"],
41
+ requiredWorkflowTools: ["gsd_summary_save"],
42
+ },
43
+ "plan-milestone": {
44
+ allowedGsdTools: ["gsd_plan_milestone", "gsd_decision_save", "gsd_requirement_update"],
45
+ requiredWorkflowTools: ["gsd_plan_milestone"],
46
+ },
47
+ "discuss-milestone": {
48
+ allowedGsdTools: [
49
+ "gsd_summary_save",
50
+ "gsd_decision_save",
51
+ "gsd_requirement_save",
52
+ "gsd_requirement_update",
53
+ "gsd_plan_milestone",
54
+ "gsd_milestone_generate_id",
55
+ ],
56
+ requiredWorkflowTools: [
57
+ "gsd_summary_save",
58
+ "gsd_requirement_save",
59
+ "gsd_requirement_update",
60
+ "gsd_plan_milestone",
61
+ "gsd_milestone_generate_id",
62
+ ],
63
+ },
64
+ "discuss-slice": {
65
+ allowedGsdTools: ["gsd_summary_save", "gsd_decision_save"],
66
+ requiredWorkflowTools: ["gsd_summary_save"],
67
+ },
68
+ "validate-milestone": {
69
+ allowedGsdTools: ["gsd_validate_milestone", "gsd_reassess_roadmap", "subagent"],
70
+ requiredWorkflowTools: ["gsd_milestone_status", "gsd_validate_milestone", "gsd_reassess_roadmap"],
71
+ },
72
+ "complete-milestone": {
73
+ allowedGsdTools: ["gsd_complete_milestone", "subagent"],
74
+ requiredWorkflowTools: ["gsd_milestone_status", "gsd_complete_milestone"],
75
+ },
76
+ "research-slice": {
77
+ allowedGsdTools: ["gsd_summary_save", "gsd_decision_save"],
78
+ requiredWorkflowTools: ["gsd_summary_save"],
79
+ },
80
+ "plan-slice": {
81
+ allowedGsdTools: ["gsd_plan_slice", "gsd_plan_task", "gsd_decision_save"],
82
+ requiredWorkflowTools: ["gsd_plan_slice"],
83
+ },
84
+ "refine-slice": {
85
+ allowedGsdTools: ["gsd_plan_slice", "gsd_plan_task", "gsd_decision_save"],
86
+ requiredWorkflowTools: [],
87
+ },
88
+ "replan-slice": {
89
+ allowedGsdTools: ["gsd_replan_slice", "gsd_plan_task", "gsd_decision_save"],
90
+ requiredWorkflowTools: ["gsd_replan_slice"],
91
+ },
92
+ "complete-slice": {
93
+ allowedGsdTools: [
94
+ "gsd_slice_complete",
95
+ "gsd_task_reopen",
96
+ "gsd_replan_slice",
97
+ "gsd_decision_save",
98
+ "gsd_requirement_update",
99
+ "subagent",
100
+ ],
101
+ requiredWorkflowTools: ["gsd_slice_complete", "gsd_task_reopen", "gsd_replan_slice"],
102
+ forbiddenGsdTools: {
103
+ gsd_uat_result_save: "Run UAT owns persisted UAT Assessment.",
104
+ },
105
+ },
106
+ "reassess-roadmap": {
107
+ allowedGsdTools: ["gsd_reassess_roadmap"],
108
+ requiredWorkflowTools: ["gsd_milestone_status", "gsd_reassess_roadmap"],
109
+ },
110
+ "execute-task": {
111
+ allowedGsdTools: ["gsd_task_complete", "gsd_decision_save"],
112
+ requiredWorkflowTools: ["gsd_task_complete"],
113
+ },
114
+ "execute-task-simple": {
115
+ allowedGsdTools: ["gsd_task_complete", "gsd_decision_save"],
116
+ requiredWorkflowTools: ["gsd_task_complete"],
117
+ },
118
+ "reactive-execute": {
119
+ allowedGsdTools: ["gsd_task_complete", "gsd_decision_save"],
120
+ requiredWorkflowTools: ["gsd_task_complete"],
121
+ },
122
+ "run-uat": {
123
+ allowedGsdTools: [...RUN_UAT_WORKFLOW_TOOL_NAMES, "subagent"],
124
+ requiredWorkflowTools: [...RUN_UAT_WORKFLOW_TOOL_NAMES],
125
+ forbiddenGsdTools: {
126
+ gsd_exec: "Use gsd_uat_exec so acceptance evidence is typed as UAT-owned.",
127
+ gsd_save_gate_result: "gsd_uat_result_save owns the aggregate UAT gate.",
128
+ gsd_summary_save: "gsd_uat_result_save owns persisted UAT Assessment writes.",
129
+ },
130
+ },
131
+ "gate-evaluate": {
132
+ allowedGsdTools: ["gsd_save_gate_result"],
133
+ requiredWorkflowTools: ["gsd_save_gate_result"],
134
+ },
135
+ "rewrite-docs": {
136
+ allowedGsdTools: ["gsd_summary_save", "gsd_decision_save"],
137
+ requiredWorkflowTools: [],
138
+ },
139
+ "workflow-preferences": {
140
+ allowedGsdTools: ["gsd_summary_save"],
141
+ requiredWorkflowTools: [],
142
+ },
143
+ "discuss-project": {
144
+ allowedGsdTools: ["gsd_summary_save", "gsd_decision_save", "gsd_requirement_save"],
145
+ requiredWorkflowTools: ["ask_user_questions", "gsd_summary_save"],
146
+ },
147
+ "discuss-requirements": {
148
+ allowedGsdTools: ["gsd_requirement_save", "gsd_summary_save"],
149
+ requiredWorkflowTools: ["ask_user_questions", "gsd_requirement_save", "gsd_summary_save"],
150
+ },
151
+ "research-decision": {
152
+ allowedGsdTools: ["gsd_summary_save"],
153
+ requiredWorkflowTools: ["ask_user_questions"],
154
+ },
155
+ "research-project": {
156
+ allowedGsdTools: ["gsd_summary_save", "gsd_decision_save"],
157
+ requiredWorkflowTools: [],
158
+ },
159
+ };
160
+ export const AUTO_UNIT_SCOPED_TOOLS = Object.fromEntries(Object.entries(UNIT_TOOL_CONTRACTS).map(([unitType, contract]) => [unitType, contract.allowedGsdTools]));
161
+ export function getUnitToolSurfaceContract(unitType) {
162
+ return UNIT_TOOL_CONTRACTS[unitType];
163
+ }
164
+ export function getRequiredWorkflowToolsForUnit(unitType) {
165
+ return [...(UNIT_TOOL_CONTRACTS[unitType]?.requiredWorkflowTools ?? [])];
166
+ }
167
+ export function getForbiddenGsdToolReason(unitType, toolName) {
168
+ return UNIT_TOOL_CONTRACTS[unitType]?.forbiddenGsdTools?.[toolName];
169
+ }