@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
@@ -12,6 +12,7 @@ import { tmpdir } from "node:os";
12
12
 
13
13
  import { buildSliceSummaryExcerpt, buildCompleteMilestonePrompt, buildValidateMilestonePrompt } from "../auto-prompts.ts";
14
14
  import { invalidateAllCaches } from "../cache.ts";
15
+ import { closeDatabase, insertMilestone, openDatabase } from "../gsd-db.ts";
15
16
 
16
17
  // ─── Fixture helpers ──────────────────────────────────────────────────────
17
18
 
@@ -364,3 +365,32 @@ test("validate-milestone prompt uses slice excerpts and on-demand paths instead
364
365
  "validate prompt must not inline full assessment traces",
365
366
  );
366
367
  });
368
+
369
+ test("validate-milestone prompt inlines planned verification classes as canonical rows", async (t) => {
370
+ const base = createBase();
371
+ t.after(() => {
372
+ try { closeDatabase(); } catch { /* ignore */ }
373
+ cleanup(base);
374
+ });
375
+ invalidateAllCaches();
376
+
377
+ openDatabase(join(base, ".gsd", "gsd.db"));
378
+ insertMilestone({
379
+ id: "M001",
380
+ planning: {
381
+ verificationContract: "Local command exits 0.",
382
+ verificationOperational: "No long-running child process remains.",
383
+ },
384
+ });
385
+ writeRoadmap(base, makeRoadmap());
386
+ writeSummary(base, "S01", makeFatSummary("S01"));
387
+ writeSummary(base, "S02", makeFatSummary("S02"));
388
+
389
+ const prompt = await buildValidateMilestonePrompt("M001", "Test Milestone", base);
390
+
391
+ assert.match(prompt, /### Verification Classes \(from planning\)/);
392
+ assert.match(prompt, /Every row in this table must appear in `verificationClasses`/);
393
+ assert.match(prompt, /\| Class \| Planned Check \|/);
394
+ assert.match(prompt, /\| Contract \| Local command exits 0\. \|/);
395
+ assert.match(prompt, /\| Operational \| No long-running child process remains\. \|/);
396
+ });
@@ -20,6 +20,7 @@ import {
20
20
  insertMilestone,
21
21
  insertSlice,
22
22
  insertTask,
23
+ setSliceSummaryMd,
23
24
  } from '../gsd-db.ts';
24
25
  import { handleCompleteSlice } from '../tools/complete-slice.ts';
25
26
  import type { CompleteSliceParams } from '../types.ts';
@@ -153,4 +154,45 @@ describe('complete-slice verification gate (#3580)', () => {
153
154
  );
154
155
  }
155
156
  });
157
+
158
+ test('backfills prior verification narrative when verification is omitted on re-completion', async () => {
159
+ // Seed full_summary_md with a prior verification narrative (simulates a
160
+ // previous completion where the verification text was recorded).
161
+ const priorVerification = 'All 12 API integration tests pass — zero regressions detected.';
162
+ const priorSummary = [
163
+ '---',
164
+ 'verification_result: passed',
165
+ '---',
166
+ '',
167
+ '# S01: Test Slice',
168
+ '',
169
+ '## What Happened',
170
+ '',
171
+ 'narrative goes here',
172
+ '',
173
+ '## Verification',
174
+ '',
175
+ priorVerification,
176
+ '',
177
+ '## Requirements Advanced',
178
+ '',
179
+ ].join('\n');
180
+ setSliceSummaryMd('M001', 'S01', priorSummary, '');
181
+
182
+ // Complete slice without providing verification — handler must backfill
183
+ // from the existing summary rather than writing an empty section.
184
+ const result = await handleCompleteSlice(
185
+ makeParams({ verification: undefined }),
186
+ basePath,
187
+ );
188
+
189
+ assert.ok(!('error' in result), `expected success, got: ${'error' in result ? (result as { error: string }).error : ''}`);
190
+
191
+ const { summaryPath } = result as { summaryPath: string };
192
+ const written = fs.readFileSync(summaryPath, 'utf8');
193
+ assert.ok(
194
+ written.includes(priorVerification),
195
+ `prior verification narrative must be preserved when verification is omitted; got:\n${written}`,
196
+ );
197
+ });
156
198
  });
