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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (329) hide show
  1. package/dist/cli.js +3 -2
  2. package/dist/help-text.js +10 -6
  3. package/dist/resources/.managed-resources-content-hash +1 -1
  4. package/dist/resources/extensions/browser-tools/engine/managed-gsd-browser.js +495 -0
  5. package/dist/resources/extensions/browser-tools/engine/selection.js +16 -0
  6. package/dist/resources/extensions/browser-tools/extension-manifest.json +2 -2
  7. package/dist/resources/extensions/browser-tools/index.js +57 -9
  8. package/dist/resources/extensions/browser-tools/package.json +5 -1
  9. package/dist/resources/extensions/gsd/auto/orchestrator.js +0 -1
  10. package/dist/resources/extensions/gsd/auto-dashboard.js +77 -13
  11. package/dist/resources/extensions/gsd/auto-dispatch.js +16 -0
  12. package/dist/resources/extensions/gsd/auto-post-unit.js +21 -3
  13. package/dist/resources/extensions/gsd/auto-prompts.js +63 -22
  14. package/dist/resources/extensions/gsd/auto-recovery.js +3 -4
  15. package/dist/resources/extensions/gsd/auto-runtime-state.js +3 -0
  16. package/dist/resources/extensions/gsd/auto-tool-tracking.js +1 -1
  17. package/dist/resources/extensions/gsd/auto-unit-tool-scope.js +18 -66
  18. package/dist/resources/extensions/gsd/auto-worktree.js +18 -5
  19. package/dist/resources/extensions/gsd/auto.js +9 -2
  20. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +20 -14
  21. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +28 -13
  22. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +18 -29
  23. package/dist/resources/extensions/gsd/browser-evidence.js +29 -2
  24. package/dist/resources/extensions/gsd/closeout-consistency-gate.js +61 -0
  25. package/dist/resources/extensions/gsd/commands/handlers/ops.js +2 -2
  26. package/dist/resources/extensions/gsd/commands-handlers.js +76 -11
  27. package/dist/resources/extensions/gsd/commands-mcp-status.js +2 -1
  28. package/dist/resources/extensions/gsd/dashboard-overlay.js +21 -7
  29. package/dist/resources/extensions/gsd/docs/preferences-reference.md +8 -0
  30. package/dist/resources/extensions/gsd/doctor-runtime-checks.js +2 -2
  31. package/dist/resources/extensions/gsd/escalation.js +4 -4
  32. package/dist/resources/extensions/gsd/forensics.js +74 -2
  33. package/dist/resources/extensions/gsd/gsd-db.js +5 -2
  34. package/dist/resources/extensions/gsd/guided-flow.js +118 -175
  35. package/dist/resources/extensions/gsd/mcp-project-config.js +9 -76
  36. package/dist/resources/extensions/gsd/memory-store.js +4 -1
  37. package/dist/resources/extensions/gsd/milestone-closeout.js +3 -1
  38. package/dist/resources/extensions/gsd/pending-auto-start.js +0 -1
  39. package/dist/resources/extensions/gsd/post-unit-hooks.js +9 -0
  40. package/dist/resources/extensions/gsd/preferences-validation.js +39 -0
  41. package/dist/resources/extensions/gsd/prompt-loader.js +7 -0
  42. package/dist/resources/extensions/gsd/prompts/forensics.md +61 -1
  43. package/dist/resources/extensions/gsd/prompts/gate-evaluate.md +3 -1
  44. package/dist/resources/extensions/gsd/prompts/parallel-research-slices.md +3 -1
  45. package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  46. package/dist/resources/extensions/gsd/prompts/reactive-execute.md +3 -1
  47. package/dist/resources/extensions/gsd/prompts/run-uat.md +25 -21
  48. package/dist/resources/extensions/gsd/prompts/validate-milestone.md +3 -3
  49. package/dist/resources/extensions/gsd/recovery-classification.js +20 -0
  50. package/dist/resources/extensions/gsd/rule-registry.js +428 -52
  51. package/dist/resources/extensions/gsd/state.js +2 -2
  52. package/dist/resources/extensions/gsd/templates/plan.md +3 -1
  53. package/dist/resources/extensions/gsd/tool-contract.js +5 -0
  54. package/dist/resources/extensions/gsd/tool-presentation-plan.js +17 -7
  55. package/dist/resources/extensions/gsd/tools/complete-slice.js +15 -1
  56. package/dist/resources/extensions/gsd/tools/complete-task.js +11 -1
  57. package/dist/resources/extensions/gsd/tools/validate-milestone.js +46 -16
  58. package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +132 -18
  59. package/dist/resources/extensions/gsd/unit-tool-contracts.js +169 -0
  60. package/dist/resources/extensions/gsd/verdict-parser.js +59 -15
  61. package/dist/resources/extensions/gsd/verification-gate.js +72 -1
  62. package/dist/resources/extensions/gsd/workflow-mcp.js +3 -75
  63. package/dist/resources/extensions/shared/gsd-browser-cli.js +145 -0
  64. package/dist/rtk.d.ts +7 -1
  65. package/dist/rtk.js +27 -11
  66. package/dist/update-check.d.ts +15 -1
  67. package/dist/update-check.js +87 -12
  68. package/dist/update-cmd.d.ts +1 -0
  69. package/dist/update-cmd.js +53 -2
  70. package/dist/web/standalone/.next/BUILD_ID +1 -1
  71. package/dist/web/standalone/.next/app-path-routes-manifest.json +7 -7
  72. package/dist/web/standalone/.next/build-manifest.json +2 -2
  73. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  74. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  75. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  76. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  77. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  78. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  79. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  80. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  81. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  82. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  83. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  84. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  85. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  86. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  87. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  88. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  89. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  90. package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
  91. package/dist/web/standalone/.next/server/app/index.html +1 -1
  92. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  93. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  94. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  95. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  96. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  97. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  98. package/dist/web/standalone/.next/server/app-paths-manifest.json +7 -7
  99. package/dist/web/standalone/.next/server/chunks/8357.js +1 -1
  100. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  101. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  102. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  103. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  104. package/package.json +4 -2
  105. package/packages/cloud-mcp-gateway/package.json +2 -2
  106. package/packages/contracts/package.json +1 -1
  107. package/packages/daemon/package.json +4 -4
  108. package/packages/gsd-agent-core/dist/agent-session.d.ts +9 -0
  109. package/packages/gsd-agent-core/dist/agent-session.d.ts.map +1 -1
  110. package/packages/gsd-agent-core/dist/agent-session.js +32 -0
  111. package/packages/gsd-agent-core/dist/agent-session.js.map +1 -1
  112. package/packages/gsd-agent-core/dist/index.d.ts +1 -0
  113. package/packages/gsd-agent-core/dist/index.d.ts.map +1 -1
  114. package/packages/gsd-agent-core/dist/index.js +1 -0
  115. package/packages/gsd-agent-core/dist/index.js.map +1 -1
  116. package/packages/gsd-agent-core/dist/session/agent-session-compaction.d.ts +2 -0
  117. package/packages/gsd-agent-core/dist/session/agent-session-compaction.d.ts.map +1 -1
  118. package/packages/gsd-agent-core/dist/session/agent-session-compaction.js +8 -2
  119. package/packages/gsd-agent-core/dist/session/agent-session-compaction.js.map +1 -1
  120. package/packages/gsd-agent-core/dist/session/agent-session-host.d.ts +7 -0
  121. package/packages/gsd-agent-core/dist/session/agent-session-host.d.ts.map +1 -1
  122. package/packages/gsd-agent-core/dist/session/agent-session-host.js.map +1 -1
  123. package/packages/gsd-agent-core/dist/session/agent-session-prompt.d.ts.map +1 -1
  124. package/packages/gsd-agent-core/dist/session/agent-session-prompt.js +69 -1
  125. package/packages/gsd-agent-core/dist/session/agent-session-prompt.js.map +1 -1
  126. package/packages/gsd-agent-core/dist/turn-latency.d.ts +47 -0
  127. package/packages/gsd-agent-core/dist/turn-latency.d.ts.map +1 -0
  128. package/packages/gsd-agent-core/dist/turn-latency.js +123 -0
  129. package/packages/gsd-agent-core/dist/turn-latency.js.map +1 -0
  130. package/packages/gsd-agent-core/package.json +6 -6
  131. package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/gsd-widget-prototype.d.ts +21 -0
  132. package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/gsd-widget-prototype.d.ts.map +1 -0
  133. package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/gsd-widget-prototype.js +213 -0
  134. package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/gsd-widget-prototype.js.map +1 -0
  135. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  136. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js +5 -0
  137. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js.map +1 -1
  138. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  139. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js +20 -0
  140. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  141. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/input-controller.d.ts.map +1 -1
  142. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/input-controller.js +7 -1
  143. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/input-controller.js.map +1 -1
  144. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-command-handlers.d.ts.map +1 -1
  145. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-command-handlers.js +6 -0
  146. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-command-handlers.js.map +1 -1
  147. package/packages/gsd-agent-modes/package.json +7 -7
  148. package/packages/mcp-server/dist/remote-questions.d.ts.map +1 -1
  149. package/packages/mcp-server/dist/remote-questions.js +23 -9
  150. package/packages/mcp-server/dist/remote-questions.js.map +1 -1
  151. package/packages/mcp-server/dist/workflow-tools.js +2 -2
  152. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  153. package/packages/mcp-server/package.json +3 -3
  154. package/packages/native/package.json +1 -1
  155. package/packages/pi-agent-core/dist/agent-loop.js +42 -3
  156. package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
  157. package/packages/pi-agent-core/dist/agent.d.ts +5 -1
  158. package/packages/pi-agent-core/dist/agent.d.ts.map +1 -1
  159. package/packages/pi-agent-core/dist/agent.js +2 -0
  160. package/packages/pi-agent-core/dist/agent.js.map +1 -1
  161. package/packages/pi-agent-core/dist/harness/agent-harness.d.ts.map +1 -1
  162. package/packages/pi-agent-core/dist/harness/agent-harness.js +3 -1
  163. package/packages/pi-agent-core/dist/harness/agent-harness.js.map +1 -1
  164. package/packages/pi-agent-core/dist/harness/types.d.ts +1 -0
  165. package/packages/pi-agent-core/dist/harness/types.d.ts.map +1 -1
  166. package/packages/pi-agent-core/dist/harness/types.js.map +1 -1
  167. package/packages/pi-agent-core/dist/types.d.ts +6 -1
  168. package/packages/pi-agent-core/dist/types.d.ts.map +1 -1
  169. package/packages/pi-agent-core/dist/types.js.map +1 -1
  170. package/packages/pi-agent-core/package.json +1 -1
  171. package/packages/pi-ai/dist/api-registry.d.ts +2 -0
  172. package/packages/pi-ai/dist/api-registry.d.ts.map +1 -1
  173. package/packages/pi-ai/dist/api-registry.js +23 -0
  174. package/packages/pi-ai/dist/api-registry.js.map +1 -1
  175. package/packages/pi-ai/dist/models.generated.d.ts +74 -6
  176. package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
  177. package/packages/pi-ai/dist/models.generated.js +78 -10
  178. package/packages/pi-ai/dist/models.generated.js.map +1 -1
  179. package/packages/pi-ai/dist/stream.js +6 -6
  180. package/packages/pi-ai/dist/stream.js.map +1 -1
  181. package/packages/pi-ai/package.json +1 -1
  182. package/packages/pi-coding-agent/dist/core/extensions/extension-upstream-types.d.ts +3 -0
  183. package/packages/pi-coding-agent/dist/core/extensions/extension-upstream-types.d.ts.map +1 -1
  184. package/packages/pi-coding-agent/dist/core/extensions/extension-upstream-types.js.map +1 -1
  185. package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
  186. package/packages/pi-coding-agent/dist/core/model-registry.js +2 -2
  187. package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
  188. package/packages/pi-coding-agent/dist/core/tools/bash.js +2 -2
  189. package/packages/pi-coding-agent/dist/core/tools/bash.js.map +1 -1
  190. package/packages/pi-coding-agent/dist/core/tools/edit.d.ts.map +1 -1
  191. package/packages/pi-coding-agent/dist/core/tools/edit.js +3 -2
  192. package/packages/pi-coding-agent/dist/core/tools/edit.js.map +1 -1
  193. package/packages/pi-coding-agent/dist/core/tools/render-utils.d.ts +1 -0
  194. package/packages/pi-coding-agent/dist/core/tools/render-utils.d.ts.map +1 -1
  195. package/packages/pi-coding-agent/dist/core/tools/render-utils.js +6 -0
  196. package/packages/pi-coding-agent/dist/core/tools/render-utils.js.map +1 -1
  197. package/packages/pi-coding-agent/dist/core/tools/write.d.ts.map +1 -1
  198. package/packages/pi-coding-agent/dist/core/tools/write.js +3 -2
  199. package/packages/pi-coding-agent/dist/core/tools/write.js.map +1 -1
  200. package/packages/pi-coding-agent/package.json +7 -7
  201. package/packages/pi-tui/package.json +1 -1
  202. package/packages/rpc-client/package.json +2 -2
  203. package/pkg/package.json +1 -1
  204. package/src/resources/extensions/browser-tools/engine/managed-gsd-browser.ts +579 -0
  205. package/src/resources/extensions/browser-tools/engine/selection.ts +19 -0
  206. package/src/resources/extensions/browser-tools/extension-manifest.json +2 -2
  207. package/src/resources/extensions/browser-tools/index.ts +60 -9
  208. package/src/resources/extensions/browser-tools/package.json +5 -1
  209. package/src/resources/extensions/browser-tools/tests/browser-engine-selection.test.mjs +35 -0
  210. package/src/resources/extensions/browser-tools/tests/managed-gsd-browser-tools.test.mjs +33 -0
  211. package/src/resources/extensions/gsd/auto/orchestrator.ts +0 -1
  212. package/src/resources/extensions/gsd/auto-dashboard.ts +82 -14
  213. package/src/resources/extensions/gsd/auto-dispatch.ts +19 -0
  214. package/src/resources/extensions/gsd/auto-post-unit.ts +28 -2
  215. package/src/resources/extensions/gsd/auto-prompts.ts +97 -15
  216. package/src/resources/extensions/gsd/auto-recovery.ts +3 -3
  217. package/src/resources/extensions/gsd/auto-runtime-state.ts +4 -0
  218. package/src/resources/extensions/gsd/auto-tool-tracking.ts +1 -1
  219. package/src/resources/extensions/gsd/auto-unit-tool-scope.ts +43 -74
  220. package/src/resources/extensions/gsd/auto-worktree.ts +23 -5
  221. package/src/resources/extensions/gsd/auto.ts +12 -2
  222. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +20 -14
  223. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +32 -13
  224. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +50 -54
  225. package/src/resources/extensions/gsd/browser-evidence.ts +26 -2
  226. package/src/resources/extensions/gsd/closeout-consistency-gate.ts +137 -0
  227. package/src/resources/extensions/gsd/commands/handlers/ops.ts +2 -2
  228. package/src/resources/extensions/gsd/commands-handlers.ts +76 -11
  229. package/src/resources/extensions/gsd/commands-mcp-status.ts +2 -1
  230. package/src/resources/extensions/gsd/dashboard-overlay.ts +28 -7
  231. package/src/resources/extensions/gsd/docs/preferences-reference.md +8 -0
  232. package/src/resources/extensions/gsd/doctor-runtime-checks.ts +2 -2
  233. package/src/resources/extensions/gsd/escalation.ts +4 -4
  234. package/src/resources/extensions/gsd/forensics.ts +99 -5
  235. package/src/resources/extensions/gsd/gsd-db.ts +5 -2
  236. package/src/resources/extensions/gsd/guided-flow.ts +214 -216
  237. package/src/resources/extensions/gsd/mcp-project-config.ts +13 -78
  238. package/src/resources/extensions/gsd/memory-store.ts +4 -1
  239. package/src/resources/extensions/gsd/milestone-closeout.ts +3 -1
  240. package/src/resources/extensions/gsd/pending-auto-start.ts +0 -2
  241. package/src/resources/extensions/gsd/post-unit-hooks.ts +14 -1
  242. package/src/resources/extensions/gsd/preferences-validation.ts +36 -0
  243. package/src/resources/extensions/gsd/prompt-loader.ts +8 -0
  244. package/src/resources/extensions/gsd/prompts/forensics.md +61 -1
  245. package/src/resources/extensions/gsd/prompts/gate-evaluate.md +3 -1
  246. package/src/resources/extensions/gsd/prompts/parallel-research-slices.md +3 -1
  247. package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  248. package/src/resources/extensions/gsd/prompts/reactive-execute.md +3 -1
  249. package/src/resources/extensions/gsd/prompts/run-uat.md +25 -21
  250. package/src/resources/extensions/gsd/prompts/validate-milestone.md +3 -3
  251. package/src/resources/extensions/gsd/recovery-classification.ts +20 -0
  252. package/src/resources/extensions/gsd/rule-registry.ts +558 -58
  253. package/src/resources/extensions/gsd/rule-types.ts +2 -0
  254. package/src/resources/extensions/gsd/state.ts +2 -2
  255. package/src/resources/extensions/gsd/templates/plan.md +3 -1
  256. package/src/resources/extensions/gsd/tests/auto-dashboard.test.ts +105 -4
  257. package/src/resources/extensions/gsd/tests/auto-orchestrator.test.ts +37 -0
  258. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +10 -2
  259. package/src/resources/extensions/gsd/tests/auto-start-bootstrap-await-3420.test.ts +4 -1
  260. package/src/resources/extensions/gsd/tests/auto-warning-noise-regression.test.ts +12 -2
  261. package/src/resources/extensions/gsd/tests/browser-evidence.test.ts +142 -0
  262. package/src/resources/extensions/gsd/tests/check-auto-start-pending-gate.test.ts +9 -15
  263. package/src/resources/extensions/gsd/tests/check-auto-start-ready-guard.test.ts +26 -16
  264. package/src/resources/extensions/gsd/tests/commands-dispatcher-unmerged-milestone.test.ts +21 -0
  265. package/src/resources/extensions/gsd/tests/complete-milestone-excerpt.test.ts +30 -0
  266. package/src/resources/extensions/gsd/tests/complete-slice-verification-gate.test.ts +42 -0
  267. package/src/resources/extensions/gsd/tests/dashboard-overlay.test.ts +45 -0
  268. package/src/resources/extensions/gsd/tests/deep-planning-mode-dispatch.test.ts +53 -0
  269. package/src/resources/extensions/gsd/tests/discuss-milestone-structured-questions.test.ts +31 -0
  270. package/src/resources/extensions/gsd/tests/dispatch-complete-milestone-guard.test.ts +40 -1
  271. package/src/resources/extensions/gsd/tests/doctor-runtime-checks.test.ts +27 -0
  272. package/src/resources/extensions/gsd/tests/escalation.test.ts +16 -27
  273. package/src/resources/extensions/gsd/tests/forensics-issue-routing.test.ts +20 -0
  274. package/src/resources/extensions/gsd/tests/forensics-prompt-rendering.test.ts +3 -0
  275. package/src/resources/extensions/gsd/tests/forensics-tool-scope.test.ts +69 -0
  276. package/src/resources/extensions/gsd/tests/gate-1b-orphan-discrimination.test.ts +31 -79
  277. package/src/resources/extensions/gsd/tests/guided-discuss-milestone-prompt-rendering.test.ts +40 -1
  278. package/src/resources/extensions/gsd/tests/guided-dispatch-root.test.ts +86 -0
  279. package/src/resources/extensions/gsd/tests/guided-flow-session-isolation.test.ts +5 -3
  280. package/src/resources/extensions/gsd/tests/guided-flow-state-rebuild.test.ts +40 -4
  281. package/src/resources/extensions/gsd/tests/guided-flow.test.ts +12 -9
  282. package/src/resources/extensions/gsd/tests/integration/auto-recovery.test.ts +4 -4
  283. package/src/resources/extensions/gsd/tests/integration/auto-worktree-milestone-merge.test.ts +8 -0
  284. package/src/resources/extensions/gsd/tests/integration/parallel-merge.test.ts +16 -0
  285. package/src/resources/extensions/gsd/tests/integration/run-uat.test.ts +69 -10
  286. package/src/resources/extensions/gsd/tests/mcp-project-config.test.ts +32 -0
  287. package/src/resources/extensions/gsd/tests/mcp-status.test.ts +2 -0
  288. package/src/resources/extensions/gsd/tests/memory-maintenance.test.ts +39 -8
  289. package/src/resources/extensions/gsd/tests/merge-closeout-consistency-gate.test.ts +63 -0
  290. package/src/resources/extensions/gsd/tests/merge-db-cycle.test.ts +10 -1
  291. package/src/resources/extensions/gsd/tests/milestone-closeout.test.ts +9 -1
  292. package/src/resources/extensions/gsd/tests/new-milestone-discuss-routing.test.ts +3 -3
  293. package/src/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +9 -0
  294. package/src/resources/extensions/gsd/tests/post-unit-hooks.test.ts +157 -0
  295. package/src/resources/extensions/gsd/tests/post-unit-retry-on-orchestrator-bridge.test.ts +179 -0
  296. package/src/resources/extensions/gsd/tests/preferences.test.ts +29 -0
  297. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +61 -1
  298. package/src/resources/extensions/gsd/tests/prompt-loader-extension-dir.test.ts +14 -0
  299. package/src/resources/extensions/gsd/tests/queued-discuss-fast-path.test.ts +7 -8
  300. package/src/resources/extensions/gsd/tests/register-hooks-depth-verification.test.ts +44 -0
  301. package/src/resources/extensions/gsd/tests/rule-registry.test.ts +75 -0
  302. package/src/resources/extensions/gsd/tests/run-uat-composer.test.ts +4 -0
  303. package/src/resources/extensions/gsd/tests/runtime-invariant-modules.test.ts +36 -0
  304. package/src/resources/extensions/gsd/tests/session-start-footer.test.ts +100 -0
  305. package/src/resources/extensions/gsd/tests/state-reconciliation-drift.test.ts +139 -0
  306. package/src/resources/extensions/gsd/tests/token-tool-gating.test.ts +4 -4
  307. package/src/resources/extensions/gsd/tests/tool-invocation-error-loop-break.test.ts +19 -0
  308. package/src/resources/extensions/gsd/tests/tool-param-optionality.test.ts +7 -1
  309. package/src/resources/extensions/gsd/tests/validate-milestone-prompt-verification-classes.test.ts +6 -3
  310. package/src/resources/extensions/gsd/tests/validate-milestone-write-order.test.ts +133 -0
  311. package/src/resources/extensions/gsd/tests/verification-gate.test.ts +51 -0
  312. package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +351 -0
  313. package/src/resources/extensions/gsd/tests/write-gate-planning-unit.test.ts +15 -0
  314. package/src/resources/extensions/gsd/tool-contract.ts +6 -0
  315. package/src/resources/extensions/gsd/tool-presentation-plan.ts +38 -8
  316. package/src/resources/extensions/gsd/tools/complete-slice.ts +14 -1
  317. package/src/resources/extensions/gsd/tools/complete-task.ts +20 -2
  318. package/src/resources/extensions/gsd/tools/validate-milestone.ts +46 -15
  319. package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +163 -20
  320. package/src/resources/extensions/gsd/types.ts +69 -5
  321. package/src/resources/extensions/gsd/unit-tool-contracts.ts +186 -0
  322. package/src/resources/extensions/gsd/verdict-parser.ts +54 -13
  323. package/src/resources/extensions/gsd/verification-gate.ts +87 -1
  324. package/src/resources/extensions/gsd/workflow-mcp.ts +3 -75
  325. package/src/resources/extensions/shared/gsd-browser-cli.ts +172 -0
  326. package/src/resources/extensions/gsd/tests/gate-1b-recovery-bound-corrections.test.ts +0 -246
  327. package/src/resources/extensions/gsd/tests/gate-1b-recovery-bound.test.ts +0 -218
  328. /package/dist/web/standalone/.next/static/{eRWf-RI9bzbrwEurm_3uI → 9y3LeeR2uGr2yRj9RjY3D}/_buildManifest.js +0 -0
  329. /package/dist/web/standalone/.next/static/{eRWf-RI9bzbrwEurm_3uI → 9y3LeeR2uGr2yRj9RjY3D}/_ssgManifest.js +0 -0
