@opengsd/gsd-pi 1.2.0-dev.844675c9 → 1.2.0-dev.b1abb545

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 (375) hide show
  1. package/dist/cli-web-branch.d.ts +2 -0
  2. package/dist/cli-web-branch.js +9 -2
  3. package/dist/help-text.js +5 -0
  4. package/dist/resources/.managed-resources-content-hash +1 -1
  5. package/dist/resources/extensions/ask-user-questions.js +78 -23
  6. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +84 -228
  7. package/dist/resources/extensions/claude-code-cli/turn-assembler.js +224 -0
  8. package/dist/resources/extensions/github-sync/templates.js +3 -3
  9. package/dist/resources/extensions/gsd/artifact-projection.js +14 -0
  10. package/dist/resources/extensions/gsd/auto/loop.js +74 -56
  11. package/dist/resources/extensions/gsd/auto/orchestrator.js +109 -11
  12. package/dist/resources/extensions/gsd/auto/phases.js +28 -3
  13. package/dist/resources/extensions/gsd/auto/run-unit.js +2 -1
  14. package/dist/resources/extensions/gsd/auto/session.js +3 -0
  15. package/dist/resources/extensions/gsd/auto-dashboard.js +16 -4
  16. package/dist/resources/extensions/gsd/auto-dispatch.js +6 -5
  17. package/dist/resources/extensions/gsd/auto-model-selection.js +8 -0
  18. package/dist/resources/extensions/gsd/auto-post-unit.js +4 -3
  19. package/dist/resources/extensions/gsd/auto-prompts.js +81 -8
  20. package/dist/resources/extensions/gsd/auto-recovery.js +48 -49
  21. package/dist/resources/extensions/gsd/auto-runtime-state.js +14 -0
  22. package/dist/resources/extensions/gsd/auto-start.js +12 -23
  23. package/dist/resources/extensions/gsd/auto-timers.js +16 -2
  24. package/dist/resources/extensions/gsd/auto-tool-tracking.js +32 -0
  25. package/dist/resources/extensions/gsd/auto-unit-tool-scope.js +4 -29
  26. package/dist/resources/extensions/gsd/auto-verification.js +7 -7
  27. package/dist/resources/extensions/gsd/auto-worktree.js +21 -19
  28. package/dist/resources/extensions/gsd/auto.js +11 -7
  29. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +28 -37
  30. package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +11 -37
  31. package/dist/resources/extensions/gsd/bootstrap/query-tools.js +2 -2
  32. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +100 -138
  33. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +63 -4
  34. package/dist/resources/extensions/gsd/closeout-consistency-gate.js +21 -4
  35. package/dist/resources/extensions/gsd/codebase-generator.js +8 -4
  36. package/dist/resources/extensions/gsd/commands/handlers/auto.js +3 -0
  37. package/dist/resources/extensions/gsd/commands-handlers.js +20 -0
  38. package/dist/resources/extensions/gsd/commands-inspect.js +4 -8
  39. package/dist/resources/extensions/gsd/commands-maintenance.js +61 -41
  40. package/dist/resources/extensions/gsd/commands-ship.js +2 -2
  41. package/dist/resources/extensions/gsd/commands-verdict.js +12 -2
  42. package/dist/resources/extensions/gsd/db-workspace.js +103 -0
  43. package/dist/resources/extensions/gsd/delegation-policy.js +2 -10
  44. package/dist/resources/extensions/gsd/discussion-handoff.js +218 -0
  45. package/dist/resources/extensions/gsd/docs/preferences-reference.md +9 -0
  46. package/dist/resources/extensions/gsd/doctor.js +16 -9
  47. package/dist/resources/extensions/gsd/error-classifier.js +1 -1
  48. package/dist/resources/extensions/gsd/git-conflict-state.js +16 -1
  49. package/dist/resources/extensions/gsd/gsd-db.js +12 -0
  50. package/dist/resources/extensions/gsd/guided-flow.js +34 -468
  51. package/dist/resources/extensions/gsd/guided-unit-completion.js +225 -0
  52. package/dist/resources/extensions/gsd/markdown-renderer.js +2 -1
  53. package/dist/resources/extensions/gsd/mcp-filter.js +2 -1
  54. package/dist/resources/extensions/gsd/mcp-tool-name.js +26 -0
  55. package/dist/resources/extensions/gsd/md-importer.js +4 -3
  56. package/dist/resources/extensions/gsd/migrate/safety.js +2 -2
  57. package/dist/resources/extensions/gsd/migration-auto-check.js +3 -2
  58. package/dist/resources/extensions/gsd/milestone-closeout-proof.js +72 -0
  59. package/dist/resources/extensions/gsd/milestone-closeout.js +12 -4
  60. package/dist/resources/extensions/gsd/milestone-merge-transaction.js +10 -0
  61. package/dist/resources/extensions/gsd/milestone-planning-persistence.js +156 -0
  62. package/dist/resources/extensions/gsd/milestone-readiness.js +77 -0
  63. package/dist/resources/extensions/gsd/milestone-settlement.js +50 -0
  64. package/dist/resources/extensions/gsd/milestone-validation-evidence.js +73 -0
  65. package/dist/resources/extensions/gsd/milestone-validation-verdict.js +57 -0
  66. package/dist/resources/extensions/gsd/parallel-eligibility.js +3 -6
  67. package/dist/resources/extensions/gsd/parallel-orchestrator.js +3 -2
  68. package/dist/resources/extensions/gsd/preferences-diagnostics.js +67 -0
  69. package/dist/resources/extensions/gsd/preferences.js +147 -29
  70. package/dist/resources/extensions/gsd/prompts/complete-slice.md +1 -0
  71. package/dist/resources/extensions/gsd/prompts/execute-task.md +2 -0
  72. package/dist/resources/extensions/gsd/prompts/guided-discuss-project.md +3 -1
  73. package/dist/resources/extensions/gsd/prompts/plan-milestone.md +2 -0
  74. package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -0
  75. package/dist/resources/extensions/gsd/prompts/refine-slice.md +1 -0
  76. package/dist/resources/extensions/gsd/prompts/system.md +1 -1
  77. package/dist/resources/extensions/gsd/provider-payload-policy.js +83 -0
  78. package/dist/resources/extensions/gsd/pull-request-process.js +13 -0
  79. package/dist/resources/extensions/gsd/quality-gate-closure.js +109 -0
  80. package/dist/resources/extensions/gsd/question-transport.js +86 -0
  81. package/dist/resources/extensions/gsd/roadmap-slices.js +8 -2
  82. package/dist/resources/extensions/gsd/slice-parallel-orchestrator.js +3 -2
  83. package/dist/resources/extensions/gsd/state.js +13 -5
  84. package/dist/resources/extensions/gsd/templates/plan.md +7 -0
  85. package/dist/resources/extensions/gsd/templates/project.md +1 -0
  86. package/dist/resources/extensions/gsd/templates/roadmap.md +1 -1
  87. package/dist/resources/extensions/gsd/templates/uat.md +5 -1
  88. package/dist/resources/extensions/gsd/tool-contract.js +52 -8
  89. package/dist/resources/extensions/gsd/tool-presentation-plan.js +15 -34
  90. package/dist/resources/extensions/gsd/tool-surface-snapshot.js +17 -0
  91. package/dist/resources/extensions/gsd/tools/plan-milestone.js +15 -143
  92. package/dist/resources/extensions/gsd/tools/reassess-roadmap.js +39 -0
  93. package/dist/resources/extensions/gsd/tools/validate-milestone.js +15 -78
  94. package/dist/resources/extensions/gsd/uat-policy.js +16 -10
  95. package/dist/resources/extensions/gsd/uat-run.js +9 -14
  96. package/dist/resources/extensions/gsd/unit-context-composer.js +40 -20
  97. package/dist/resources/extensions/gsd/unit-runtime.js +3 -2
  98. package/dist/resources/extensions/gsd/unit-tool-contracts.js +2 -1
  99. package/dist/resources/extensions/gsd/user-input-boundary.js +23 -0
  100. package/dist/resources/extensions/gsd/validation-block-guard.js +2 -0
  101. package/dist/resources/extensions/gsd/web-app-uat.js +80 -0
  102. package/dist/resources/extensions/gsd/workflow-mcp.js +15 -102
  103. package/dist/resources/extensions/gsd/workflow-reconcile.js +4 -3
  104. package/dist/resources/extensions/gsd/workflow-tool-surface.js +46 -0
  105. package/dist/resources/extensions/gsd/workspace-git-guard.js +2 -0
  106. package/dist/resources/extensions/gsd/worktree-state-projection.js +33 -4
  107. package/dist/resources/extensions/gsd/worktree-telemetry.js +12 -0
  108. package/dist/resources/extensions/shared/interview-ui.js +2 -2
  109. package/dist/resources/shared/claude-runtime-floor.js +182 -0
  110. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  111. package/dist/update-cmd.js +20 -0
  112. package/dist/web/standalone/.next/BUILD_ID +1 -1
  113. package/dist/web/standalone/.next/app-path-routes-manifest.json +7 -7
  114. package/dist/web/standalone/.next/build-manifest.json +3 -3
  115. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  116. package/dist/web/standalone/.next/react-loadable-manifest.json +8 -8
  117. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  118. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  119. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  120. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  121. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  122. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  123. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  124. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  125. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  126. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  127. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  128. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  129. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  130. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  131. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  132. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  133. package/dist/web/standalone/.next/server/app/index.html +1 -1
  134. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  135. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  136. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  137. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  138. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  139. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  140. package/dist/web/standalone/.next/server/app-paths-manifest.json +7 -7
  141. package/dist/web/standalone/.next/server/chunks/8357.js +1 -1
  142. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  143. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  144. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  145. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  146. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  147. package/dist/web/standalone/.next/static/chunks/2659.b7b129ee6a769448.js +1 -0
  148. package/dist/web/standalone/.next/static/chunks/2772.bfa657f49f955239.js +1 -0
  149. package/dist/web/standalone/.next/static/chunks/{3616.4113d484a994e411.js → 3616.3c60753b8ffcbd2e.js} +1 -1
  150. package/dist/web/standalone/.next/static/chunks/4283.e4873b058df143a1.js +2 -0
  151. package/dist/web/standalone/.next/static/chunks/5826.a46ecdd1cfe8dabc.js +1 -0
  152. package/dist/web/standalone/.next/static/chunks/796.cf859a427a2cb2ac.js +10 -0
  153. package/dist/web/standalone/.next/static/chunks/8785.2e5a118797fb2dd2.js +1 -0
  154. package/dist/web/standalone/.next/static/chunks/{webpack-dda80a1ef5587410.js → webpack-fbea77b5f9953368.js} +1 -1
  155. package/dist/web/standalone/node_modules/node-pty/build/Makefile +1 -1
  156. package/dist/web-mode.d.ts +2 -0
  157. package/dist/web-mode.js +20 -8
  158. package/package.json +2 -1
  159. package/packages/cloud-mcp-gateway/package.json +2 -2
  160. package/packages/contracts/package.json +1 -1
  161. package/packages/daemon/package.json +4 -4
  162. package/packages/gsd-agent-core/dist/session/agent-session-extensions.d.ts +2 -0
  163. package/packages/gsd-agent-core/dist/session/agent-session-extensions.d.ts.map +1 -1
  164. package/packages/gsd-agent-core/dist/session/agent-session-extensions.js +14 -0
  165. package/packages/gsd-agent-core/dist/session/agent-session-extensions.js.map +1 -1
  166. package/packages/gsd-agent-core/package.json +5 -5
  167. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  168. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js +106 -40
  169. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  170. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-extension-widgets.d.ts.map +1 -1
  171. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-extension-widgets.js +6 -0
  172. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-extension-widgets.js.map +1 -1
  173. package/packages/gsd-agent-modes/package.json +7 -7
  174. package/packages/mcp-server/dist/server.d.ts +10 -0
  175. package/packages/mcp-server/dist/server.d.ts.map +1 -1
  176. package/packages/mcp-server/dist/server.js +8 -0
  177. package/packages/mcp-server/dist/server.js.map +1 -1
  178. package/packages/mcp-server/dist/workflow-tools.d.ts +41 -0
  179. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  180. package/packages/mcp-server/dist/workflow-tools.js +2 -1
  181. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  182. package/packages/mcp-server/package.json +3 -3
  183. package/packages/native/package.json +1 -1
  184. package/packages/pi-agent-core/package.json +1 -1
  185. package/packages/pi-ai/dist/models.generated.d.ts +8 -93
  186. package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
  187. package/packages/pi-ai/dist/models.generated.js +35 -120
  188. package/packages/pi-ai/dist/models.generated.js.map +1 -1
  189. package/packages/pi-ai/package.json +1 -1
  190. package/packages/pi-coding-agent/package.json +7 -7
  191. package/packages/pi-tui/dist/components/input.js +1 -1
  192. package/packages/pi-tui/dist/components/input.js.map +1 -1
  193. package/packages/pi-tui/dist/keys.d.ts.map +1 -1
  194. package/packages/pi-tui/dist/keys.js +39 -30
  195. package/packages/pi-tui/dist/keys.js.map +1 -1
  196. package/packages/pi-tui/dist/stdin-buffer.d.ts.map +1 -1
  197. package/packages/pi-tui/dist/stdin-buffer.js +22 -0
  198. package/packages/pi-tui/dist/stdin-buffer.js.map +1 -1
  199. package/packages/pi-tui/package.json +2 -2
  200. package/packages/rpc-client/package.json +2 -2
  201. package/pkg/package.json +1 -1
  202. package/src/resources/extensions/ask-user-questions.ts +87 -24
  203. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +108 -281
  204. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +240 -0
  205. package/src/resources/extensions/claude-code-cli/turn-assembler.ts +287 -0
  206. package/src/resources/extensions/github-sync/templates.ts +3 -3
  207. package/src/resources/extensions/github-sync/tests/templates.test.ts +2 -2
  208. package/src/resources/extensions/gsd/artifact-projection.ts +31 -0
  209. package/src/resources/extensions/gsd/auto/contracts.ts +32 -2
  210. package/src/resources/extensions/gsd/auto/loop-deps.ts +2 -0
  211. package/src/resources/extensions/gsd/auto/loop.ts +83 -61
  212. package/src/resources/extensions/gsd/auto/orchestrator.ts +125 -12
  213. package/src/resources/extensions/gsd/auto/phases.ts +35 -3
  214. package/src/resources/extensions/gsd/auto/run-unit.ts +2 -1
  215. package/src/resources/extensions/gsd/auto/session.ts +4 -0
  216. package/src/resources/extensions/gsd/auto-dashboard.ts +18 -4
  217. package/src/resources/extensions/gsd/auto-dispatch.ts +20 -7
  218. package/src/resources/extensions/gsd/auto-model-selection.ts +8 -0
  219. package/src/resources/extensions/gsd/auto-post-unit.ts +4 -3
  220. package/src/resources/extensions/gsd/auto-prompts.ts +107 -9
  221. package/src/resources/extensions/gsd/auto-recovery.ts +50 -50
  222. package/src/resources/extensions/gsd/auto-runtime-state.ts +26 -0
  223. package/src/resources/extensions/gsd/auto-start.ts +17 -20
  224. package/src/resources/extensions/gsd/auto-timers.ts +16 -2
  225. package/src/resources/extensions/gsd/auto-tool-tracking.ts +35 -0
  226. package/src/resources/extensions/gsd/auto-unit-tool-scope.ts +9 -30
  227. package/src/resources/extensions/gsd/auto-verification.ts +7 -8
  228. package/src/resources/extensions/gsd/auto-worktree.ts +33 -26
  229. package/src/resources/extensions/gsd/auto.ts +15 -8
  230. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +29 -37
  231. package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +10 -37
  232. package/src/resources/extensions/gsd/bootstrap/query-tools.ts +2 -2
  233. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +116 -151
  234. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +107 -3
  235. package/src/resources/extensions/gsd/closeout-consistency-gate.ts +27 -5
  236. package/src/resources/extensions/gsd/codebase-generator.ts +9 -5
  237. package/src/resources/extensions/gsd/commands/handlers/auto.ts +3 -0
  238. package/src/resources/extensions/gsd/commands-handlers.ts +18 -0
  239. package/src/resources/extensions/gsd/commands-inspect.ts +7 -8
  240. package/src/resources/extensions/gsd/commands-maintenance.ts +74 -40
  241. package/src/resources/extensions/gsd/commands-ship.ts +2 -2
  242. package/src/resources/extensions/gsd/commands-verdict.ts +19 -2
  243. package/src/resources/extensions/gsd/db-workspace.ts +170 -0
  244. package/src/resources/extensions/gsd/delegation-policy.ts +3 -11
  245. package/src/resources/extensions/gsd/discussion-handoff.ts +276 -0
  246. package/src/resources/extensions/gsd/docs/preferences-reference.md +9 -0
  247. package/src/resources/extensions/gsd/doctor.ts +15 -5
  248. package/src/resources/extensions/gsd/error-classifier.ts +1 -1
  249. package/src/resources/extensions/gsd/git-conflict-state.ts +17 -1
  250. package/src/resources/extensions/gsd/gsd-db.ts +12 -0
  251. package/src/resources/extensions/gsd/guided-flow.ts +47 -558
  252. package/src/resources/extensions/gsd/guided-unit-completion.ts +275 -0
  253. package/src/resources/extensions/gsd/markdown-renderer.ts +2 -1
  254. package/src/resources/extensions/gsd/mcp-filter.ts +2 -1
  255. package/src/resources/extensions/gsd/mcp-tool-name.ts +35 -0
  256. package/src/resources/extensions/gsd/md-importer.ts +3 -3
  257. package/src/resources/extensions/gsd/migrate/safety.ts +2 -2
  258. package/src/resources/extensions/gsd/migration-auto-check.ts +2 -2
  259. package/src/resources/extensions/gsd/milestone-closeout-proof.ts +131 -0
  260. package/src/resources/extensions/gsd/milestone-closeout.ts +12 -4
  261. package/src/resources/extensions/gsd/milestone-merge-transaction.ts +47 -0
  262. package/src/resources/extensions/gsd/milestone-planning-persistence.ts +224 -0
  263. package/src/resources/extensions/gsd/milestone-readiness.ts +125 -0
  264. package/src/resources/extensions/gsd/milestone-settlement.ts +81 -0
  265. package/src/resources/extensions/gsd/milestone-validation-evidence.ts +95 -0
  266. package/src/resources/extensions/gsd/milestone-validation-verdict.ts +80 -0
  267. package/src/resources/extensions/gsd/parallel-eligibility.ts +4 -5
  268. package/src/resources/extensions/gsd/parallel-orchestrator.ts +6 -2
  269. package/src/resources/extensions/gsd/preferences-diagnostics.ts +98 -0
  270. package/src/resources/extensions/gsd/preferences-types.ts +16 -0
  271. package/src/resources/extensions/gsd/preferences.ts +173 -28
  272. package/src/resources/extensions/gsd/prompts/complete-slice.md +1 -0
  273. package/src/resources/extensions/gsd/prompts/execute-task.md +2 -0
  274. package/src/resources/extensions/gsd/prompts/guided-discuss-project.md +3 -1
  275. package/src/resources/extensions/gsd/prompts/plan-milestone.md +2 -0
  276. package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -0
  277. package/src/resources/extensions/gsd/prompts/refine-slice.md +1 -0
  278. package/src/resources/extensions/gsd/prompts/system.md +1 -1
  279. package/src/resources/extensions/gsd/provider-payload-policy.ts +140 -0
  280. package/src/resources/extensions/gsd/pull-request-process.ts +41 -0
  281. package/src/resources/extensions/gsd/quality-gate-closure.ts +140 -0
  282. package/src/resources/extensions/gsd/question-transport.ts +138 -0
  283. package/src/resources/extensions/gsd/roadmap-slices.ts +8 -2
  284. package/src/resources/extensions/gsd/slice-parallel-orchestrator.ts +6 -2
  285. package/src/resources/extensions/gsd/state.ts +15 -5
  286. package/src/resources/extensions/gsd/templates/plan.md +7 -0
  287. package/src/resources/extensions/gsd/templates/project.md +1 -0
  288. package/src/resources/extensions/gsd/templates/roadmap.md +1 -1
  289. package/src/resources/extensions/gsd/templates/uat.md +5 -1
  290. package/src/resources/extensions/gsd/tests/ask-user-questions-render.test.ts +92 -0
  291. package/src/resources/extensions/gsd/tests/auto-dashboard.test.ts +29 -1
  292. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +321 -5
  293. package/src/resources/extensions/gsd/tests/auto-milestone-target.test.ts +23 -0
  294. package/src/resources/extensions/gsd/tests/auto-model-selection-tool-poisoning.test.ts +18 -0
  295. package/src/resources/extensions/gsd/tests/auto-orchestrator.test.ts +133 -4
  296. package/src/resources/extensions/gsd/tests/auto-runtime-state.test.ts +34 -0
  297. package/src/resources/extensions/gsd/tests/canonical-milestone-root.test.ts +20 -0
  298. package/src/resources/extensions/gsd/tests/codebase-generator.test.ts +22 -0
  299. package/src/resources/extensions/gsd/tests/commands-dispatcher-workspace-git.test.ts +11 -0
  300. package/src/resources/extensions/gsd/tests/commands-verdict.test.ts +38 -1
  301. package/src/resources/extensions/gsd/tests/dispatch-complete-milestone-guard.test.ts +34 -3
  302. package/src/resources/extensions/gsd/tests/dispatch-run-uat-browser-tools.test.ts +88 -0
  303. package/src/resources/extensions/gsd/tests/doctor-scope-db-unavailable.test.ts +18 -0
  304. package/src/resources/extensions/gsd/tests/execute-task-rendering.test.ts +1 -0
  305. package/src/resources/extensions/gsd/tests/fixtures/pr-body/swarm-lane-no-blockers.md +1 -5
  306. package/src/resources/extensions/gsd/tests/fixtures/pr-body/swarm-lane-with-blockers.md +1 -5
  307. package/src/resources/extensions/gsd/tests/gate-state-canonicalization.test.ts +48 -1
  308. package/src/resources/extensions/gsd/tests/mcp-tool-name.test.ts +34 -0
  309. package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +58 -0
  310. package/src/resources/extensions/gsd/tests/milestone-closeout-proof.test.ts +99 -0
  311. package/src/resources/extensions/gsd/tests/milestone-closeout.test.ts +25 -0
  312. package/src/resources/extensions/gsd/tests/milestone-merge-transaction.test.ts +46 -0
  313. package/src/resources/extensions/gsd/tests/milestone-readiness.test.ts +65 -0
  314. package/src/resources/extensions/gsd/tests/milestone-validation-evidence.test.ts +41 -0
  315. package/src/resources/extensions/gsd/tests/milestone-validation-verdict.test.ts +55 -0
  316. package/src/resources/extensions/gsd/tests/plan-milestone.test.ts +45 -0
  317. package/src/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +2 -0
  318. package/src/resources/extensions/gsd/tests/planning-crossval.test.ts +45 -0
  319. package/src/resources/extensions/gsd/tests/preferences-diagnostics.test.ts +67 -0
  320. package/src/resources/extensions/gsd/tests/preferences.test.ts +183 -0
  321. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +46 -0
  322. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +9 -0
  323. package/src/resources/extensions/gsd/tests/provider-payload-policy.test.ts +165 -0
  324. package/src/resources/extensions/gsd/tests/pull-request-process.test.ts +47 -0
  325. package/src/resources/extensions/gsd/tests/register-hooks-depth-verification.test.ts +94 -0
  326. package/src/resources/extensions/gsd/tests/roadmap-parse-regression.test.ts +40 -0
  327. package/src/resources/extensions/gsd/tests/runtime-invariant-modules.test.ts +25 -1
  328. package/src/resources/extensions/gsd/tests/session-start-footer.test.ts +80 -0
  329. package/src/resources/extensions/gsd/tests/single-writer-invariant.test.ts +101 -1
  330. package/src/resources/extensions/gsd/tests/stale-queued-milestone.test.ts +27 -0
  331. package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +2 -1
  332. package/src/resources/extensions/gsd/tests/tool-availability-audit.test.ts +35 -0
  333. package/src/resources/extensions/gsd/tests/tool-naming.test.ts +35 -42
  334. package/src/resources/extensions/gsd/tests/uat-policy.test.ts +23 -0
  335. package/src/resources/extensions/gsd/tests/unit-context-composer.test.ts +47 -0
  336. package/src/resources/extensions/gsd/tests/user-input-boundary.test.ts +86 -1
  337. package/src/resources/extensions/gsd/tests/validate-milestone-stuck-guard.test.ts +39 -0
  338. package/src/resources/extensions/gsd/tests/web-app-uat.test.ts +150 -0
  339. package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +126 -9
  340. package/src/resources/extensions/gsd/tests/workspace-git-preflight.test.ts +15 -0
  341. package/src/resources/extensions/gsd/tests/worktree-manager.test.ts +21 -0
  342. package/src/resources/extensions/gsd/tests/worktree-projection-writers.test.ts +1 -1
  343. package/src/resources/extensions/gsd/tests/worktree-telemetry.test.ts +22 -0
  344. package/src/resources/extensions/gsd/tests/write-gate.test.ts +79 -0
  345. package/src/resources/extensions/gsd/tool-contract.ts +86 -8
  346. package/src/resources/extensions/gsd/tool-presentation-plan.ts +16 -33
  347. package/src/resources/extensions/gsd/tool-surface-snapshot.ts +47 -0
  348. package/src/resources/extensions/gsd/tools/plan-milestone.ts +19 -160
  349. package/src/resources/extensions/gsd/tools/reassess-roadmap.ts +43 -0
  350. package/src/resources/extensions/gsd/tools/validate-milestone.ts +25 -84
  351. package/src/resources/extensions/gsd/uat-policy.ts +19 -10
  352. package/src/resources/extensions/gsd/uat-run.ts +10 -14
  353. package/src/resources/extensions/gsd/unit-context-composer.ts +85 -20
  354. package/src/resources/extensions/gsd/unit-runtime.ts +3 -2
  355. package/src/resources/extensions/gsd/unit-tool-contracts.ts +2 -1
  356. package/src/resources/extensions/gsd/user-input-boundary.ts +18 -0
  357. package/src/resources/extensions/gsd/validation-block-guard.ts +2 -0
  358. package/src/resources/extensions/gsd/web-app-uat.ts +101 -0
  359. package/src/resources/extensions/gsd/workflow-mcp.ts +22 -110
  360. package/src/resources/extensions/gsd/workflow-reconcile.ts +3 -3
  361. package/src/resources/extensions/gsd/workflow-tool-surface.ts +73 -0
  362. package/src/resources/extensions/gsd/workspace-git-guard.ts +1 -0
  363. package/src/resources/extensions/gsd/worktree-lifecycle.ts +7 -16
  364. package/src/resources/extensions/gsd/worktree-state-projection.ts +55 -7
  365. package/src/resources/extensions/gsd/worktree-telemetry.ts +16 -0
  366. package/src/resources/extensions/shared/interview-ui.ts +15 -2
  367. package/src/resources/shared/claude-runtime-floor.ts +248 -0
  368. package/dist/web/standalone/.next/static/chunks/2659.feb6499ca863ebfc.js +0 -1
  369. package/dist/web/standalone/.next/static/chunks/2772.151789db0edea835.js +0 -1
  370. package/dist/web/standalone/.next/static/chunks/4283.10a065467b5340d8.js +0 -2
  371. package/dist/web/standalone/.next/static/chunks/5826.960dc4634cc9b0d3.js +0 -1
  372. package/dist/web/standalone/.next/static/chunks/796.46f811c0fac23aab.js +0 -10
  373. package/dist/web/standalone/.next/static/chunks/8785.d32f7a61f55c1600.js +0 -1
  374. /package/dist/web/standalone/.next/static/{Qbr81pQ-pbQXP4bq2VXLv → 3PtrU9qGPEXwNLWkIyiqk}/_buildManifest.js +0 -0
  375. /package/dist/web/standalone/.next/static/{Qbr81pQ-pbQXP4bq2VXLv → 3PtrU9qGPEXwNLWkIyiqk}/_ssgManifest.js +0 -0