@@ -6,6 +6,7 @@ import test from "node:test";
6
6
  import assert from "node:assert/strict";
7
7
 
8
8
  import { GSDDashboardOverlay } from "../dashboard-overlay.ts";
9
+ import type { UnitMetrics } from "../metrics.ts";
9
10
  import { assertFullOuterBorder } from "./tui-border-assertions.ts";
10
11
 
11
12
  const fakeTheme = {
@@ -23,3 +24,47 @@ test("GSDDashboardOverlay renders inside the shared full border", (t) => {
23
24
  assert.ok(lines.some((line) => line.startsWith("│")), "body rows should have side borders");
24
25
  assert.match(lines.at(-1) ?? "", /^╰─+╯$/);
25
26
  });
27
+
28
+ test("GSDDashboardOverlay reuses metrics aggregations until the unit count changes", (t) => {
29
+ const overlay = new GSDDashboardOverlay({ requestRender() {} }, fakeTheme as any, () => {});
30
+ t.after(() => overlay.dispose());
31
+
32
+ const firstUnits = [makeUnit("M001/S001/T001", 0.25)];
33
+ const firstMetrics = (overlay as any).ensureMetricsCache(firstUnits);
34
+
35
+ overlay.invalidate();
36
+
37
+ const sameCountUnits = [makeUnit("M001/S001/T002", 0.5)];
38
+ const sameCountMetrics = (overlay as any).ensureMetricsCache(sameCountUnits);
39
+ assert.equal(sameCountMetrics, firstMetrics, "same unit count should reuse cached metrics");
40
+ assert.equal(sameCountMetrics.totals.cost, 0.25);
41
+
42
+ const increasedCountMetrics = (overlay as any).ensureMetricsCache([
43
+ ...sameCountUnits,
44
+ makeUnit("M001/S001/T003", 0.75),
45
+ ]);
46
+ assert.notEqual(increasedCountMetrics, firstMetrics, "changed unit count should recompute metrics");
47
+ assert.equal(increasedCountMetrics.totals.units, 2);
48
+ assert.equal(increasedCountMetrics.totals.cost, 1.25);
49
+ });
50
+
51
+ function makeUnit(id: string, cost: number): UnitMetrics {
52
+ return {
53
+ type: "execute-task",
54
+ id,
55
+ model: "claude-sonnet-4.5",
56
+ startedAt: 1000,
57
+ finishedAt: 2000,
58
+ tokens: {
59
+ input: 100,
60
+ output: 50,
61
+ cacheRead: 25,
62
+ cacheWrite: 10,
63
+ total: 185,
64
+ },
65
+ cost,
66
+ toolCalls: 1,
67
+ assistantMessages: 1,
68
+ userMessages: 1,
69
+ };
70
+ }
@@ -229,6 +229,24 @@ function makeIsolatedBaseWithCleanup(t: TestContext): string {
229
229
  return base;
230
230
  }
231
231
 
232
+ function setGsdHeadless(t: TestContext): void {
233
+ const previous = process.env.GSD_HEADLESS;
234
+ process.env.GSD_HEADLESS = "1";
235
+ t.after(() => {
236
+ if (previous === undefined) delete process.env.GSD_HEADLESS;
237
+ else process.env.GSD_HEADLESS = previous;
238
+ });
239
+ }
240
+
241
+ function unsetGsdHeadless(t: TestContext): void {
242
+ const previous = process.env.GSD_HEADLESS;
243
+ delete process.env.GSD_HEADLESS;
244
+ t.after(() => {
245
+ if (previous === undefined) delete process.env.GSD_HEADLESS;
246
+ else process.env.GSD_HEADLESS = previous;
247
+ });
248
+ }
249
+
232
250
  function writeValidProject(base: string): void {
233
251
  writeFileSync(join(base, ".gsd", "PROJECT.md"), VALID_PROJECT_MD);
234
252
  }
@@ -364,16 +382,33 @@ test("Deep mode: discuss-project does NOT dispatch when planning_depth is 'light
364
382
  test("Deep mode: discuss-project DOES dispatch when planning_depth is 'deep' and PROJECT.md missing", async (t) => {
365
383
  const base = makeIsolatedBaseWithCleanup(t);
366
384
 
385
+ unsetGsdHeadless(t);
386
+
367
387
  const prefs = { planning_depth: "deep" } as GSDPreferences;
368
388
  const result = await rule(PROJECT_RULE_NAME).match(makeCtx(base, prefs));
369
389
  assert.ok(result && result.action === "dispatch", "deep mode + missing PROJECT.md must dispatch");
370
390
  if (result.action === "dispatch") {
371
391
  assert.strictEqual(result.unitType, "discuss-project");
372
392
  assert.strictEqual(result.unitId, "PROJECT");
393
+ assert.strictEqual(result.pauseAfterDispatch, true);
373
394
  assert.ok(result.prompt.length > 0, "prompt must be non-empty");
374
395
  }
375
396
  });
376
397
 
398
+ test("Deep mode: discuss-project does not pause when GSD_HEADLESS is set", async (t) => {
399
+ const base = makeIsolatedBaseWithCleanup(t);
400
+
401
+ setGsdHeadless(t);
402
+
403
+ const prefs = { planning_depth: "deep" } as GSDPreferences;
404
+ const result = await rule(PROJECT_RULE_NAME).match(makeCtx(base, prefs));
405
+ assert.ok(result && result.action === "dispatch", "deep mode + missing PROJECT.md must dispatch");
406
+ if (result.action === "dispatch") {
407
+ assert.strictEqual(result.unitType, "discuss-project");
408
+ assert.strictEqual(result.pauseAfterDispatch, false);
409
+ }
410
+ });
411
+
377
412
  test("Deep mode: discuss-project does NOT dispatch when PROJECT.md already exists and is valid", async (t) => {
378
413
  const base = makeIsolatedBaseWithCleanup(t);
379
414
 
@@ -432,6 +467,8 @@ test("Deep mode: discuss-requirements does NOT dispatch when PROJECT.md missing
432
467
  test("Deep mode: discuss-requirements DOES dispatch when PROJECT.md exists and REQUIREMENTS.md missing", async (t) => {
433
468
  const base = makeIsolatedBaseWithCleanup(t);
434
469
 
470
+ unsetGsdHeadless(t);
471
+
435
472
  writeValidProject(base);
436
473
  const prefs = { planning_depth: "deep" } as GSDPreferences;
437
474
  const result = await rule(REQUIREMENTS_RULE_NAME).match(makeCtx(base, prefs));
@@ -439,6 +476,22 @@ test("Deep mode: discuss-requirements DOES dispatch when PROJECT.md exists and R
439
476
  if (result.action === "dispatch") {
440
477
  assert.strictEqual(result.unitType, "discuss-requirements");
441
478
  assert.strictEqual(result.unitId, "REQUIREMENTS");
479
+ assert.strictEqual(result.pauseAfterDispatch, true);
480
+ }
481
+ });
482
+
483
+ test("Deep mode: discuss-requirements does not pause when GSD_HEADLESS is set", async (t) => {
484
+ const base = makeIsolatedBaseWithCleanup(t);
485
+
486
+ setGsdHeadless(t);
487
+
488
+ writeValidProject(base);
489
+ const prefs = { planning_depth: "deep" } as GSDPreferences;
490
+ const result = await rule(REQUIREMENTS_RULE_NAME).match(makeCtx(base, prefs));
491
+ assert.ok(result && result.action === "dispatch", "deep mode + PROJECT.md present + REQUIREMENTS.md missing must dispatch");
492
+ if (result.action === "dispatch") {
493
+ assert.strictEqual(result.unitType, "discuss-requirements");
494
+ assert.strictEqual(result.pauseAfterDispatch, false);
442
495
  }
443
496
  });
444
497
 
@@ -44,14 +44,26 @@ function setGsdHeadless(t: { after: (fn: () => void) => void }): void {
44
44
  });
45
45
  }
46
46
 
47
+ function unsetGsdHeadless(t: { after: (fn: () => void) => void }): void {
48
+ const previous = process.env.GSD_HEADLESS;
49
+ delete process.env.GSD_HEADLESS;
50
+ t.after(() => {
51
+ if (previous === undefined) delete process.env.GSD_HEADLESS;
52
+ else process.env.GSD_HEADLESS = previous;
53
+ });
54
+ }
55
+
47
56
  test("auto-dispatch passes structuredQuestionsAvailable=true into discuss-milestone prompt", async (t) => {
48
57
  const tmp = mkdtempSync(join(tmpdir(), "gsd-discuss-milestone-structured-"));
49
58
  t.after(() => rmSync(tmp, { recursive: true, force: true }));
50
59
 
60
+ unsetGsdHeadless(t);
61
+
51
62
  const result = await resolveDispatch(makeContext(tmp, "needs-discussion", "true"));
52
63
 
53
64
  assert.equal(result.action, "dispatch");
54
65
  assert.equal(result.unitType, "discuss-milestone");
66
+ assert.equal(result.pauseAfterDispatch, true);
55
67
  assert.match(
56
68
  result.prompt,
57
69
  /\*\*Structured questions available: true\*\*/,
@@ -62,10 +74,13 @@ test("auto-dispatch preserves structuredQuestionsAvailable=false for discuss-mil
62
74
  const tmp = mkdtempSync(join(tmpdir(), "gsd-discuss-milestone-plain-"));
63
75
  t.after(() => rmSync(tmp, { recursive: true, force: true }));
64
76
 
77
+ unsetGsdHeadless(t);
78
+
65
79
  const result = await resolveDispatch(makeContext(tmp, "pre-planning", "false"));
66
80
 
67
81
  assert.equal(result.action, "dispatch");
68
82
  assert.equal(result.unitType, "discuss-milestone");
83
+ assert.equal(result.pauseAfterDispatch, true);
69
84
  assert.match(
70
85
  result.prompt,
71
86
  /\*\*Structured questions available: false\*\*/,
@@ -82,6 +97,7 @@ test("auto-dispatch uses discuss-headless prompt when GSD_HEADLESS is set", asyn
82
97
 
83
98
  assert.equal(result.action, "dispatch");
84
99
  assert.equal(result.unitType, "discuss-milestone");
100
+ assert.equal(result.pauseAfterDispatch, false);
85
101
  assert.match(result.prompt, /This is a \*\*headless\*\* flow/);
86
102
  assert.doesNotMatch(result.prompt, /\*\*Structured questions available: true\*\*/);
87
103
  });
@@ -96,10 +112,24 @@ test("auto-dispatch uses discuss-headless prompt for needs-discussion when GSD_H
96
112
 
97
113
  assert.equal(result.action, "dispatch");
98
114
  assert.equal(result.unitType, "discuss-milestone");
115
+ assert.equal(result.pauseAfterDispatch, false);
99
116
  assert.match(result.prompt, /This is a \*\*headless\*\* flow/);
100
117
  assert.doesNotMatch(result.prompt, /\*\*Structured questions available: true\*\*/);
101
118
  });
102
119
 
120
+ test("auto-dispatch pauses after execution-entry discuss-milestone recovery", async (t) => {
121
+ const tmp = mkdtempSync(join(tmpdir(), "gsd-discuss-milestone-executing-"));
122
+ t.after(() => rmSync(tmp, { recursive: true, force: true }));
123
+
124
+ unsetGsdHeadless(t);
125
+
126
+ const result = await resolveDispatch(makeContext(tmp, "executing", "true"));
127
+
128
+ assert.equal(result.action, "dispatch");
129
+ assert.equal(result.unitType, "discuss-milestone");
130
+ assert.equal(result.pauseAfterDispatch, true);
131
+ });
132
+
103
133
  test("auto-dispatch uses discuss-headless prompt for executing when GSD_HEADLESS is set", async (t) => {
104
134
  const tmp = mkdtempSync(join(tmpdir(), "gsd-discuss-milestone-headless-"));
105
135
  t.after(() => rmSync(tmp, { recursive: true, force: true }));
@@ -110,6 +140,7 @@ test("auto-dispatch uses discuss-headless prompt for executing when GSD_HEADLESS
110
140
 
111
141
  assert.equal(result.action, "dispatch");
112
142
  assert.equal(result.unitType, "discuss-milestone");
143
+ assert.equal(result.pauseAfterDispatch, false);
113
144
  assert.match(result.prompt, /This is a \*\*headless\*\* flow/);
114
145
  assert.doesNotMatch(result.prompt, /\*\*Structured questions available: true\*\*/);
115
146
  });
@@ -14,7 +14,7 @@ import { execFileSync } from "node:child_process";
14
14
 
15
15
  import { DISPATCH_RULES, resolveDispatch, type DispatchContext } from "../auto-dispatch.ts";
16
16
  import { AutoSession } from "../auto/session.ts";
17
- import { closeDatabase, insertMilestone, insertSlice, openDatabase } from "../gsd-db.ts";
17
+ import { closeDatabase, insertAssessment, insertGateRow, insertMilestone, insertSlice, openDatabase } from "../gsd-db.ts";
18
18
 
19
19
  function makeBase(): string {
20
20
  const base = mkdtempSync(join(tmpdir(), "gsd-complete-dispatch-"));
@@ -225,6 +225,14 @@ describe("complete phase dispatch guard (#5683)", () => {
225
225
  base = makeBase();
226
226
  openDatabase(join(base, ".gsd", "gsd.db"));
227
227
  insertMilestone({ id: "M001", title: "Milestone One", status: "complete" });
228
+ insertSlice({ milestoneId: "M001", id: "S01", title: "Done", status: "complete" });
229
+ insertAssessment({
230
+ path: "milestones/M001/M001-VALIDATION.md",
231
+ milestoneId: "M001",
232
+ status: "pass",
233
+ scope: "milestone-validation",
234
+ fullContent: "verdict: pass",
235
+ });
228
236
 
229
237
  const ctx = buildDispatchCtx(base);
230
238
  ctx.state.phase = "complete";
@@ -234,6 +242,37 @@ describe("complete phase dispatch guard (#5683)", () => {
234
242
  assert.equal(result?.action, "stop");
235
243
  assert.equal(result?.reason, "All milestones complete.");
236
244
  });
245
+
246
+ test("blocks terminal stop when closed milestone still has pending gates", async () => {
247
+ base = makeBase();
248
+ openDatabase(join(base, ".gsd", "gsd.db"));
249
+ insertMilestone({ id: "M001", title: "Milestone One", status: "complete" });
250
+ insertSlice({ milestoneId: "M001", id: "S01", title: "Done", status: "complete" });
251
+ insertAssessment({
252
+ path: "milestones/M001/M001-VALIDATION.md",
253
+ milestoneId: "M001",
254
+ status: "pass",
255
+ scope: "milestone-validation",
256
+ fullContent: "verdict: pass",
257
+ });
258
+ insertGateRow({
259
+ milestoneId: "M001",
260
+ sliceId: "S01",
261
+ gateId: "Q3",
262
+ scope: "slice",
263
+ status: "pending",
264
+ });
265
+
266
+ const ctx = buildDispatchCtx(base);
267
+ ctx.state.phase = "complete";
268
+
269
+ const result = await rule.match(ctx);
270
+
271
+ assert.equal(result?.action, "stop");
272
+ assert.equal(result?.level, "warning");
273
+ assert.match(result?.reason ?? "", /closeout-consistency-blocked/);
274
+ assert.match(result?.reason ?? "", /quality gate Q3 is still pending/);
275
+ });
237
276
  });
238
277
 
239
278
  describe("complete milestone context recovery guard (#5831)", () => {
@@ -45,3 +45,30 @@ test("doctor fix respects git.manage_gitignore false (#4161)", async (t) => {
45
45
  assert.equal(readFileSync(join(dir, ".gitignore"), "utf-8"), "node_modules/\n");
46
46
  assert.equal(existsSync(join(dir, ".gsd", "PREFERENCES.md")), true);
47
47
  });
48
+
49
+ test("doctor fix resets run-uat counters at the dispatch cap", async (t) => {
50
+ const dir = createGitProject();
51
+ t.after(() => rmSync(dir, { recursive: true, force: true }));
52
+
53
+ const runtimeDir = join(dir, ".gsd", "runtime");
54
+ mkdirSync(runtimeDir, { recursive: true });
55
+ const counterPath = join(runtimeDir, "uat-count-M002-S01.json");
56
+ writeFileSync(
57
+ counterPath,
58
+ JSON.stringify({ count: 3, updatedAt: "2026-06-02T19:40:23.289Z" }) + "\n",
59
+ "utf-8",
60
+ );
61
+
62
+ const detect = await runGSDDoctor(dir);
63
+ const issue = detect.issues.find((candidate) => candidate.code === "uat_retry_exhausted");
64
+ assert.ok(issue, "doctor reports the exhausted UAT retry counter at the dispatch cap");
65
+ assert.equal(issue.unitId, "M002/S01");
66
+ assert.match(issue.message, /3 attempt\(s\)/);
67
+
68
+ const fixed = await runGSDDoctor(dir, { fix: true, scope: "M002/S02" });
69
+ assert.ok(
70
+ fixed.fixesApplied.some((fix) => fix.includes("reset exhausted run-uat retry counter for M002/S01")),
71
+ "doctor --fix resets the blocked counter even when the current displayed scope has advanced",
72
+ );
73
+ assert.equal(existsSync(counterPath), false);
74
+ });
@@ -106,7 +106,7 @@ test("ADR-011 P2: writeEscalationArtifact persists canonical JSON at tasks/T##-E
106
106
  assert.equal(row?.escalation_artifact_path, path);
107
107
  });
108
108
 
109
- test("ADR-011 P2: continueWithDefault=true sets awaiting_review (NOT pending) — no pause", (t) => {
109
+ test("ADR-011 P2: continueWithDefault=true sets awaiting_review (NOT pending)", (t) => {
110
110
  const base = makeBase();
111
111
  t.after(() => cleanup(base));
112
112
  seedCompletedTask(base, "T04");
@@ -126,7 +126,7 @@ test("ADR-011 P2: continueWithDefault=true sets awaiting_review (NOT pending)
126
126
  assert.equal(row?.escalation_awaiting_review, 1);
127
127
  });
128
128
 
129
- test("ADR-011 P2: detectPendingEscalation returns only pause-scoped escalations", (t) => {
129
+ test("ADR-011 P2: detectPendingEscalation pauses on unresolved awaiting_review escalations", (t) => {
130
130
  const base = makeBase();
131
131
  t.after(() => cleanup(base));
132
132
  seedCompletedTask(base, "T01");
@@ -147,7 +147,7 @@ test("ADR-011 P2: detectPendingEscalation returns only pause-scoped escalations"
147
147
 
148
148
  const tasks = [getTask("M001", "S01", "T01")!, getTask("M001", "S01", "T02")!];
149
149
  const id = detectPendingEscalation(tasks, base);
150
- assert.equal(id, "T02", "only T02 is pause-worthy; T01 is awaiting_review");
150
+ assert.equal(id, "T01", "unresolved awaiting_review escalations must pause before later tasks");
151
151
  });
152
152
 
153
153
  test("ADR-011 P2: resolveEscalation(accept) marks artifact + clears flags", (t) => {
@@ -677,26 +677,19 @@ test("ADR-011 P3 #23: concurrent escalations across parallel slices — only the
677
677
  assert.equal(detectPendingEscalation([getTask("M001", "S02", "T70")!], base), null);
678
678
  });
679
679
 
680
- test("ADR-011 P3 #24: continueWithDefault timeout late user response injects into the next task dispatched AFTER the response", (t) => {
681
- // Timeline this test pins (the "timeout" is implicit — it's just the
682
- // elapsed wall-clock where the loop keeps running after T80's
683
- // continueWithDefault=true write):
680
+ test("ADR-011 P3 #24: continueWithDefault requires explicit response before override injection", (t) => {
681
+ // Timeline this test pins:
684
682
  //
685
- // 1. T80 writes continueWithDefault=true → awaiting_review=1, loop
686
- // continues dispatching T81, T82. Neither claim fires because the
687
- // user has not responded (pins Bug 2 behavior, tested at line 244).
688
- // 2. The user responds LATE (after T81/T82 already dispatched).
689
- // 3. The very next prompt build (for T83) claims the override exactly
690
- // once. T81/T82 are in the past — they must not retroactively
691
- // receive the injection even though they ran during the window.
683
+ // 1. T80 writes continueWithDefault=true → awaiting_review=1.
684
+ // 2. Scheduler detection pauses on T80 instead of treating silence as
685
+ // consent. Prompt injection still waits until the user responds.
686
+ // 3. After the response, the next prompt build claims the override once.
692
687
  const base = makeBase();
693
688
  t.after(() => cleanup(base));
694
689
  seedCompletedTask(base, "T80");
695
- seedCompletedTask(base, "T81");
696
- seedCompletedTask(base, "T82");
697
690
  seedCompletedTask(base, "T83");
698
691
 
699
- // Phase 1 — T80 escalates with continueWithDefault=true, loop continues.
692
+ // Phase 1 — T80 escalates with continueWithDefault=true.
700
693
  writeEscalationArtifact(base, buildEscalationArtifact({
701
694
  taskId: "T80", sliceId: "S01", milestoneId: "M001",
702
695
  question: "Which cache strategy?", options: sampleOptions,
@@ -704,21 +697,17 @@ test("ADR-011 P3 #24: continueWithDefault timeout — late user response injects
704
697
  continueWithDefault: true,
705
698
  }));
706
699
 
707
- // T80 is awaiting_review (not pending) scheduler does not pause.
700
+ // T80 is awaiting_review (not pending), but scheduler detection still
701
+ // pauses until the user explicitly responds.
708
702
  assert.equal(getTask("M001", "S01", "T80")?.escalation_awaiting_review, 1);
709
703
  assert.equal(getTask("M001", "S01", "T80")?.escalation_pending, 0);
710
- assert.equal(detectPendingEscalation([getTask("M001", "S01", "T80")!], base), null);
704
+ assert.equal(detectPendingEscalation([getTask("M001", "S01", "T80")!], base), "T80");
711
705
 
712
- // T81 + T82 dispatch during the response window — neither gets the injection.
706
+ // Prompt injection must still wait for a response.
713
707
  assert.equal(
714
708
  claimOverrideForInjection(base, "M001", "S01"),
715
709
  null,
716
- "T81's prompt build must not claim the unresolved awaiting_review",
717
- );
718
- assert.equal(
719
- claimOverrideForInjection(base, "M001", "S01"),
720
- null,
721
- "T82's prompt build must also not claim the unresolved awaiting_review",
710
+ "unresolved awaiting_review must not be claimed as a default response",
722
711
  );
723
712
 
724
713
  // The response window remains open across N tasks — still no override applied.
@@ -728,7 +717,7 @@ test("ADR-011 P3 #24: continueWithDefault timeout — late user response injects
728
717
  "applied_at must stay null throughout the response window",
729
718
  );
730
719
 
731
- // Phase 2 — user responds LATE with a different option than the recommendation.
720
+ // Phase 2 — user responds with a different option than the recommendation.
732
721
  const resolveResult = resolveEscalation(
733
722
  base, "M001", "S01", "T80", "B", "after reviewing, B is the call",
734
723
  );
@@ -41,3 +41,23 @@ test("forensics prompt routes issue creation through bash tool, not github_issue
41
41
  "Prompt must instruct use of the bash tool for issue creation",
42
42
  );
43
43
  });
44
+
45
+ test("forensics prompt provides paste-once fallback when bash is unavailable", () => {
46
+ const prompt = readPrompt("forensics");
47
+
48
+ assert.match(
49
+ prompt,
50
+ /If `bash` is unavailable/i,
51
+ "Prompt must branch when bash cannot be activated",
52
+ );
53
+ assert.match(
54
+ prompt,
55
+ /paste-once shell script/i,
56
+ "Prompt must provide a user-runnable fallback instead of an impossible tool call",
57
+ );
58
+ assert.match(
59
+ prompt,
60
+ /Searching closed issues for possible duplicates/i,
61
+ "Fallback script must preserve the duplicate-search step for the user",
62
+ );
63
+ });
@@ -24,13 +24,16 @@ test("forensics prompt renders compact investigation and issue routing guidance"
24
24
  forensicData: "stuck-detected event for execute-task/M001/S01/T01",
25
25
  gsdSourceDir: process.env.GSD_TEST_WORKSPACE_ROOT ?? process.cwd(),
26
26
  dedupSection: "No duplicate issue found.",
27
+ toolingSection: "## Filing Tool Availability\n\n- `bash`: available\n- `write`: available",
27
28
  });
28
29
 
29
30
  assert.match(prompt, /Investigation Protocol/);
31
+ assert.match(prompt, /Filing Tool Availability/);
30
32
  assert.match(prompt, /gsd_milestone_status/);
31
33
  assert.match(prompt, /sqlite3 .gsd\/gsd.db/);
32
34
  assert.match(prompt, /gh issue create --repo open-gsd\/gsd-pi/);
33
35
  assert.match(prompt, /Do NOT use the `github_issues` tool/);
36
+ assert.match(prompt, /paste-once shell script/);
34
37
  assert.match(prompt, /Redaction Rules/);
35
38
  assert.doesNotMatch(prompt, /\{\{[a-zA-Z][a-zA-Z0-9_]*\}\}/);
36
39
  });
@@ -0,0 +1,69 @@
1
+ // Project/App: gsd-pi
2
+ // File Purpose: Verifies /gsd forensics scopes issue-filing tools for its queued turn.
3
+
4
+ import test from "node:test";
5
+ import assert from "node:assert/strict";
6
+
7
+ import {
8
+ applyForensicsToolScope,
9
+ buildForensicsToolingSection,
10
+ createForensicsToolScope,
11
+ restoreForensicsToolScope,
12
+ } from "../forensics.ts";
13
+
14
+ function tool(name: string): { name: string } {
15
+ return { name };
16
+ }
17
+
18
+ test("forensics adds registered filing tools for the queued turn and restores the prior tools", () => {
19
+ const originalTools = ["read"];
20
+ let activeTools = [...originalTools];
21
+ const pi = {
22
+ getActiveTools: () => [...activeTools],
23
+ getAllTools: () => ["read", "bash", "write"].map(tool),
24
+ setActiveTools: (tools: string[]) => {
25
+ activeTools = [...tools];
26
+ },
27
+ };
28
+
29
+ const scope = createForensicsToolScope(pi as any);
30
+
31
+ assert.deepEqual(scope.savedTools, originalTools);
32
+ assert.deepEqual(scope.activeToolsForTurn, ["read", "bash", "write"]);
33
+ assert.deepEqual(scope.availableFilingTools, ["bash", "write"]);
34
+ assert.deepEqual(scope.missingFilingTools, []);
35
+ assert.equal(scope.toolsChanged, true);
36
+ assert.deepEqual(activeTools, originalTools, "scope creation must not mutate active tools");
37
+
38
+ applyForensicsToolScope(pi as any, scope);
39
+ assert.deepEqual(activeTools, ["read", "bash", "write"]);
40
+
41
+ const toolingSection = buildForensicsToolingSection(scope);
42
+ assert.match(toolingSection, /`bash`: available/);
43
+ assert.match(toolingSection, /`write`: available/);
44
+ assert.match(toolingSection, /GitHub duplicate-check and issue-creation protocols/);
45
+
46
+ restoreForensicsToolScope(pi as any, scope);
47
+ assert.deepEqual(activeTools, originalTools);
48
+ });
49
+
50
+ test("forensics tooling guidance falls back when bash is not registered", () => {
51
+ let activeTools = ["read"];
52
+ const pi = {
53
+ getActiveTools: () => [...activeTools],
54
+ getAllTools: () => ["read", "write"].map(tool),
55
+ setActiveTools: (tools: string[]) => {
56
+ activeTools = [...tools];
57
+ },
58
+ };
59
+
60
+ const scope = createForensicsToolScope(pi as any);
61
+
62
+ assert.deepEqual(scope.availableFilingTools, ["write"]);
63
+ assert.deepEqual(scope.missingFilingTools, ["bash"]);
64
+ assert.deepEqual(scope.activeToolsForTurn, ["read", "write"]);
65
+
66
+ const toolingSection = buildForensicsToolingSection(scope);
67
+ assert.match(toolingSection, /`bash`: unavailable/);
68
+ assert.match(toolingSection, /paste-once shell script fallback/);
69
+ });