@@ -32,6 +32,8 @@ export interface RuleLifecycle {
32
32
  retry_on?: string;
33
33
  /** Max times this hook can fire for the same trigger unit. */
34
34
  max_cycles?: number;
35
+ /** Whether this hook is advisory or blocking. */
36
+ criticality?: PostUnitHookConfig["criticality"];
35
37
  /** Idempotency key pattern for this hook. */
36
38
  idempotency_key?: string;
37
39
  }
@@ -896,8 +896,8 @@ export async function deriveStateFromDb(
896
896
  }
897
897
 
898
898
  // ADR-011 Phase 2: pause-on-escalation takes precedence over dispatching the
899
- // next task. `awaiting_review` tasks (continueWithDefault=true) are NOT
900
- // surfaced here they let the loop continue.
899
+ // next task. `awaiting_review` tasks (continueWithDefault=true) still pause
900
+ // here so silence is never treated as consent.
901
901
  //
902
902
  // We do NOT gate this on `phases.mid_execution_escalation` — creation of
903
903
  // new escalations is gated at the write site (tools/complete-task.ts:315),
@@ -132,14 +132,16 @@
132
132
  Verify field rules:
133
133
  - MUST be a mechanically executable command: `npm test`, `grep -q "pattern" file`, `test -f path`