@@ -23,11 +23,12 @@ import {
23
23
  import { runUnit, shouldDeferUnitFailsafeTimeout } from "../auto/run-unit.js";
24
24
  import { scheduleAutoWakeup, _resetAutoWakeupsForTest } from "../auto/schedule-wakeup.js";
25
25
  import { writeUnitRuntimeRecord, readUnitRuntimeRecord } from "../unit-runtime.js";
26
- import { autoLoop } from "../auto/loop.js";
27
- import { runDispatch, runUnitPhase } from "../auto/phases.js";
26
+ import { autoLoop as rawAutoLoop } from "../auto/loop.js";
27
+ import { runPreDispatch, runDispatch, runUnitPhase } from "../auto/phases.js";
28
28
  import { detectStuck } from "../auto/detect-stuck.js";
29
- import type { UnitResult, AgentEndEvent } from "../auto/types.js";
29
+ import type { UnitResult, AgentEndEvent, LoopState } from "../auto/types.js";
30
30
  import type { LoopDeps } from "../auto/loop-deps.js";
31
+ import type { AutoAdvanceResult, AutoOrchestrationModule, AutoStatus, UnitRef } from "../auto/contracts.js";
31
32
  import { WorktreeStateProjection } from "../worktree-state-projection.js";
32
33
  import { ModelPolicyDispatchBlockedError } from "../auto-model-selection.js";
33
34
  import type { SessionLockStatus } from "../session-lock.js";
@@ -40,6 +41,17 @@ import { SourceObservationStore } from "../source-observations.js";
40
41
 
41
42
  // ─── Helpers ─────────────────────────────────────────────────────────────────
42
43
 
44
+ const ORCHESTRATION_MISSING_REASON =
45
+ "Auto Orchestration Module is not wired; cannot dispatch built-in GSD Unit.";
46
+
47
+ type CapturedAutoSideEffects<T> = {
48
+ result: T;
49
+ stopped: boolean;
50
+ stoppedReason?: string;
51
+ paused: boolean;
52
+ pausedReason?: string;
53
+ };
54
+
43
55
  function makeEvent(
44
56
  messages: unknown[] = [{ role: "assistant" }],
45
57
  ): AgentEndEvent {
@@ -67,12 +79,203 @@ async function waitForMicrotasks(
67
79
  assert.fail(`Timed out waiting for ${label}`);
68
80
  }
69
81
 
82
+ function makeLoopState(): LoopState {
83
+ return {
84
+ recentUnits: [],
85
+ stuckRecoveryAttempts: 0,
86
+ consecutiveFinalizeTimeouts: 0,
87
+ consecutiveDispatchCount: new Map<string, number>(),
88
+ lastDispatchedKey: null,
89
+ lastDispatchPhase: null,
90
+ };
91
+ }
92
+
93
+ function createLoopTestOrchestration(
94
+ ctx: any,
95
+ pi: any,
96
+ s: any,
97
+ deps: LoopDeps,
98
+ ): AutoOrchestrationModule {
99
+ // Production auto.ts wires the real Auto Orchestration Module before entering
100
+ // autoLoop. These loop-mechanics tests keep their LoopDeps fixtures by
101
+ // adapting the old phase helpers to the public orchestration Interface.
102
+ const loopState = makeLoopState();
103
+ const status: AutoStatus = { phase: "running", transitionCount: 0 };
104
+ let iteration = 0;
105
+ let seq = 0;
106
+
107
+ function nextSeq(): number {
108
+ return ++seq;
109
+ }
110
+
111
+ function clearActiveUnit(): void {
112
+ status.activeUnit = undefined;
113
+ }
114
+
115
+ async function captureAutoSideEffects<T>(
116
+ run: () => Promise<T>,
117
+ ): Promise<CapturedAutoSideEffects<T>> {
118
+ const originalStopAuto = deps.stopAuto;
119
+ const originalPauseAuto = deps.pauseAuto;
120
+ let stoppedReason: string | undefined;
121
+ let pausedReason: string | undefined;
122
+ let stopped = false;
123
+ let paused = false;
124
+
125
+ (deps as any).stopAuto = async (...args: Parameters<LoopDeps["stopAuto"]>) => {
126
+ stopped = true;
127
+ stoppedReason = args[2];
128
+ return originalStopAuto(...args);
129
+ };
130
+ (deps as any).pauseAuto = async (...args: Parameters<LoopDeps["pauseAuto"]>) => {
131
+ paused = true;
132
+ const context = args[2] as { message?: string } | undefined;
133
+ pausedReason = context?.message;
134
+ return originalPauseAuto(...args);
135
+ };
136
+
137
+ try {
138
+ const result = await run();
139
+ return { result, stopped, stoppedReason, paused, pausedReason };
140
+ } finally {
141
+ (deps as any).stopAuto = originalStopAuto;
142
+ (deps as any).pauseAuto = originalPauseAuto;
143
+ }
144
+ }
145
+
146
+ function resultForBreak(
147
+ reason: string,
148
+ sideEffects: CapturedAutoSideEffects<unknown>,
149
+ ): AutoAdvanceResult {
150
+ clearActiveUnit();
151
+ status.phase = sideEffects.paused && !sideEffects.stopped ? "paused" : "stopped";
152
+ status.transitionCount += 1;
153
+ if (sideEffects.paused && !sideEffects.stopped) {
154
+ return {
155
+ kind: "blocked",
156
+ reason: sideEffects.pausedReason ?? reason,
157
+ action: "pause",
158
+ };
159
+ }
160
+ return {
161
+ kind: "stopped",
162
+ reason: sideEffects.stoppedReason ?? reason,
163
+ };
164
+ }
165
+
166
+ return {
167
+ async start() {
168
+ status.phase = "running";
169
+ status.transitionCount += 1;
170
+ return { kind: "started" };
171
+ },
172
+ async advance() {
173
+ iteration += 1;
174
+ seq = 0;
175
+ const prefs = deps.loadEffectiveGSDPreferences()?.preferences;
176
+ const ic = {
177
+ ctx,
178
+ pi,
179
+ s,
180
+ deps,
181
+ prefs,
182
+ iteration,
183
+ flowId: `loop-test-orchestration-${iteration}`,
184
+ nextSeq,
185
+ };
186
+
187
+ const preDispatch = await captureAutoSideEffects(() => runPreDispatch(ic, loopState));
188
+ const preDispatchResult = preDispatch.result;
189
+ if (preDispatchResult.action === "break") {
190
+ return resultForBreak(preDispatchResult.reason, preDispatch);
191
+ }
192
+ if (preDispatchResult.action === "continue") {
193
+ return { kind: "skipped", reason: "pre-dispatch-skip" };
194
+ }
195
+ if (preDispatchResult.action === "retry") {
196
+ return { kind: "paused", reason: preDispatchResult.reason };
197
+ }
198
+
199
+ const dispatch = await captureAutoSideEffects(() =>
200
+ runDispatch(ic, preDispatchResult.data, loopState),
201
+ );
202
+ if (dispatch.result.action === "break") {
203
+ return resultForBreak(dispatch.result.reason, dispatch);
204
+ }
205
+ if (dispatch.result.action === "continue") {
206
+ return {
207
+ kind: "skipped",
208
+ reason: "dispatch-skip",
209
+ stateSnapshot: preDispatchResult.data.state,
210
+ };
211
+ }
212
+ if (dispatch.result.action === "retry") {
213
+ return { kind: "paused", reason: dispatch.result.reason };
214
+ }
215
+
216
+ const data = dispatch.result.data;
217
+ const unit: UnitRef = { unitType: data.unitType, unitId: data.unitId };
218
+ s.pendingOrchestrationDispatch = {
219
+ unitType: data.unitType,
220
+ unitId: data.unitId,
221
+ prompt: data.prompt,
222
+ pauseAfterUatDispatch: data.pauseAfterUatDispatch,
223
+ state: data.state,
224
+ mid: data.mid,
225
+ midTitle: data.midTitle,
226
+ };
227
+ status.phase = "running";
228
+ status.activeUnit = unit;
229
+ status.transitionCount += 1;
230
+ return { kind: "advanced", unit, stateSnapshot: data.state };
231
+ },
232
+ async completeActiveUnit() {
233
+ clearActiveUnit();
234
+ },
235
+ async retryActiveUnit() {
236
+ clearActiveUnit();
237
+ },
238
+ async resume() {
239
+ status.phase = "running";
240
+ status.transitionCount += 1;
241
+ return { kind: "resumed" };
242
+ },
243
+ async stop(reason: string) {
244
+ status.phase = "stopped";
245
+ clearActiveUnit();
246
+ status.transitionCount += 1;
247
+ return { kind: "stopped", reason };
248
+ },
249
+ getStatus() {
250
+ return {
251
+ ...status,
252
+ activeUnit: status.activeUnit ? { ...status.activeUnit } : undefined,
253
+ };
254
+ },
255
+ };
256
+ }
257
+
258
+ async function autoLoop(
259
+ ctx: any,
260
+ pi: any,
261
+ s: any,
262
+ deps: LoopDeps,
263
+ options?: Parameters<typeof rawAutoLoop>[4],
264
+ ): Promise<void> {
265
+ if (!s.orchestration) {
266
+ s.orchestration = createLoopTestOrchestration(ctx, pi, s, deps);
267
+ }
268
+ await rawAutoLoop(ctx, pi, s, deps, options);
269
+ }
270
+
70
271
  /**
71
272
  * Build a minimal mock AutoSession with controllable newSession behavior.
72
273
  */
73
274
  function makeMockSession(opts?: {
74
275
  newSessionResult?: { cancelled: boolean };
75
276
  newSessionThrows?: string;
277
+ /** Reject newSession() with a specific Error instance (e.g. TypeError). */
278
+ newSessionThrowsError?: Error;
76
279
  newSessionDelayMs?: number;
77
280
  onNewSessionStart?: (session: any) => void;
78
281
  onNewSessionSettle?: (session: any) => void;
@@ -87,6 +290,9 @@ function makeMockSession(opts?: {
87
290
  cmdCtx: {
88
291
  newSession: (options?: { abortSignal?: AbortSignal; workspaceRoot?: string }) => {
89
292
  opts?.onNewSessionStart?.(session);
293
+ if (opts?.newSessionThrowsError) {
294
+ return Promise.reject(opts.newSessionThrowsError);
295
+ }
90
296
  if (opts?.newSessionThrows) {
91
297
  return Promise.reject(new Error(opts.newSessionThrows));
92
298
  }
@@ -497,6 +703,73 @@ test("runUnit returns cancelled when session creation fails", async () => {
497
703
  assert.equal(pi.calls.length, 0);
498
704
  });
499
705
 
706
+ test("runUnit: TypeError from newSession is classified as structural (isTransient: false)", async () => {
707
+ // Regression for #572: a TypeError thrown from newSession (e.g. "something is
708
+ // not a function") indicates a programming error, not a transient provider
709
+ // blip. Before the fix it was always classified isTransient: true, causing
710
+ // auto-mode to retry indefinitely instead of surfacing the real problem.
711
+ _resetPendingResolve();
712
+
713
+ const baseCtx = {
714
+ ...makeMockCtx(),
715
+ ui: { notify: () => {}, setStatus: () => {}, setWorkingMessage: () => {} },
716
+ sessionManager: { getEntries: () => [] },
717
+ modelRegistry: { getProviderAuthMode: () => undefined, isProviderRequestReady: () => true },
718
+ } as any;
719
+ const pi = makeMockPi();
720
+ const s = makeMockSession({
721
+ newSessionThrowsError: new TypeError("pi.sendMessage is not a function"),
722
+ });
723
+
724
+ const result = await runUnit(baseCtx, pi, s, "task", "T01", "prompt");
725
+
726
+ assert.equal(result.status, "cancelled");
727
+ assert.equal(result.errorContext?.category, "session-failed");
728
+ assert.equal(result.errorContext?.isTransient, false, "TypeError must be non-transient");
729
+ });
730
+
731
+ test("runUnit: 'is not a function' message from newSession is classified as structural", async () => {
732
+ // Regression for #572: the pattern also catches errors where the thrown
733
+ // object is not a TypeError instance but the message contains "is not a function".
734
+ _resetPendingResolve();
735
+
736
+ const baseCtx = {
737
+ ...makeMockCtx(),
738
+ ui: { notify: () => {}, setStatus: () => {}, setWorkingMessage: () => {} },
739
+ sessionManager: { getEntries: () => [] },
740
+ modelRegistry: { getProviderAuthMode: () => undefined, isProviderRequestReady: () => true },
741
+ } as any;
742
+ const pi = makeMockPi();
743
+ const s = makeMockSession({ newSessionThrows: "pi.sendMessage is not a function" });
744
+
745
+ const result = await runUnit(baseCtx, pi, s, "task", "T01", "prompt");
746
+
747
+ assert.equal(result.status, "cancelled");
748
+ assert.equal(result.errorContext?.category, "session-failed");
749
+ assert.equal(result.errorContext?.isTransient, false, "'is not a function' errors must be non-transient");
750
+ });
751
+
752
+ test("runUnit: generic network error from newSession remains transient", async () => {
753
+ // Confirm that non-structural session errors (e.g. 429, ECONNREFUSED) are
754
+ // still classified as transient so auto-mode can retry them.
755
+ _resetPendingResolve();
756
+
757
+ const baseCtx = {
758
+ ...makeMockCtx(),
759
+ ui: { notify: () => {}, setStatus: () => {}, setWorkingMessage: () => {} },
760
+ sessionManager: { getEntries: () => [] },
761
+ modelRegistry: { getProviderAuthMode: () => undefined, isProviderRequestReady: () => true },
762
+ } as any;
763
+ const pi = makeMockPi();
764
+ const s = makeMockSession({ newSessionThrows: "connection refused" });
765
+
766
+ const result = await runUnit(baseCtx, pi, s, "task", "T01", "prompt");
767
+
768
+ assert.equal(result.status, "cancelled");
769
+ assert.equal(result.errorContext?.category, "session-failed");
770
+ assert.equal(result.errorContext?.isTransient, true, "network errors must remain transient");
771
+ });
772
+
500
773
  test("runUnit clears queued switch cancellation when session creation fails", async () => {
501
774
  _resetPendingResolve();
502
775
 
@@ -1164,6 +1437,34 @@ test("autoLoop exits when s.active is set to false", async (t) => {
1164
1437
  );
1165
1438
  });
1166
1439
 
1440
+ test("autoLoop pauses visibly when Auto Orchestration Module is not wired", async () => {
1441
+ _resetPendingResolve();
1442
+
1443
+ const ctx = makeMockCtx();
1444
+ ctx.ui.setStatus = () => {};
1445
+ const pi = makeMockPi();
1446
+ const s = makeLoopSession();
1447
+ let pauseContext: unknown;
1448
+
1449
+ const deps = makeMockDeps({
1450
+ pauseAuto: async (_ctx, _pi, errorContext) => {
1451
+ pauseContext = errorContext;
1452
+ deps.callLog.push("pauseAuto");
1453
+ },
1454
+ });
1455
+
1456
+ await rawAutoLoop(ctx, pi, s, deps);
1457
+
1458
+ assert.ok(deps.callLog.includes("pauseAuto"), "missing orchestration should pause auto-mode");
1459
+ assert.equal(
1460
+ (pauseContext as { message?: string } | undefined)?.message,
1461
+ ORCHESTRATION_MISSING_REASON,
1462
+ );
1463
+ assert.equal(deps.callLog.includes("deriveState"), false);
1464
+ assert.equal(deps.callLog.includes("resolveDispatch"), false);
1465
+ assert.equal(s.pendingOrchestrationDispatch, null);
1466
+ });
1467
+
1167
1468
  test("autoLoop exits on terminal complete state", async (t) => {
1168
1469
  _resetPendingResolve();
1169
1470
 
@@ -1990,11 +2291,15 @@ test("autoLoop retries next iteration when orchestration reports paused", async
1990
2291
  },
1991
2292
  });
1992
2293
 
2294
+ const journalEvents: Array<{ eventType: string; data?: any }> = [];
1993
2295
  const deps = makeMockDeps({
1994
2296
  resolveDispatch: async () => {
1995
2297
  deps.callLog.push("resolveDispatch");
1996
2298
  throw new Error("legacy resolveDispatch must not run after orchestration paused");
1997
2299
  },
2300
+ emitJournalEvent: (entry: any) => {
2301
+ journalEvents.push(entry);
2302
+ },
1998
2303
  });
1999
2304
 
2000
2305
  await autoLoop(ctx, pi, s, deps);
@@ -2007,6 +2312,11 @@ test("autoLoop retries next iteration when orchestration reports paused", async
2007
2312
  "orchestration paused must not fall back to legacy dispatch",
2008
2313
  );
2009
2314
  assert.equal(s.pendingOrchestrationDispatch, null, "no orchestration dispatch should remain pending");
2315
+
2316
+ const pausedIterationEnd = journalEvents.find(
2317
+ (e) => e.eventType === "iteration-end" && e.data?.status === "paused",
2318
+ );
2319
+ assert.ok(pausedIterationEnd, "orchestration paused must emit iteration-end to close the iteration journal");
2010
2320
  });
2011
2321
 
2012
2322
  test("autoLoop consumes pending orchestration dispatch without advancing twice", async () => {
@@ -2094,8 +2404,13 @@ test("autoLoop stops orchestrator complete state through completion surface", as
2094
2404
  start: async () => ({ kind: "stopped" as const, reason: "unused" }),
2095
2405
  advance: async () => ({
2096
2406
  kind: "stopped" as const,
2097
- reason: "all milestones complete",
2407
+ reason: "legacy text not used",
2098
2408
  stateSnapshot,
2409
+ terminalOutcome: {
2410
+ code: "all-complete" as const,
2411
+ displayReason: "All milestones complete",
2412
+ allMilestonesComplete: true as const,
2413
+ },
2099
2414
  }),
2100
2415
  completeActiveUnit: async () => {},
2101
2416
  retryActiveUnit: async () => {},
@@ -2121,6 +2436,7 @@ test("autoLoop stops orchestrator complete state through completion surface", as
2121
2436
  milestoneTitle: "Priority Levels",
2122
2437
  allMilestonesComplete: true,
2123
2438
  });
2439
+ assert.equal((stopCalls[0]?.options as any)?.terminalOutcome?.code, "all-complete");
2124
2440
  assert.equal(
2125
2441
  deps.callLog.includes("resolveDispatch"),
2126
2442
  false,
@@ -2960,7 +3276,7 @@ test("autoLoop closes journal iteration on pre-dispatch health-gate break", asyn
2960
3276
  assert.equal(deps.callLog.includes("pauseAuto"), true);
2961
3277
  assert.deepEqual(pauseOptions, { expectedCurrentUnit: null });
2962
3278
  assert.ok(
2963
- journalEvents.some((event) => event.eventType === "iteration-end" && event.data?.reason === "pre-dispatch-break"),
3279
+ journalEvents.some((event) => event.eventType === "iteration-end" && event.data?.reason === "health-gate-failed"),
2964
3280
  "pre-dispatch break must close the started iteration",
2965
3281
  );
2966
3282
  });
@@ -1,8 +1,13 @@
1
1
  import { describe, it } from "node:test";
2
2
  import assert from "node:assert/strict";
3
+ import { readFileSync } from "node:fs";
4
+ import { dirname, join } from "node:path";
5
+ import { fileURLToPath } from "node:url";
3
6
 
4
7
  import { parseMilestoneTarget, parseModelFlag } from "../commands/handlers/auto.js";
5
8
 
9
+ const __dirname = dirname(fileURLToPath(import.meta.url));
10
+
6
11
  describe("parseMilestoneTarget", () => {
7
12
  it("extracts a simple milestone ID", () => {
8
13
  const result = parseMilestoneTarget("auto M016");
@@ -60,6 +65,24 @@ describe("parseMilestoneTarget", () => {
60
65
  });
61
66
  });
62
67
 
68
+ describe("auto preference diagnostics", () => {
69
+ it("notifies preference diagnostics before launching auto-mode", () => {
70
+ const source = readFileSync(join(__dirname, "..", "commands", "handlers", "auto.ts"), "utf-8");
71
+ const autoBlockIndex = source.indexOf('if (trimmed === "auto" || trimmed.startsWith("auto "))');
72
+ assert.ok(autoBlockIndex >= 0, "auto command block should exist");
73
+ const nextBlockIndex = source.indexOf('if (trimmed === "stop")', autoBlockIndex);
74
+ const autoBlock = source.slice(autoBlockIndex, nextBlockIndex);
75
+ const notifyIndex = autoBlock.indexOf("notifyPreferenceDiagnostics");
76
+ assert.ok(notifyIndex >= 0, "auto command should notify preference diagnostics");
77
+ for (const match of autoBlock.matchAll(/startAutoDetached/g)) {
78
+ assert.ok(
79
+ notifyIndex < match.index!,
80
+ "preference diagnostics should be notified before each auto-mode launch",
81
+ );
82
+ }
83
+ });
84
+ });
85
+
63
86
  describe("parseModelFlag", () => {
64
87
  it("extracts provider/model from --model", () => {
65
88
  const result = parseModelFlag("auto --model openrouter/openai/gpt-5.4");
@@ -33,6 +33,7 @@ import {
33
33
  selectAndApplyModel,
34
34
  ModelPolicyDispatchBlockedError,
35
35
  clearToolBaseline,
36
+ getRegisteredToolSnapshot,
36
37
  getToolBaselineSnapshot,
37
38
  } from "../auto-model-selection.js";
38
39
  import { applyModelPolicyFilter } from "../uok/model-policy.js";
@@ -773,3 +774,20 @@ test("baseline union (#477): getToolBaselineSnapshot includes live tools not pre
773
774
  env.cleanup();
774
775
  }
775
776
  });
777
+
778
+ test("registered tool snapshot includes tools hidden from the active surface", () => {
779
+ const pi = {
780
+ getActiveTools: () => ["read"],
781
+ getAllTools: () => [
782
+ { name: "read" },
783
+ { name: "browser_navigate" },
784
+ { name: "mcp__gsd-browser__*" },
785
+ ],
786
+ };
787
+
788
+ assert.deepEqual(getRegisteredToolSnapshot(pi as any), [
789
+ "read",
790
+ "browser_navigate",
791
+ "mcp__gsd-browser__*",
792
+ ]);
793
+ });
@@ -33,6 +33,8 @@ import type { UnifiedRule } from "../rule-types.js";
33
33
  import { supportsStructuredQuestions } from "../workflow-mcp.js";
34
34
  import {
35
35
  closeDatabase,
36
+ insertAssessment,
37
+ insertGateRow,
36
38
  insertMilestone,
37
39
  insertSlice,
38
40
  insertTask,
@@ -302,6 +304,46 @@ test("advance() sets active unit and is reflected in status", async (t) => {
302
304
  });
303
305
  });
304
306
 
307
+ test("advance() blocks source dispatch when an earlier slice is incomplete", async (t) => {
308
+ const f = makeFixture({
309
+ dispatch: () => ({
310
+ action: "dispatch",
311
+ unitType: "execute-task",
312
+ unitId: "M001/S02/T01",
313
+ prompt: "fixture-prompt",
314
+ }),
315
+ });
316
+ t.after(() => f.cleanup());
317
+
318
+ insertSlice({
319
+ id: "S02",
320
+ milestoneId: "M001",
321
+ title: "Second slice",
322
+ status: "active",
323
+ risk: "low",
324
+ depends: [],
325
+ demo: "",
326
+ sequence: 2,
327
+ });
328
+ insertTask({
329
+ id: "T01",
330
+ sliceId: "S02",
331
+ milestoneId: "M001",
332
+ title: "Second task",
333
+ status: "active",
334
+ });
335
+
336
+ const result = await f.orchestrator.advance();
337
+
338
+ assert.equal(result.kind, "blocked");
339
+ if (result.kind !== "blocked") return;
340
+ assert.equal(result.action, "stop");
341
+ assert.match(result.reason, /earlier slice M001\/S01 is not complete/);
342
+ assert.equal(f.session.pendingOrchestrationDispatch, null);
343
+ assert.deepEqual(f.orchestrator.getStatus().activeUnit, undefined);
344
+ assert.ok(f.journalNames().includes("advance-blocked"));
345
+ });
346
+
305
347
  test("getStatus() returns defensive copy of activeUnit", async (t) => {
306
348
  const f = makeFixture();
307
349
  t.after(() => f.cleanup());
@@ -374,10 +416,65 @@ test("advance() reports completion when complete state has no next unit", async
374
416
 
375
417
  assert.equal(result.kind, "stopped");
376
418
  if (result.kind !== "stopped") return;
377
- assert.equal(result.reason, "all milestones complete");
419
+ assert.equal(result.reason, "All milestones complete");
420
+ assert.equal(result.terminalOutcome?.code, "all-complete");
378
421
  assert.equal(f.orchestrator.getStatus().phase, "stopped");
379
422
  });
380
423
 
424
+ test("advance() blocks all-complete stop when completed milestone is still unmerged in a worktree", async (t) => {
425
+ const f = makeFixture({ complete: true, noTask: true });
426
+ t.after(() => f.cleanup());
427
+
428
+ insertSlice({
429
+ id: "S01",
430
+ milestoneId: "M001",
431
+ title: "Slice",
432
+ status: "complete",
433
+ risk: "low",
434
+ depends: [],
435
+ demo: "",
436
+ sequence: 1,
437
+ });
438
+ insertAssessment({
439
+ path: "milestones/M001/M001-VALIDATION.md",
440
+ milestoneId: "M001",
441
+ status: "pass",
442
+ scope: "milestone-validation",
443
+ fullContent: "verdict: pass",
444
+ });
445
+ insertGateRow({
446
+ milestoneId: "M001",
447
+ sliceId: "S01",
448
+ gateId: "Q3",
449
+ scope: "slice",
450
+ status: "pending",
451
+ });
452
+
453
+ const worktreePath = join(f.base, ".gsd", "worktrees", "M001");
454
+ mkdirSync(join(f.base, ".gsd", "worktrees"), { recursive: true });
455
+ execFileSync("git", ["worktree", "add", "-b", "milestone/M001", worktreePath], { cwd: f.base, stdio: "ignore" });
456
+ mkdirSync(join(worktreePath, ".gsd", "milestones", "M001"), { recursive: true });
457
+ writeFileSync(join(worktreePath, ".gsd", "milestones", "M001", "M001-SUMMARY.md"), "# Milestone Summary\n");
458
+ f.session.basePath = worktreePath;
459
+ f.session.originalBasePath = f.base;
460
+ f.session.currentMilestoneId = "M001";
461
+ f.session.milestoneMergedInPhases = false;
462
+
463
+ const result = await f.orchestrator.advance();
464
+
465
+ assert.equal(result.kind, "blocked");
466
+ if (result.kind !== "blocked") return;
467
+ assert.equal(result.action, "pause");
468
+ assert.equal(result.terminalOutcome?.code, "settlement-blocked");
469
+ assert.match(result.reason, /worktree branch has not been merged to main/);
470
+ assert.doesNotMatch(result.reason, /quality gate Q3 is still pending/);
471
+ assert.equal(f.orchestrator.getStatus().phase, "paused");
472
+ assert.equal(f.session.milestoneSettlement?.ok, false);
473
+ const names = f.journalNames();
474
+ assert.ok(names.includes("advance-blocked"));
475
+ assert.ok(!names.includes("advance-stopped"));
476
+ });
477
+
381
478
  test("advance() stopped clears previous activeUnit and resets idempotent lock", async (t) => {
382
479
  // First advance dispatches; then we make the milestone resolve to no unit by
383
480
  // closing it on disk + DB and re-deriving. Simpler: drive a fixture that
@@ -696,7 +793,10 @@ test("stuck-loop: start() resets the ring so a fresh saturation cycle is require
696
793
  assert.equal(next.kind, "advanced");
697
794
  });
698
795
 
699
- test("stuck-loop: resume() resets the ring", async (t) => {
796
+ test("stuck-loop: resume() preserves ring so detection accumulates across pause/resume", async (t) => {
797
+ // Regression for #572: resume() must NOT reset dispatchKeyWindow. Before the
798
+ // fix, a pause/resume cycle cleared the window, letting a stuck loop silently
799
+ // re-accumulate STUCK_WINDOW_SIZE dispatches before being detected again.
700
800
  const f = makeFixture();
701
801
  t.after(() => f.cleanup());
702
802
 
@@ -707,11 +807,39 @@ test("stuck-loop: resume() resets the ring", async (t) => {
707
807
  const resumed = await f.orchestrator.resume();
708
808
  assert.equal(resumed.kind, "resumed");
709
809
 
810
+ // The ring is preserved, so the next advance pushes it to STUCK_WINDOW_SIZE
811
+ // and triggers stuck-loop detection — not a fresh dispatch.
710
812
  const next = await f.orchestrator.advance();
711
- assert.equal(next.kind, "advanced");
813
+ assert.equal(next.kind, "blocked");
814
+ if (next.kind !== "blocked") return;
815
+ assert.equal(next.action, "stop");
816
+ assert.ok(next.reason.startsWith("stuck-loop:"), `expected stuck-loop reason, got: ${next.reason}`);
817
+ });
818
+
819
+ test("stuck-loop: stop('pause') preserves ring across the stop/resume cycle", async (t) => {
820
+ // Regression for #572: stop("pause") must behave the same as resume() —
821
+ // the window must survive so detection accumulates across pause/resume pairs.
822
+ const f = makeFixture();
823
+ t.after(() => f.cleanup());
824
+
825
+ for (let i = 0; i < STUCK_WINDOW_SIZE - 1; i++) {
826
+ await f.orchestrator.advance();
827
+ }
828
+
829
+ const stopped = await f.orchestrator.stop("pause");
830
+ assert.equal(stopped.kind, "stopped");
831
+
832
+ const resumed = await f.orchestrator.resume();
833
+ assert.equal(resumed.kind, "resumed");
834
+
835
+ const next = await f.orchestrator.advance();
836
+ assert.equal(next.kind, "blocked");
837
+ if (next.kind !== "blocked") return;
838
+ assert.equal(next.action, "stop");
839
+ assert.ok(next.reason.startsWith("stuck-loop:"), `expected stuck-loop reason, got: ${next.reason}`);
712
840
  });
713
841
 
714
- test("stuck-loop: stop() resets the ring", async (t) => {
842
+ test("stuck-loop: stop('user-request') resets the ring (hard stop)", async (t) => {
715
843
  const f = makeFixture();
716
844
  t.after(() => f.cleanup());
717
845
 
@@ -722,6 +850,7 @@ test("stuck-loop: stop() resets the ring", async (t) => {
722
850
  const stopped = await f.orchestrator.stop("user-request");
723
851
  assert.equal(stopped.kind, "stopped");
724
852
 
853
+ // Hard stop clears the ring, so the next advance dispatches fresh.
725
854
  const next = await f.orchestrator.advance();
726
855
  assert.equal(next.kind, "advanced");
727
856
  });