134
134
  - MUST NOT use shell pipes, redirects, semicolons, backticks, command substitution, output trimming, or grep regex alternation with `|`
135
+ - For absence checks, use `! grep -q "pattern" file` or `! rg -q "pattern" file`; do not use `grep -c` or `rg -c` to assert zero matches because count commands exit 1 when they find zero matches
135
136
  - MUST NOT use inline `node -e` assertions for verification; put assertions in a real test file and run it with `node --test` or a package test script
136
137
  - For content/document tasks: verify file existence, section count, YAML validity, or word count
137
138
  NOT exact phrasing, specific formulas, or "zero TBD" aspirational criteria
138
139
  - If no command can verify the output, write: "Manual review — file exists and is non-empty"
139
140
  - BAD: `python3 -m pytest tests/ -q --tb=short 2>&1 | tail -5`
141
+ - BAD: `grep -c "old_api" src/index.ts`
140
142
  - BAD: "Sections 3.1 and 3.2 exist with exact formulas. Zero TBD/TODO."
141
143
  - GOOD: `python3 -m pytest tests/ -q --tb=short`
142
- - GOOD: `node --test tests/verify-doc.test.js`, `grep -q "Required heading" doc.md`, `test -s doc.md`
144
+ - GOOD: `node --test tests/verify-doc.test.js`, `grep -q "Required heading" doc.md`, `! grep -q "old_api" src/index.ts`, `test -s doc.md`
143
145
 
144
146
  Integration closure rule:
145
147
  - At least one slice in any multi-boundary milestone should perform real composition/wiring, not just contract hardening
@@ -4,6 +4,7 @@ import { mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
4
4
  import { join } from "node:path";
5
5
  import { tmpdir } from "node:os";
6
6
  import { execFileSync } from "node:child_process";
7
+ import { visibleWidth } from "@gsd/pi-tui";
7
8
 
8
9
  import {
9
10
  unitVerb,
@@ -57,6 +58,17 @@ function cleanup(dir: string): void {
57
58
  }
58
59
  }
59
60
 
61
+ function assertLinesFit(lines: string[], width: number): void {
62
+ for (const line of lines) {
63
+ assert.ok(
64
+ visibleWidth(line) <= width,
65
+ `line exceeds width ${width}: ${visibleWidth(line)} "${line}"`,
66
+ );
67
+ }
68
+ }
69
+
70
+ type RenderableWidget = { render(width: number): string[]; invalidate(): void; dispose?: () => void };
71
+
60
72
  // ─── unitVerb ─────────────────────────────────────────────────────────────
61
73
 
62
74
  test("unitVerb maps known unit types to verbs", () => {
@@ -662,6 +674,95 @@ test("updateProgressWidget full mode keeps footer-owned signals out of auto deck
662
674
  assert.doesNotMatch(rendered, /\$/, "footer owns session cost display");
663
675
  });
664
676
 
677
+ test("updateProgressWidget small mode renders the dense horizontal grid", (t) => {
678
+ const dir = makeTempDir("small-dense-grid");
679
+ const homeDir = makeTempDir("small-dense-grid-home");
680
+ const projectPrefsPath = join(dir, ".gsd", "preferences.md");
681
+ const globalPrefsPath = join(homeDir, ".gsd", "preferences.md");
682
+ mkdirSync(join(dir, ".gsd"), { recursive: true });
683
+ mkdirSync(join(homeDir, ".gsd"), { recursive: true });
684
+ writeFileSync(projectPrefsPath, "---\nversion: 1\nwidget_mode: full\n---\n", "utf-8");
685
+ writeFileSync(globalPrefsPath, "---\nversion: 1\nwidget_mode: full\n---\n", "utf-8");
686
+
687
+ const holder: { widget?: RenderableWidget } = {};
688
+
689
+ t.after(() => {
690
+ holder.widget?.dispose?.();
691
+ closeDatabase();
692
+ clearSliceProgressCache();
693
+ _resetWidgetModeForTests();
694
+ cleanup(dir);
695
+ cleanup(homeDir);
696
+ });
697
+
698
+ openDatabase(join(dir, ".gsd", "gsd.db"));
699
+ insertMilestone({ id: "M004", title: "Budget Tracking", status: "active" });
700
+ insertSlice({ milestoneId: "M004", id: "S01", title: "Schema migration", status: "complete", sequence: 1 });
701
+ insertSlice({ milestoneId: "M004", id: "S02", title: "Expense add", status: "pending", sequence: 2 });
702
+ insertTask({ milestoneId: "M004", sliceId: "S01", id: "T01", title: "Add repeat column via idempotent ALTER TABLE", status: "complete" });
703
+ insertTask({ milestoneId: "M004", sliceId: "S01", id: "T02", title: "Backfill repeat metadata", status: "pending" });
704
+
705
+ _resetWidgetModeForTests();
706
+ setWidgetMode("small", projectPrefsPath, globalPrefsPath);
707
+
708
+ updateProgressWidget(
709
+ {
710
+ hasUI: true,
711
+ ui: {
712
+ setHeader() {},
713
+ setStatus() {},
714
+ setWidget(_key: string, factory: any) {
715
+ if (_key === "gsd-progress") {
716
+ holder.widget = factory(
717
+ { requestRender() {} },
718
+ { fg: (_color: string, text: string) => text, bold: (text: string) => text },
719
+ );
720
+ }
721
+ },
722
+ },
723
+ } as any,
724
+ "execute-task",
725
+ "M004/S01/T02",
726
+ {
727
+ phase: "executing",
728
+ activeMilestone: { id: "M004", title: "Budget Tracking" },
729
+ activeSlice: { id: "S01", title: "Schema migration" },
730
+ activeTask: { id: "T02", title: "Backfill repeat metadata" },
731
+ } as any,
732
+ {
733
+ getAutoStartTime: () => Date.now() - 18_000,
734
+ isStepMode: () => false,
735
+ getCmdCtx: () => null,
736
+ getBasePath: () => dir,
737
+ isVerbose: () => false,
738
+ isSessionSwitching: () => false,
739
+ getCurrentDispatchedModelId: () => null,
740
+ },
741
+ );
742
+
743
+ assert.ok(holder.widget, "progress widget should be installed");
744
+ const widget = holder.widget;
745
+ const lines = widget.render(120);
746
+ const rendered = lines.join("\n");
747
+
748
+ assert.equal(lines.length, 4, `small widget should render as rule + two dense rows + rule:\n${rendered}`);
749
+ assert.match(rendered, /STATUS\s+.*AUTO\s+running/);
750
+ assert.match(rendered, /UNIT\s+M004\/S01\/T02/);
751
+ assert.match(rendered, /SPEND/);
752
+ assert.match(rendered, /TIME/);
753
+ assert.match(rendered, /PHASE\s+execute-task/);
754
+ assert.match(rendered, /WORK\s+T02: Backfill repeat m/);
755
+ assert.match(rendered, /TASK\s+2\/2/);
756
+ assert.match(rendered, /SLICE.*1\/2/);
757
+ assert.doesNotMatch(rendered, /\/gsd next|\/gsd status/);
758
+ assert.doesNotMatch(rendered, /dashboard|esc pause/);
759
+
760
+ for (const width of [40, 80, 120]) {
761
+ widget.invalidate();
762
+ assertLinesFit(widget.render(width), width);
763
+ }
764
+ });
765
+
665
766
  test("updateProgressWidget shows provider-waiting state consistently for auto and next modes", (t) => {
666
767
  const dir = makeTempDir("auto-next-dashboard");
667
768
  mkdirSync(join(dir, ".gsd"), { recursive: true });
@@ -720,14 +821,14 @@ test("updateProgressWidget shows provider-waiting state consistently for auto an
720
821
  const autoRendered = renderDashboard(false);
721
822
  const nextRendered = renderDashboard(true);
722
823
 
723
- assert.match(autoRendered, /GSD\s+·\s+AUTO\s+·\s+running/);
724
- assert.match(nextRendered, /GSD\s+·\s+NEXT\s+·\s+running/);
824
+ assert.match(autoRendered, /STATUS\s+.*AUTO\s+running/);
825
+ assert.match(nextRendered, /STATUS\s+.*NEXT\s+running/);
725
826
  assert.doesNotMatch(autoRendered.split("\n")[1] ?? "", /completing M003\/S01/);
726
827
  assert.doesNotMatch(nextRendered.split("\n")[1] ?? "", /completing M003\/S01/);
727
828
  assert.doesNotMatch(autoRendered, /waiting on provider.*Waiting on provider/i);
728
829
  assert.doesNotMatch(nextRendered, /waiting on provider.*Waiting on provider/i);
729
- assert.match(autoRendered, /completing\s+M003\/S01/);
730
- assert.match(nextRendered, /completing\s+M003\/S01/);
830
+ assert.match(autoRendered, /PHASE\s+complete-slice/);
831
+ assert.match(nextRendered, /PHASE\s+complete-slice/);
731
832
  assert.doesNotMatch(autoRendered, /Working/);
732
833
  assert.doesNotMatch(nextRendered, /Working/);
733
834
  });
@@ -574,6 +574,43 @@ test("completeActiveUnit allows a different next unit to advance", async () => {
574
574
  assert.deepEqual(second.unit, { unitType: "execute-task", unitId: "T02" });
575
575
  });
576
576
 
577
+ test("completeActiveUnit guard survives an intervening advance and blocks X→Y→X re-dispatch", async () => {
578
+ // Regression test for issue #415: lastFinalizedUnitKey was wiped on every advance(),
579
+ // allowing completed units to be re-dispatched after any interleaving unit (X→Y→X).
580
+ let nextTaskId = "T01";
581
+ const { deps } = makeDeps({
582
+ dispatch: {
583
+ async decideNextUnit() {
584
+ return { unitType: "execute-task", unitId: nextTaskId, reason: "ready", preconditions: [] };
585
+ },
586
+ },
587
+ });
588
+ const orchestrator = createAutoOrchestrator(deps);
589
+
590
+ // Step 1: advance X (T01)
591
+ const first = await orchestrator.advance();
592
+ assert.equal(first.kind, "advanced");
593
+ if (first.kind !== "advanced") throw new Error("expected first advance");
594
+
595
+ // Step 2: complete X (T01) — sets lastFinalizedUnitKey = 'execute-task:T01'
596
+ await orchestrator.completeActiveUnit(first.unit);
597
+
598
+ // Step 3: advance Y (T02) — must NOT clear lastFinalizedUnitKey
599
+ nextTaskId = "T02";
600
+ const second = await orchestrator.advance();
601
+ assert.equal(second.kind, "advanced");
602
+ if (second.kind !== "advanced") throw new Error("expected second advance (T02)");
603
+ assert.deepEqual(second.unit, { unitType: "execute-task", unitId: "T02" });
604
+
605
+ // Step 4: re-select X (T01) — must be blocked because T01 was finalized
606
+ nextTaskId = "T01";
607
+ const third = await orchestrator.advance();
608
+ assert.equal(third.kind, "blocked");
609
+ if (third.kind !== "blocked") throw new Error("expected X→Y→X re-dispatch to be blocked");
610
+ assert.equal(third.action, "stop");
611
+ assert.equal(third.reason, "state did not advance after finalized execute-task T01");
612
+ });
613
+
577
614
  test("retryActiveUnit clears in-flight idempotency without marking the unit finalized", async () => {
578
615
  const { deps, calls } = makeDeps();
579
616
  const orchestrator = createAutoOrchestrator(deps);
@@ -1558,6 +1558,14 @@ test("verifyExpectedArtifact complete-milestone passes when DB milestone is comp
1558
1558
 
1559
1559
  openDatabase(join(base, ".gsd", "gsd.db"));
1560
1560
  insertMilestone({ id: "M001", title: "Milestone One", status: "complete" });
1561
+ insertSlice({ id: "S01", milestoneId: "M001", title: "Done Slice", status: "complete" });
1562
+ insertAssessment({
1563
+ path: "milestones/M001/M001-VALIDATION.md",
1564
+ milestoneId: "M001",
1565
+ status: "pass",
1566
+ scope: "milestone-validation",
1567
+ fullContent: "verdict: pass",
1568
+ });
1561
1569
 
1562
1570
  const result = verifyExpectedArtifact("complete-milestone", "M001", base);
1563
1571
  assert.equal(result, true, "complete-milestone should pass when DB status is complete");
@@ -1566,7 +1574,7 @@ test("verifyExpectedArtifact complete-milestone passes when DB milestone is comp
1566
1574
  }
1567
1575
  });
1568
1576
 
1569
- test("verifyExpectedArtifact complete-milestone tolerates transient DB lag when SUMMARY is canonical success (#4658)", () => {
1577
+ test("verifyExpectedArtifact complete-milestone rejects success SUMMARY when DB milestone is still open (#4658)", () => {
1570
1578
  const base = makeGitBase();
1571
1579
  try {
1572
1580
  execFileSync("git", ["checkout", "-b", "feat/ms-db-lag-success"], { cwd: base, stdio: "ignore" });
@@ -1591,7 +1599,7 @@ test("verifyExpectedArtifact complete-milestone tolerates transient DB lag when
1591
1599
  insertMilestone({ id: "M001", title: "Milestone One", status: "active" });
1592
1600
 
1593
1601
  const result = verifyExpectedArtifact("complete-milestone", "M001", base);
1594
- assert.equal(result, true, "canonical success SUMMARY should pass verification during transient DB lag");
1602
+ assert.equal(result, false, "success SUMMARY must not overrule an open DB milestone");
1595
1603
  } finally {
1596
1604
  cleanup(base);
1597
1605
  }
@@ -30,6 +30,7 @@ test("checkAutoStartAfterDiscuss waits until discussion artifacts exist before r
30
30
  setPendingAutoStart(base, {
31
31
  basePath: base,
32
32
  milestoneId: "M001",
33
+ startAuto: false,
33
34
  ctx: { ui: { notify: (message: string) => notifications.push(message) } } as any,
34
35
  pi: { sendMessage: () => {} } as any,
35
36
  });
@@ -41,5 +42,7 @@ test("checkAutoStartAfterDiscuss waits until discussion artifacts exist before r
41
42
  writeFileSync(join(base, ".gsd", "STATE.md"), "# State\n", "utf-8");
42
43
 
43
44
  assert.equal(checkAutoStartAfterDiscuss(), true);
44
- assert.deepEqual(notifications, ["Milestone M001 ready."]);
45
+ assert.deepEqual(notifications, [
46
+ "Milestone M001 context captured. Continuing the planning pipeline.",
47
+ ]);
45
48
  });
@@ -92,13 +92,23 @@ test("checkAutoStartAfterDiscuss completes when discussion manifest is absent",
92
92
  setPendingAutoStart(base, {
93
93
  basePath: base,
94
94
  milestoneId: "M001",
95
- ctx: { ui: { notify: (message: string, level: string) => notifications.push({ message, level }) } } as any,
95
+ startAuto: false,
96
+ ctx: {
97
+ ui: {
98
+ notify: (message: string, level: string) => notifications.push({ message, level }),
99
+ },
100
+ } as any,
96
101
  pi: { sendMessage: () => { scheduled = true; } } as any,
97
102
  });
98
103
 
99
104
  assert.equal(checkAutoStartAfterDiscuss(), true);
100
105
  assert.equal(scheduled, false);
101
- assert.deepEqual(notifications, [{ message: "Milestone M001 ready.", level: "success" }]);
106
+ assert.deepEqual(notifications, [
107
+ {
108
+ message: "Milestone M001 context captured. Continuing the planning pipeline.",
109
+ level: "success",
110
+ },
111
+ ]);
102
112
  } finally {
103
113
  clearPendingAutoStart(base);
104
114
  rmSync(base, { recursive: true, force: true });
@@ -0,0 +1,142 @@
1
+ // Project/App: gsd-pi
2
+ // File Purpose: Unit tests for hasBrowserRequiredText heading-depth section guard.
3
+
4
+ import { describe, test } from 'node:test';
5
+ import assert from 'node:assert/strict';
6
+
7
+ import { hasBrowserRequiredText } from '../browser-evidence.ts';
8
+
9
+ describe('hasBrowserRequiredText', () => {
10
+ test('detects browser requirement in a plain test-cases section', () => {
11
+ const text = [
12
+ '## Test Cases',
13
+ '',
14
+ '1. Open index.html in a browser and navigate to /dashboard.',
15
+ '',
16
+ ].join('\n');
17
+ assert.ok(hasBrowserRequiredText(text), 'plain browser step should be detected');
18
+ });
19
+
20
+ test('ignores browser mention under a top-level non-requirement heading', () => {
21
+ const text = [
22
+ '## Not Proven',
23
+ '',
24
+ '- Keyboard usability through a real browser.',
25
+ '- Browser console cleanliness.',
26
+ '',
27
+ ].join('\n');
28
+ assert.ok(!hasBrowserRequiredText(text), 'browser mention under "Not Proven" should be ignored');
29
+ });
30
+
31
+ test('sub-heading inside a non-requirement section does not re-enable detection', () => {
32
+ // BUG (pre-fix): ### sub-heading under ## Not Proven resets inNonRequirementSection
33
+ // to false, causing subsequent lines to be detected as browser requirements.
34
+ const text = [
35
+ '## Not Proven By This UAT',
36
+ '',
37
+ '- No live browser session was used.',
38
+ '',
39
+ '### Visual Checks',
40
+ '',
41
+ '- Browser visual polish deferred to next slice.',
42
+ '- Keyboard interaction in a real browser is not proven here.',
43
+ '',
44
+ ].join('\n');
45
+ assert.ok(
46
+ !hasBrowserRequiredText(text),
47
+ 'sub-heading under a non-requirement section must not re-enable browser detection',
48
+ );
49
+ });
50
+
51
+ test('requirement-level heading after non-requirement section re-enables detection', () => {
52
+ const text = [
53
+ '## Not Proven',
54
+ '',
55
+ '- Browser polish deferred.',
56
+ '',
57
+ '## Test Cases',
58
+ '',
59
+ '1. Launch browser and open localhost.',
60
+ '',
61
+ ].join('\n');
62
+ assert.ok(
63
+ hasBrowserRequiredText(text),
64
+ 'browser step under "Test Cases" (same depth as "Not Proven") must still be detected',
65
+ );
66
+ });
67
+
68
+ test('deferred sub-heading inside a requirement section scopes exclusion to its own block', () => {
69
+ const text = [
70
+ '## Test Cases',
71
+ '',
72
+ '1. Open browser at localhost.',
73
+ '',
74
+ '### Deferred: keyboard check',
75
+ '',
76
+ '- Keyboard UAT deferred to next slice.',
77
+ '',
78
+ '### Step 2: Verify DOM',
79
+ '',
80
+ '1. Navigate to /dashboard in the browser.',
81
+ '',
82
+ ].join('\n');
83
+ assert.ok(
84
+ hasBrowserRequiredText(text),
85
+ 'browser step under "Step 2" sub-heading must be detected after a sibling "Deferred" sub-heading',
86
+ );
87
+ });
88
+
89
+ test('deferred sub-heading at same depth as test cases does not escape to parent', () => {
90
+ const text = [
91
+ '## Test Cases',
92
+ '',
93
+ '### Deferred: responsive layout',
94
+ '',
95
+ '- Responsive layout check is deferred to S02.',
96
+ '',
97
+ ].join('\n');
98
+ assert.ok(
99
+ !hasBrowserRequiredText(text),
100
+ 'content under a "Deferred" sub-heading should be excluded from detection',
101
+ );
102
+ });
103
+
104
+ test('detects browser requirement written only in a heading', () => {
105
+ // Regression: the line-by-line scan previously skip-continued past headings,
106
+ // missing browser obligations expressed only in heading text.
107
+ const text = '## Open browser session at localhost\n';
108
+ assert.ok(hasBrowserRequiredText(text), 'browser requirement in heading text must be detected');
109
+ });
110
+
111
+ test('heading that opens a non-requirement section is not itself detected as a requirement', () => {
112
+ const text = '## Not Proven\n\n- Some note.\n';
113
+ assert.ok(
114
+ !hasBrowserRequiredText(text),
115
+ 'a non-requirement section heading should not trigger browser detection',
116
+ );
117
+ });
118
+
119
+ test('returns false for empty text', () => {
120
+ assert.ok(!hasBrowserRequiredText(''), 'empty string returns false');
121
+ });
122
+
123
+ test('notes-for-tester heading with sub-headings stays non-requirement', () => {
124
+ const text = [
125
+ '## Notes for Tester',
126
+ '',
127
+ '### Browser Setup',
128
+ '',
129
+ '- Run this spec without a browser; a DOM harness is sufficient.',
130
+ '- Browser-based visual checks are deferred.',
131
+ '',
132
+ '### Follow-up Items',
133
+ '',
134
+ '- Track browser session evidence in S02.',
135
+ '',
136
+ ].join('\n');
137
+ assert.ok(
138
+ !hasBrowserRequiredText(text),
139
+ 'sub-headings under "Notes for Tester" should not re-enable browser detection',
140
+ );
141
+ });
142
+ });
@@ -164,17 +164,13 @@ describe("checkAutoStartAfterDiscuss Gate 1a (pending depth-verification gate)",
164
164
  test("Gate 1a does NOT trip when the pending gate is for a DIFFERENT milestone", () => {
165
165
  base = mkBase();
166
166
  openDatabase(":memory:");
167
- // status: "queued" so that Gate 1b downstream of Gate 1a fires its
168
- // recovery notify ("context file exists but milestone is still queued") —
169
- // observing that notify proves we advanced past Gate 1a. If Gate 1a
170
- // wrongly tripped on the M999 gate it would `return false` immediately
171
- // and Gate 1b would never run, so the notify would be absent.
172
167
  insertMilestone({ id: "M001", title: "Pending Gate Test", status: "queued" });
173
168
 
174
169
  cap = mkCapture();
175
170
  setPendingAutoStart(base, {
176
171
  basePath: base,
177
172
  milestoneId: "M001",
173
+ startAuto: false,
178
174
  ctx: mkCtx(cap),
179
175
  pi: mkPi(cap),
180
176
  });
@@ -182,21 +178,19 @@ describe("checkAutoStartAfterDiscuss Gate 1a (pending depth-verification gate)",
182
178
  setPendingGate("depth_verification_M999_confirm", base);
183
179
 
184
180
  const result = checkAutoStartAfterDiscuss();
185
- assert.equal(result, false, "Gate 1b returns false (expected) but only if Gate 1a let us through");
181
+ assert.equal(result, true, "different milestone gate must not block this handoff");
186
182
 
187
- // Positive proof we passed Gate 1a: Gate 1b emitted its recovery notify
188
- // about M001 (not M999 the pending-gate milestone is irrelevant here).
189
- const gate1bNotify = cap.notifies.find(n =>
190
- n.level === "warning" && /M001.*context file exists but milestone is still queued/i.test(n.msg)
183
+ const successNotify = cap.notifies.find(n =>
184
+ n.level === "success" && /M001 context captured/i.test(n.msg)
191
185
  );
192
186
  assert.ok(
193
- gate1bNotify,
194
- `expected Gate 1b warning notify about M001; got: ${JSON.stringify(cap.notifies)}`,
187
+ successNotify,
188
+ `expected context-captured success notify about M001; got: ${JSON.stringify(cap.notifies)}`,
195
189
  );
196
190
 
197
- // Negative proof: no Gate 1a notification path exists in source today, but
198
- // also assert no notify mentions M999 (the pending-gate milestone) that
199
- // would suggest Gate 1a is leaking the wrong milestone into messaging.
191
+ const retryNotify = cap.notifies.find(n => /queued|gsd_plan_milestone/i.test(n.msg));
192
+ assert.equal(retryNotify, undefined, "handoff must not mention queued state or internal plan retry");
193
+
200
194
  const m999Notify = cap.notifies.find(n => /M999/i.test(n.msg));
201
195
  assert.equal(m999Notify, undefined, "no notify should reference M999 (the pending-gate milestone)");
202
196
  });
@@ -1,9 +1,7 @@
1
- // gsd-pi + Regression tests for checkAutoStartAfterDiscuss "ready" notify guard (R3b)
1
+ // gsd-pi + Regression tests for checkAutoStartAfterDiscuss handoff copy (R3b)
2
2
  //
3
- // Belt-and-suspenders: even when CONTEXT.md and STATE.md exist on disk, the
4
- // "Milestone X ready." success notify must not fire when the milestone DB row
5
- // is absent. Otherwise the user sees "ready" and then /gsd reports
6
- // "No Active Milestone" because the milestone was never registered.
3
+ // Missing-row repair may accept a context handoff, but "Milestone X ready."
4
+ // is reserved for executable plans with persisted slices in DB mode.
7
5
 
8
6
  import { describe, test, beforeEach, afterEach } from "node:test";
9
7
  import assert from "node:assert/strict";
@@ -21,6 +19,7 @@ import {
21
19
  openDatabase,
22
20
  closeDatabase,
23
21
  insertMilestone,
22
+ insertSlice,
24
23
  getMilestone,
25
24
  } from "../gsd-db.ts";
26
25
  import {
@@ -92,49 +91,60 @@ describe("checkAutoStartAfterDiscuss ready-notify DB guard (R3b)", () => {
92
91
  }
93
92
  });
94
93
 
95
- test("does not announce 'ready' when the milestone DB row is absent recovers via Gate 1b", () => {
94
+ test("repairs a missing milestone DB row and accepts context-captured handoff", () => {
96
95
  base = mkBase();
97
- // Open a fresh in-memory DB but DO NOT insertMilestone for M001.
98
96
  openDatabase(":memory:");
99
97
 
100
98
  cap = mkCapture();
101
99
  setPendingAutoStart(base, {
102
100
  basePath: base,
103
101
  milestoneId: "M001",
102
+ startAuto: false,
104
103
  ctx: mkCtx(cap),
105
104
  pi: mkPi(cap),
106
105
  });
107
106
 
108
107
  const result = checkAutoStartAfterDiscuss();
109
- assert.equal(result, false, "must return false when DB row missing");
108
+ assert.equal(result, true, "missing row with pinned context should repair and accept handoff");
110
109
 
111
- // No success "ready" notify
112
110
  const successReady = cap.notifies.find(
113
111
  (n) => n.level === "success" && /ready\.?$/i.test(n.msg),
114
112
  );
115
113
  assert.equal(successReady, undefined, "must not announce 'ready' when DB row missing");
116
114
 
117
- // When CONTEXT.md is on disk the R3b path recovers: it inserts a placeholder
118
- // "queued" row (so Gate 1b can retry gsd_plan_milestone) and emits a warning.
119
115
  const recovered = getMilestone("M001");
120
116
  assert.ok(recovered, "R3b recovery must insert a placeholder 'queued' DB row");
121
117
  assert.equal(recovered!.status, "queued", "placeholder row must have status 'queued'");
122
118
 
123
- const warnNotify = cap.notifies.find((n) => n.level === "warning");
124
- assert.ok(warnNotify, "must emit a warning notify during R3b recovery");
125
- assert.match(warnNotify!.msg, /M001/, "warning must mention the milestone id");
126
- assert.match(warnNotify!.msg, /recovering/i, "warning must mention recovery");
119
+ assert.equal(
120
+ cap.notifies.some(n => n.level === "warning"),
121
+ false,
122
+ "successful missing-row repair must not warn the user",
123
+ );
124
+ assert.deepEqual(cap.notifies, [
125
+ {
126
+ msg: "Milestone M001 context captured. Continuing the planning pipeline.",
127
+ level: "success",
128
+ },
129
+ ]);
127
130
  });
128
131
 
129
- test("announces 'ready' when DB row exists", () => {
132
+ test("announces 'ready' when DB row has executable slices", () => {
130
133
  base = mkBase();
131
134
  openDatabase(":memory:");
132
135
  insertMilestone({ id: "M001", title: "Ready Guard Test", status: "active" });
136
+ insertSlice({
137
+ id: "S01",
138
+ milestoneId: "M001",
139
+ title: "Executable Slice",
140
+ status: "pending",
141
+ });
133
142
 
134
143
  cap = mkCapture();
135
144
  setPendingAutoStart(base, {
136
145
  basePath: base,
137
146
  milestoneId: "M001",
147
+ startAuto: false,
138
148
  ctx: mkCtx(cap),
139
149
  pi: mkPi(cap),
140
150
  });
@@ -9,6 +9,7 @@ import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
9
9
  import { handleGSDCommand } from "../commands/dispatcher.ts";
10
10
  import {
11
11
  closeDatabase,
12
+ insertAssessment,
12
13
  insertMilestone,
13
14
  insertSlice,
14
15
  openDatabase,
@@ -106,6 +107,13 @@ function seedRegisteredCompletedWorktreeWithoutRoadmap(base: string): void {
106
107
  title: "Live Text Search",
107
108
  status: "complete",
108
109
  });
110
+ insertAssessment({
111
+ path: "milestones/M008/M008-VALIDATION.md",
112
+ milestoneId: "M008",
113
+ status: "pass",
114
+ scope: "milestone-validation",
115
+ fullContent: "verdict: pass",
116
+ });
109
117
  writeFileSync(
110
118
  join(base, ".gsd", "PREFERENCES.md"),
111
119
  "---\ngit:\n isolation: worktree\n---\n",
@@ -124,6 +132,19 @@ function seedRegisteredCompletedWorktree(base: string): void {
124
132
  mkdirSync(join(base, ".gsd"), { recursive: true });
125
133
  openDatabase(join(base, ".gsd", "gsd.db"));
126
134
  insertMilestone({ id: "M008", title: "Live Text Search", status: "complete" });
135
+ insertSlice({
136
+ id: "S01",
137
+ milestoneId: "M008",
138
+ title: "Live Text Search",
139
+ status: "complete",
140
+ });
141
+ insertAssessment({
142
+ path: "milestones/M008/M008-VALIDATION.md",
143
+ milestoneId: "M008",
144
+ status: "pass",
145
+ scope: "milestone-validation",
146
+ fullContent: "verdict: pass",
147
+ });
127
148
  writeWorktreePreferencesAndRoadmap(base);
128
149
 
129
150
  const worktreePath = join(base, ".gsd", "worktrees", "M008");