gsd-pi 2.81.0-dev.72a81bdf3 → 2.82.0-dev.20138ae42

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 (463) hide show
  1. package/README.md +50 -31
  2. package/dist/resources/.managed-resources-content-hash +1 -1
  3. package/dist/resources/GSD-WORKFLOW.md +10 -1
  4. package/dist/resources/extensions/browser-tools/tools/screenshot.js +1 -0
  5. package/dist/resources/extensions/browser-tools/tools/zoom.js +1 -0
  6. package/dist/resources/extensions/claude-code-cli/partial-builder.js +2 -1
  7. package/dist/resources/extensions/cmux/index.js +5 -0
  8. package/dist/resources/extensions/gsd/auto/infra-errors.js +9 -3
  9. package/dist/resources/extensions/gsd/auto/loop.js +5 -5
  10. package/dist/resources/extensions/gsd/auto/orchestrator.js +124 -6
  11. package/dist/resources/extensions/gsd/auto/phases.js +30 -1
  12. package/dist/resources/extensions/gsd/auto/workflow-memory-pressure.js +12 -0
  13. package/dist/resources/extensions/gsd/auto-dashboard.js +66 -1
  14. package/dist/resources/extensions/gsd/auto-dispatch.js +13 -6
  15. package/dist/resources/extensions/gsd/auto-model-selection.js +2 -0
  16. package/dist/resources/extensions/gsd/auto-post-unit.js +233 -127
  17. package/dist/resources/extensions/gsd/auto-prompts.js +13 -5
  18. package/dist/resources/extensions/gsd/auto-recovery.js +31 -1
  19. package/dist/resources/extensions/gsd/auto-start.js +85 -12
  20. package/dist/resources/extensions/gsd/auto-verification.js +28 -22
  21. package/dist/resources/extensions/gsd/auto-worktree.js +111 -1
  22. package/dist/resources/extensions/gsd/auto.js +158 -55
  23. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +25 -6
  24. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +9 -8
  25. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +9 -2
  26. package/dist/resources/extensions/gsd/bootstrap/subagent-input.js +21 -9
  27. package/dist/resources/extensions/gsd/bootstrap/system-context.js +55 -12
  28. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +16 -2
  29. package/dist/resources/extensions/gsd/clean-root-preflight.js +170 -8
  30. package/dist/resources/extensions/gsd/commands/catalog.js +4 -1
  31. package/dist/resources/extensions/gsd/commands/handlers/core.js +38 -1
  32. package/dist/resources/extensions/gsd/commands-bootstrap.js +5 -0
  33. package/dist/resources/extensions/gsd/commands-handlers.js +15 -2
  34. package/dist/resources/extensions/gsd/context-store.js +112 -0
  35. package/dist/resources/extensions/gsd/crash-recovery.js +31 -5
  36. package/dist/resources/extensions/gsd/db/unit-dispatches.js +3 -2
  37. package/dist/resources/extensions/gsd/db-writer.js +150 -84
  38. package/dist/resources/extensions/gsd/dispatch-guard.js +2 -2
  39. package/dist/resources/extensions/gsd/docs/preferences-reference.md +1 -1
  40. package/dist/resources/extensions/gsd/doctor-git-checks.js +41 -6
  41. package/dist/resources/extensions/gsd/doctor-runtime-checks.js +28 -11
  42. package/dist/resources/extensions/gsd/doctor.js +2 -28
  43. package/dist/resources/extensions/gsd/export-html.js +27 -425
  44. package/dist/resources/extensions/gsd/git-service.js +39 -1
  45. package/dist/resources/extensions/gsd/gsd-db.js +1 -0
  46. package/dist/resources/extensions/gsd/guided-flow.js +93 -111
  47. package/dist/resources/extensions/gsd/guided-unit-context.js +23 -0
  48. package/dist/resources/extensions/gsd/knowledge-backfill.js +144 -0
  49. package/dist/resources/extensions/gsd/knowledge-capture.js +136 -0
  50. package/dist/resources/extensions/gsd/knowledge-parser.js +154 -0
  51. package/dist/resources/extensions/gsd/knowledge-projection.js +210 -0
  52. package/dist/resources/extensions/gsd/markdown-renderer.js +6 -1
  53. package/dist/resources/extensions/gsd/md-importer.js +1 -1
  54. package/dist/resources/extensions/gsd/memory-backfill.js +73 -17
  55. package/dist/resources/extensions/gsd/memory-consolidation-scanner.js +222 -0
  56. package/dist/resources/extensions/gsd/migrate/command.js +5 -0
  57. package/dist/resources/extensions/gsd/migrate/parsers.js +10 -0
  58. package/dist/resources/extensions/gsd/migrate/preview.js +9 -0
  59. package/dist/resources/extensions/gsd/migrate/transformer.js +51 -4
  60. package/dist/resources/extensions/gsd/migrate/writer.js +11 -1
  61. package/dist/resources/extensions/gsd/migration-auto-check.js +12 -17
  62. package/dist/resources/extensions/gsd/milestone-actions.js +11 -4
  63. package/dist/resources/extensions/gsd/native-git-bridge.js +48 -12
  64. package/dist/resources/extensions/gsd/pending-auto-start.js +52 -0
  65. package/dist/resources/extensions/gsd/post-execution-checks.js +73 -2
  66. package/dist/resources/extensions/gsd/pre-execution-checks.js +28 -1
  67. package/dist/resources/extensions/gsd/prompt-loader.js +1 -1
  68. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +1 -1
  69. package/dist/resources/extensions/gsd/prompts/complete-slice.md +1 -1
  70. package/dist/resources/extensions/gsd/prompts/discuss-headless.md +8 -8
  71. package/dist/resources/extensions/gsd/prompts/discuss.md +9 -9
  72. package/dist/resources/extensions/gsd/prompts/guided-discuss-project.md +4 -4
  73. package/dist/resources/extensions/gsd/prompts/guided-discuss-requirements.md +3 -3
  74. package/dist/resources/extensions/gsd/prompts/plan-slice.md +4 -4
  75. package/dist/resources/extensions/gsd/prompts/queue.md +4 -4
  76. package/dist/resources/extensions/gsd/prompts/refine-slice.md +2 -2
  77. package/dist/resources/extensions/gsd/prompts/rewrite-docs.md +1 -1
  78. package/dist/resources/extensions/gsd/prompts/system.md +2 -2
  79. package/dist/resources/extensions/gsd/provider-switch-observer.js +146 -0
  80. package/dist/resources/extensions/gsd/smart-entry-routing.js +36 -0
  81. package/dist/resources/extensions/gsd/state-reconciliation/drift/merge-state.js +6 -1
  82. package/dist/resources/extensions/gsd/state-reconciliation/drift/project-md.js +9 -14
  83. package/dist/resources/extensions/gsd/state-reconciliation/drift/roadmap.js +19 -24
  84. package/dist/resources/extensions/gsd/status-guards.js +4 -0
  85. package/dist/resources/extensions/gsd/templates/knowledge.md +2 -2
  86. package/dist/resources/extensions/gsd/templates/plan.md +8 -5
  87. package/dist/resources/extensions/gsd/templates/task-plan.md +4 -2
  88. package/dist/resources/extensions/gsd/tools/complete-milestone.js +6 -8
  89. package/dist/resources/extensions/gsd/tools/complete-slice.js +6 -8
  90. package/dist/resources/extensions/gsd/tools/plan-milestone.js +7 -1
  91. package/dist/resources/extensions/gsd/tools/plan-slice.js +89 -14
  92. package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +119 -0
  93. package/dist/resources/extensions/gsd/unit-context-manifest.js +32 -10
  94. package/dist/resources/extensions/gsd/validation.js +23 -1
  95. package/dist/resources/extensions/gsd/verification-gate.js +68 -7
  96. package/dist/resources/extensions/gsd/verification-verdict.js +26 -0
  97. package/dist/resources/extensions/gsd/workflow-projections.js +6 -8
  98. package/dist/resources/extensions/gsd/worktree-lifecycle.js +54 -10
  99. package/dist/resources/extensions/shared/html-shell.js +388 -0
  100. package/dist/resources/extensions/subagent/index.js +448 -78
  101. package/dist/resources/extensions/subagent/launch.js +77 -0
  102. package/dist/resources/extensions/subagent/run-store.js +148 -0
  103. package/dist/resources/extensions/visual-brief/artifact-policy.js +29 -0
  104. package/dist/resources/extensions/visual-brief/extension-manifest.json +8 -0
  105. package/dist/resources/extensions/visual-brief/index.js +5 -0
  106. package/dist/resources/extensions/visual-brief/page-contract.js +124 -0
  107. package/dist/resources/extensions/visual-brief/prompts.js +140 -0
  108. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  109. package/dist/web/standalone/.next/BUILD_ID +1 -1
  110. package/dist/web/standalone/.next/app-path-routes-manifest.json +12 -12
  111. package/dist/web/standalone/.next/build-manifest.json +3 -3
  112. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  113. package/dist/web/standalone/.next/react-loadable-manifest.json +3 -3
  114. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  115. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  116. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  117. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  118. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  119. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  120. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  121. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  122. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  123. package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
  124. package/dist/web/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
  125. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  126. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  127. package/dist/web/standalone/.next/server/app/_not-found.rsc +4 -7
  128. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +4 -7
  129. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  130. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +4 -5
  131. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  132. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  133. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -5
  134. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  135. package/dist/web/standalone/.next/server/app/index.html +1 -1
  136. package/dist/web/standalone/.next/server/app/index.rsc +4 -7
  137. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  138. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +4 -7
  139. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  140. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +4 -5
  141. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -5
  142. package/dist/web/standalone/.next/server/app/page.js +2 -2
  143. package/dist/web/standalone/.next/server/app/page.js.nft.json +1 -1
  144. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  145. package/dist/web/standalone/.next/server/app-paths-manifest.json +12 -12
  146. package/dist/web/standalone/.next/server/chunks/4266.js +2 -0
  147. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  148. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  149. package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
  150. package/dist/web/standalone/.next/server/next-font-manifest.json +1 -1
  151. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  152. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  153. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  154. package/dist/web/standalone/.next/static/chunks/2973.33f26573894b6153.js +2 -0
  155. package/dist/web/standalone/.next/static/chunks/{8359.e059d86b255fce1c.js → 8359.7eb3bb8f8ecf4c01.js} +2 -2
  156. package/dist/web/standalone/.next/static/chunks/app/layout-8c10ec293ae0f1d5.js +1 -0
  157. package/dist/web/standalone/.next/static/chunks/{webpack-de742b64187e13fe.js → webpack-9a4db269f9ed63ad.js} +1 -1
  158. package/dist/web/standalone/.next/static/css/746ee28c929d1880.css +1 -0
  159. package/package.json +6 -5
  160. package/packages/contracts/dist/rpc.test.js +7 -0
  161. package/packages/contracts/dist/rpc.test.js.map +1 -1
  162. package/packages/contracts/dist/workflow.d.ts +21 -0
  163. package/packages/contracts/dist/workflow.d.ts.map +1 -1
  164. package/packages/contracts/dist/workflow.js +24 -0
  165. package/packages/contracts/dist/workflow.js.map +1 -1
  166. package/packages/contracts/src/rpc.test.ts +8 -0
  167. package/packages/contracts/src/workflow.ts +24 -0
  168. package/packages/daemon/package.json +2 -2
  169. package/packages/mcp-server/README.md +14 -3
  170. package/packages/mcp-server/dist/workflow-tools.d.ts +0 -3
  171. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  172. package/packages/mcp-server/dist/workflow-tools.js +80 -0
  173. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  174. package/packages/mcp-server/package.json +2 -2
  175. package/packages/mcp-server/src/workflow-tools-parity.test.ts +244 -0
  176. package/packages/mcp-server/src/workflow-tools.test.ts +23 -1
  177. package/packages/mcp-server/src/workflow-tools.ts +168 -0
  178. package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
  179. package/packages/native/package.json +1 -1
  180. package/packages/native/tsconfig.json +2 -1
  181. package/packages/native/tsconfig.tsbuildinfo +1 -1
  182. package/packages/pi-agent-core/package.json +1 -1
  183. package/packages/pi-agent-core/tsconfig.tsbuildinfo +1 -1
  184. package/packages/pi-ai/dist/index.d.ts +2 -2
  185. package/packages/pi-ai/dist/index.d.ts.map +1 -1
  186. package/packages/pi-ai/dist/index.js +1 -1
  187. package/packages/pi-ai/dist/index.js.map +1 -1
  188. package/packages/pi-ai/dist/providers/openai-codex-responses.d.ts.map +1 -1
  189. package/packages/pi-ai/dist/providers/openai-codex-responses.js +82 -1
  190. package/packages/pi-ai/dist/providers/openai-codex-responses.js.map +1 -1
  191. package/packages/pi-ai/dist/providers/openai-codex-responses.test.d.ts +2 -0
  192. package/packages/pi-ai/dist/providers/openai-codex-responses.test.d.ts.map +1 -0
  193. package/packages/pi-ai/dist/providers/openai-codex-responses.test.js +52 -0
  194. package/packages/pi-ai/dist/providers/openai-codex-responses.test.js.map +1 -0
  195. package/packages/pi-ai/dist/providers/simple-options.d.ts +2 -4
  196. package/packages/pi-ai/dist/providers/simple-options.d.ts.map +1 -1
  197. package/packages/pi-ai/dist/providers/simple-options.js +5 -6
  198. package/packages/pi-ai/dist/providers/simple-options.js.map +1 -1
  199. package/packages/pi-ai/dist/providers/simple-options.test.d.ts +2 -0
  200. package/packages/pi-ai/dist/providers/simple-options.test.d.ts.map +1 -0
  201. package/packages/pi-ai/dist/providers/simple-options.test.js +50 -0
  202. package/packages/pi-ai/dist/providers/simple-options.test.js.map +1 -0
  203. package/packages/pi-ai/dist/providers/transform-messages.d.ts +11 -0
  204. package/packages/pi-ai/dist/providers/transform-messages.d.ts.map +1 -1
  205. package/packages/pi-ai/dist/providers/transform-messages.js +20 -0
  206. package/packages/pi-ai/dist/providers/transform-messages.js.map +1 -1
  207. package/packages/pi-ai/package.json +1 -1
  208. package/packages/pi-ai/src/index.ts +7 -2
  209. package/packages/pi-ai/src/providers/openai-codex-responses.test.ts +63 -0
  210. package/packages/pi-ai/src/providers/openai-codex-responses.ts +91 -1
  211. package/packages/pi-ai/src/providers/simple-options.test.ts +60 -0
  212. package/packages/pi-ai/src/providers/simple-options.ts +5 -6
  213. package/packages/pi-ai/src/providers/transform-messages.ts +24 -0
  214. package/packages/pi-ai/tsconfig.tsbuildinfo +1 -1
  215. package/packages/pi-coding-agent/dist/core/agent-session-thinking-level.test.d.ts +2 -0
  216. package/packages/pi-coding-agent/dist/core/agent-session-thinking-level.test.d.ts.map +1 -0
  217. package/packages/pi-coding-agent/dist/core/agent-session-thinking-level.test.js +66 -0
  218. package/packages/pi-coding-agent/dist/core/agent-session-thinking-level.test.js.map +1 -0
  219. package/packages/pi-coding-agent/dist/core/agent-session.js +1 -1
  220. package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
  221. package/packages/pi-coding-agent/dist/core/system-prompt.js +4 -4
  222. package/packages/pi-coding-agent/dist/core/system-prompt.js.map +1 -1
  223. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.d.ts.map +1 -1
  224. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js +24 -6
  225. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js.map +1 -1
  226. package/packages/pi-coding-agent/dist/tests/system-prompt-file-safety.test.d.ts +2 -0
  227. package/packages/pi-coding-agent/dist/tests/system-prompt-file-safety.test.d.ts.map +1 -0
  228. package/packages/pi-coding-agent/dist/tests/system-prompt-file-safety.test.js +17 -0
  229. package/packages/pi-coding-agent/dist/tests/system-prompt-file-safety.test.js.map +1 -0
  230. package/packages/pi-coding-agent/package.json +1 -1
  231. package/packages/pi-coding-agent/src/core/agent-session-thinking-level.test.ts +79 -0
  232. package/packages/pi-coding-agent/src/core/agent-session.ts +1 -1
  233. package/packages/pi-coding-agent/src/core/system-prompt.ts +4 -4
  234. package/packages/pi-coding-agent/src/modes/interactive/components/footer.ts +23 -7
  235. package/packages/pi-coding-agent/src/tests/system-prompt-file-safety.test.ts +22 -0
  236. package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
  237. package/packages/pi-tui/dist/tui.d.ts.map +1 -1
  238. package/packages/pi-tui/dist/tui.js +5 -0
  239. package/packages/pi-tui/dist/tui.js.map +1 -1
  240. package/packages/pi-tui/package.json +1 -1
  241. package/packages/pi-tui/src/tui.ts +6 -0
  242. package/packages/pi-tui/tsconfig.tsbuildinfo +1 -1
  243. package/packages/rpc-client/package.json +1 -1
  244. package/packages/rpc-client/tsconfig.tsbuildinfo +1 -1
  245. package/pkg/package.json +1 -1
  246. package/src/resources/GSD-WORKFLOW.md +10 -1
  247. package/src/resources/extensions/browser-tools/tools/screenshot.ts +1 -0
  248. package/src/resources/extensions/browser-tools/tools/zoom.ts +1 -0
  249. package/src/resources/extensions/claude-code-cli/partial-builder.ts +2 -1
  250. package/src/resources/extensions/claude-code-cli/tests/partial-builder.test.ts +19 -2
  251. package/src/resources/extensions/cmux/index.ts +6 -0
  252. package/src/resources/extensions/gsd/auto/contracts.ts +59 -16
  253. package/src/resources/extensions/gsd/auto/infra-errors.ts +9 -3
  254. package/src/resources/extensions/gsd/auto/loop.ts +8 -5
  255. package/src/resources/extensions/gsd/auto/orchestrator.ts +129 -6
  256. package/src/resources/extensions/gsd/auto/phases.ts +36 -1
  257. package/src/resources/extensions/gsd/auto/workflow-memory-pressure.ts +13 -0
  258. package/src/resources/extensions/gsd/auto-dashboard.ts +72 -1
  259. package/src/resources/extensions/gsd/auto-dispatch.ts +14 -6
  260. package/src/resources/extensions/gsd/auto-model-selection.ts +2 -1
  261. package/src/resources/extensions/gsd/auto-post-unit.ts +266 -139
  262. package/src/resources/extensions/gsd/auto-prompts.ts +13 -5
  263. package/src/resources/extensions/gsd/auto-recovery.ts +29 -0
  264. package/src/resources/extensions/gsd/auto-start.ts +92 -9
  265. package/src/resources/extensions/gsd/auto-verification.ts +36 -34
  266. package/src/resources/extensions/gsd/auto-worktree.ts +119 -1
  267. package/src/resources/extensions/gsd/auto.ts +167 -53
  268. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +33 -6
  269. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +9 -8
  270. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +10 -2
  271. package/src/resources/extensions/gsd/bootstrap/subagent-input.ts +19 -7
  272. package/src/resources/extensions/gsd/bootstrap/system-context.ts +58 -15
  273. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +19 -3
  274. package/src/resources/extensions/gsd/clean-root-preflight.ts +174 -8
  275. package/src/resources/extensions/gsd/commands/catalog.ts +4 -1
  276. package/src/resources/extensions/gsd/commands/handlers/core.ts +41 -1
  277. package/src/resources/extensions/gsd/commands-bootstrap.ts +10 -0
  278. package/src/resources/extensions/gsd/commands-handlers.ts +19 -2
  279. package/src/resources/extensions/gsd/context-store.ts +120 -1
  280. package/src/resources/extensions/gsd/crash-recovery.ts +30 -4
  281. package/src/resources/extensions/gsd/db/unit-dispatches.ts +4 -3
  282. package/src/resources/extensions/gsd/db-writer.ts +167 -84
  283. package/src/resources/extensions/gsd/dispatch-guard.ts +2 -2
  284. package/src/resources/extensions/gsd/docs/preferences-reference.md +1 -1
  285. package/src/resources/extensions/gsd/doctor-git-checks.ts +44 -6
  286. package/src/resources/extensions/gsd/doctor-runtime-checks.ts +25 -13
  287. package/src/resources/extensions/gsd/doctor-types.ts +2 -0
  288. package/src/resources/extensions/gsd/doctor.ts +2 -27
  289. package/src/resources/extensions/gsd/export-html.ts +27 -427
  290. package/src/resources/extensions/gsd/git-service.ts +45 -1
  291. package/src/resources/extensions/gsd/gsd-db.ts +3 -0
  292. package/src/resources/extensions/gsd/guided-flow.ts +126 -128
  293. package/src/resources/extensions/gsd/guided-unit-context.ts +30 -0
  294. package/src/resources/extensions/gsd/knowledge-backfill.ts +164 -0
  295. package/src/resources/extensions/gsd/knowledge-capture.ts +160 -0
  296. package/src/resources/extensions/gsd/knowledge-parser.ts +174 -0
  297. package/src/resources/extensions/gsd/knowledge-projection.ts +241 -0
  298. package/src/resources/extensions/gsd/markdown-renderer.ts +6 -1
  299. package/src/resources/extensions/gsd/md-importer.ts +1 -1
  300. package/src/resources/extensions/gsd/memory-backfill.ts +89 -17
  301. package/src/resources/extensions/gsd/memory-consolidation-scanner.ts +277 -0
  302. package/src/resources/extensions/gsd/migrate/command.ts +5 -0
  303. package/src/resources/extensions/gsd/migrate/parsers.ts +11 -0
  304. package/src/resources/extensions/gsd/migrate/preview.ts +10 -0
  305. package/src/resources/extensions/gsd/migrate/transformer.ts +58 -4
  306. package/src/resources/extensions/gsd/migrate/writer.ts +14 -1
  307. package/src/resources/extensions/gsd/migration-auto-check.ts +15 -23
  308. package/src/resources/extensions/gsd/milestone-actions.ts +10 -4
  309. package/src/resources/extensions/gsd/native-git-bridge.ts +54 -12
  310. package/src/resources/extensions/gsd/pending-auto-start.ts +79 -0
  311. package/src/resources/extensions/gsd/post-execution-checks.ts +87 -2
  312. package/src/resources/extensions/gsd/pre-execution-checks.ts +32 -1
  313. package/src/resources/extensions/gsd/prompt-loader.ts +1 -1
  314. package/src/resources/extensions/gsd/prompts/complete-milestone.md +1 -1
  315. package/src/resources/extensions/gsd/prompts/complete-slice.md +1 -1
  316. package/src/resources/extensions/gsd/prompts/discuss-headless.md +8 -8
  317. package/src/resources/extensions/gsd/prompts/discuss.md +9 -9
  318. package/src/resources/extensions/gsd/prompts/guided-discuss-project.md +4 -4
  319. package/src/resources/extensions/gsd/prompts/guided-discuss-requirements.md +3 -3
  320. package/src/resources/extensions/gsd/prompts/plan-slice.md +4 -4
  321. package/src/resources/extensions/gsd/prompts/queue.md +4 -4
  322. package/src/resources/extensions/gsd/prompts/refine-slice.md +2 -2
  323. package/src/resources/extensions/gsd/prompts/rewrite-docs.md +1 -1
  324. package/src/resources/extensions/gsd/prompts/system.md +2 -2
  325. package/src/resources/extensions/gsd/provider-switch-observer.ts +185 -0
  326. package/src/resources/extensions/gsd/smart-entry-routing.ts +77 -0
  327. package/src/resources/extensions/gsd/state-reconciliation/drift/merge-state.ts +8 -1
  328. package/src/resources/extensions/gsd/state-reconciliation/drift/project-md.ts +12 -15
  329. package/src/resources/extensions/gsd/state-reconciliation/drift/roadmap.ts +17 -25
  330. package/src/resources/extensions/gsd/status-guards.ts +5 -0
  331. package/src/resources/extensions/gsd/templates/knowledge.md +2 -2
  332. package/src/resources/extensions/gsd/templates/plan.md +8 -5
  333. package/src/resources/extensions/gsd/templates/task-plan.md +4 -2
  334. package/src/resources/extensions/gsd/tests/auto-dashboard.test.ts +71 -0
  335. package/src/resources/extensions/gsd/tests/auto-deterministic-error-classification-4973.test.ts +116 -0
  336. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +129 -0
  337. package/src/resources/extensions/gsd/tests/auto-orchestrator.test.ts +487 -4
  338. package/src/resources/extensions/gsd/tests/auto-paused-ui-cleanup.test.ts +12 -11
  339. package/src/resources/extensions/gsd/tests/auto-phases-lifecycle.test.ts +53 -2
  340. package/src/resources/extensions/gsd/tests/auto-post-unit-step-message.test.ts +12 -1
  341. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +15 -1
  342. package/src/resources/extensions/gsd/tests/auto-runtime-state.test.ts +4 -4
  343. package/src/resources/extensions/gsd/tests/auto-start-orphan-bootstrap.test.ts +1 -0
  344. package/src/resources/extensions/gsd/tests/auto-worktree-registry.test.ts +69 -1
  345. package/src/resources/extensions/gsd/tests/brief-command.test.ts +89 -0
  346. package/src/resources/extensions/gsd/tests/browser-tools-compatibility-declarations.test.ts +62 -0
  347. package/src/resources/extensions/gsd/tests/clean-root-preflight.test.ts +107 -2
  348. package/src/resources/extensions/gsd/tests/clear-stale-autostart.test.ts +11 -2
  349. package/src/resources/extensions/gsd/tests/closeout-git-deferral.test.ts +16 -0
  350. package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +4 -1
  351. package/src/resources/extensions/gsd/tests/complete-slice.test.ts +5 -9
  352. package/src/resources/extensions/gsd/tests/complete-task.test.ts +3 -1
  353. package/src/resources/extensions/gsd/tests/context-store-decisions-from-memories.test.ts +312 -0
  354. package/src/resources/extensions/gsd/tests/crash-recovery-via-db.test.ts +43 -2
  355. package/src/resources/extensions/gsd/tests/db-authority-regression.test.ts +208 -0
  356. package/src/resources/extensions/gsd/tests/db-writer.test.ts +13 -8
  357. package/src/resources/extensions/gsd/tests/decisions-projection-from-memories.test.ts +453 -0
  358. package/src/resources/extensions/gsd/tests/decisions-stop-table-writes.test.ts +348 -0
  359. package/src/resources/extensions/gsd/tests/deep-project-auto-loop.test.ts +59 -2
  360. package/src/resources/extensions/gsd/tests/dispatch-complete-milestone-guard.test.ts +39 -0
  361. package/src/resources/extensions/gsd/tests/dispatch-guard.test.ts +27 -0
  362. package/src/resources/extensions/gsd/tests/evidence-cross-ref.test.ts +38 -0
  363. package/src/resources/extensions/gsd/tests/export-html-enhancements.test.ts +8 -0
  364. package/src/resources/extensions/gsd/tests/freeform-decisions.test.ts +8 -4
  365. package/src/resources/extensions/gsd/tests/gsd-tools.test.ts +11 -7
  366. package/src/resources/extensions/gsd/tests/guided-discuss-project-prompt-rendering.test.ts +2 -0
  367. package/src/resources/extensions/gsd/tests/guided-dispatch-root.test.ts +106 -0
  368. package/src/resources/extensions/gsd/tests/guided-flow-session-isolation.test.ts +59 -11
  369. package/src/resources/extensions/gsd/tests/guided-flow.test.ts +21 -0
  370. package/src/resources/extensions/gsd/tests/guided-tool-contract.test.ts +65 -0
  371. package/src/resources/extensions/gsd/tests/headless-milestone-parity.test.ts +7 -7
  372. package/src/resources/extensions/gsd/tests/hook-model-resolution.test.ts +5 -0
  373. package/src/resources/extensions/gsd/tests/infra-error.test.ts +2 -2
  374. package/src/resources/extensions/gsd/tests/infra-errors-cooldown.test.ts +9 -0
  375. package/src/resources/extensions/gsd/tests/integration/doctor-git.test.ts +44 -0
  376. package/src/resources/extensions/gsd/tests/integration/doctor-runtime.test.ts +20 -0
  377. package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +103 -1
  378. package/src/resources/extensions/gsd/tests/integration/integration-lifecycle.test.ts +13 -5
  379. package/src/resources/extensions/gsd/tests/integration/migrate-command.test.ts +48 -3
  380. package/src/resources/extensions/gsd/tests/integration/state-machine-runtime-failures.test.ts +6 -1
  381. package/src/resources/extensions/gsd/tests/knowledge-backfill-projection.test.ts +323 -0
  382. package/src/resources/extensions/gsd/tests/knowledge-capture.test.ts +242 -0
  383. package/src/resources/extensions/gsd/tests/knowledge.test.ts +47 -2
  384. package/src/resources/extensions/gsd/tests/load-knowledge-block-rules-only.test.ts +209 -0
  385. package/src/resources/extensions/gsd/tests/memory-consolidation-scanner.test.ts +316 -0
  386. package/src/resources/extensions/gsd/tests/migrate-transformer.test.ts +5 -1
  387. package/src/resources/extensions/gsd/tests/migrate-validator-parsers.test.ts +24 -1
  388. package/src/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +6 -1
  389. package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +26 -18
  390. package/src/resources/extensions/gsd/tests/native-git-bridge-exec-fallback.test.ts +63 -2
  391. package/src/resources/extensions/gsd/tests/orphaned-worktree-audit.test.ts +121 -1
  392. package/src/resources/extensions/gsd/tests/park-db-sync.test.ts +55 -1
  393. package/src/resources/extensions/gsd/tests/pending-autostart-scope.test.ts +29 -5
  394. package/src/resources/extensions/gsd/tests/plan-milestone-sketch-render.test.ts +157 -0
  395. package/src/resources/extensions/gsd/tests/plan-milestone.test.ts +26 -0
  396. package/src/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +2 -0
  397. package/src/resources/extensions/gsd/tests/plan-slice.test.ts +225 -1
  398. package/src/resources/extensions/gsd/tests/plan-task.test.ts +17 -0
  399. package/src/resources/extensions/gsd/tests/post-exec-retry-bypass.test.ts +79 -1
  400. package/src/resources/extensions/gsd/tests/post-execution-checks.test.ts +86 -0
  401. package/src/resources/extensions/gsd/tests/post-unit-git-failure.test.ts +1 -1
  402. package/src/resources/extensions/gsd/tests/pre-execution-checks.test.ts +53 -0
  403. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +8 -0
  404. package/src/resources/extensions/gsd/tests/prompt-loader.test.ts +23 -0
  405. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +20 -1
  406. package/src/resources/extensions/gsd/tests/provider-switch-observer.test.ts +252 -0
  407. package/src/resources/extensions/gsd/tests/remediation-completion-guard.test.ts +46 -2
  408. package/src/resources/extensions/gsd/tests/session-start-footer.test.ts +16 -4
  409. package/src/resources/extensions/gsd/tests/session-switch-abort-misclassification.test.ts +10 -0
  410. package/src/resources/extensions/gsd/tests/smart-entry-routing.test.ts +113 -0
  411. package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +53 -2
  412. package/src/resources/extensions/gsd/tests/state-corruption-2945.test.ts +6 -0
  413. package/src/resources/extensions/gsd/tests/state-reconciliation-drift.test.ts +119 -23
  414. package/src/resources/extensions/gsd/tests/stuck-state-via-db.test.ts +64 -1
  415. package/src/resources/extensions/gsd/tests/summary-render-parity.test.ts +7 -3
  416. package/src/resources/extensions/gsd/tests/unit-context-manifest.test.ts +86 -7
  417. package/src/resources/extensions/gsd/tests/verification-gate.test.ts +110 -1
  418. package/src/resources/extensions/gsd/tests/verification-verdict.test.ts +78 -0
  419. package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +1 -1
  420. package/src/resources/extensions/gsd/tests/workflow-memory-pressure.test.ts +21 -1
  421. package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +1 -1
  422. package/src/resources/extensions/gsd/tests/worktree-git-pathspec.test.ts +39 -0
  423. package/src/resources/extensions/gsd/tests/worktree-journal-events.test.ts +64 -12
  424. package/src/resources/extensions/gsd/tests/worktree-lifecycle.test.ts +25 -0
  425. package/src/resources/extensions/gsd/tests/write-gate-planning-unit.test.ts +54 -0
  426. package/src/resources/extensions/gsd/tools/complete-milestone.ts +8 -10
  427. package/src/resources/extensions/gsd/tools/complete-slice.ts +6 -8
  428. package/src/resources/extensions/gsd/tools/plan-milestone.ts +5 -1
  429. package/src/resources/extensions/gsd/tools/plan-slice.ts +98 -12
  430. package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +135 -0
  431. package/src/resources/extensions/gsd/types.ts +1 -1
  432. package/src/resources/extensions/gsd/unit-context-manifest.ts +47 -11
  433. package/src/resources/extensions/gsd/validation.ts +23 -1
  434. package/src/resources/extensions/gsd/verification-gate.ts +78 -6
  435. package/src/resources/extensions/gsd/verification-verdict.ts +47 -0
  436. package/src/resources/extensions/gsd/workflow-logger.ts +4 -0
  437. package/src/resources/extensions/gsd/workflow-projections.ts +6 -8
  438. package/src/resources/extensions/gsd/worktree-lifecycle.ts +61 -10
  439. package/src/resources/extensions/shared/html-shell.ts +412 -0
  440. package/src/resources/extensions/subagent/index.ts +567 -103
  441. package/src/resources/extensions/subagent/launch.ts +131 -0
  442. package/src/resources/extensions/subagent/run-store.ts +218 -0
  443. package/src/resources/extensions/subagent/tests/launch.test.ts +115 -0
  444. package/src/resources/extensions/subagent/tests/run-store.test.ts +111 -0
  445. package/src/resources/extensions/visual-brief/artifact-policy.ts +41 -0
  446. package/src/resources/extensions/visual-brief/extension-manifest.json +8 -0
  447. package/src/resources/extensions/visual-brief/index.ts +8 -0
  448. package/src/resources/extensions/visual-brief/page-contract.ts +136 -0
  449. package/src/resources/extensions/visual-brief/prompts.ts +183 -0
  450. package/src/resources/extensions/visual-brief/tests/visual-brief.test.ts +212 -0
  451. package/dist/web/standalone/.next/server/chunks/5822.js +0 -2
  452. package/dist/web/standalone/.next/static/chunks/2556.0527fea66e123b7f.js +0 -1
  453. package/dist/web/standalone/.next/static/chunks/app/layout-a16c7a7ecdf0c2cf.js +0 -1
  454. package/dist/web/standalone/.next/static/css/54ec2745c1da488b.css +0 -1
  455. package/dist/web/standalone/.next/static/css/de70bee13400563f.css +0 -1
  456. package/dist/web/standalone/.next/static/media/4cf2300e9c8272f7-s.p.woff2 +0 -0
  457. package/dist/web/standalone/.next/static/media/747892c23ea88013-s.woff2 +0 -0
  458. package/dist/web/standalone/.next/static/media/8d697b304b401681-s.woff2 +0 -0
  459. package/dist/web/standalone/.next/static/media/93f479601ee12b01-s.p.woff2 +0 -0
  460. package/dist/web/standalone/.next/static/media/9610d9e46709d722-s.woff2 +0 -0
  461. package/dist/web/standalone/.next/static/media/ba015fad6dcf6784-s.woff2 +0 -0
  462. /package/dist/web/standalone/.next/static/{rIkMv4YSNlfSeqmGqWVns → xRy4LqKSNKdT7y6ATYyEl}/_buildManifest.js +0 -0
  463. /package/dist/web/standalone/.next/static/{rIkMv4YSNlfSeqmGqWVns → xRy4LqKSNKdT7y6ATYyEl}/_ssgManifest.js +0 -0
@@ -4,9 +4,14 @@
4
4
  import test from "node:test";
5
5
  import assert from "node:assert/strict";
6
6
 
7
- import { createAutoOrchestrator } from "../auto/orchestrator.js";
7
+ import { createAutoOrchestrator, STUCK_WINDOW_SIZE } from "../auto/orchestrator.js";
8
8
  import type { AutoOrchestratorDeps } from "../auto/contracts.js";
9
9
  import type { GSDState } from "../types.js";
10
+ import { createWiredDispatchAdapter } from "../auto.js";
11
+ import { resolveDispatch, type DispatchContext } from "../auto-dispatch.js";
12
+ import { RuleRegistry, setRegistry, resetRegistry } from "../rule-registry.js";
13
+ import type { UnifiedRule } from "../rule-types.js";
14
+ import { supportsStructuredQuestions } from "../workflow-mcp.js";
10
15
 
11
16
  function makeState(): GSDState {
12
17
  return {
@@ -62,9 +67,13 @@ function makeDeps(overrides: Partial<AutoOrchestratorDeps> = {}): { deps: AutoOr
62
67
  async cleanupOnStop() { calls.push("worktree.cleanup"); },
63
68
  },
64
69
  health: {
70
+ checkResourcesStale() {
71
+ calls.push("health.stale");
72
+ return null;
73
+ },
65
74
  async preAdvanceGate() {
66
75
  calls.push("health.pre");
67
- return { allow: true };
76
+ return { kind: "pass" };
68
77
  },
69
78
  async postAdvanceRecord() { calls.push("health.post"); },
70
79
  },
@@ -75,6 +84,9 @@ function makeDeps(overrides: Partial<AutoOrchestratorDeps> = {}): { deps: AutoOr
75
84
  notifications: {
76
85
  async notifyLifecycle(event) { calls.push(`notify:${event.name}`); },
77
86
  },
87
+ uokGate: {
88
+ async emit(input) { calls.push(`gate:${input.gateId}:${input.outcome}`); },
89
+ },
78
90
  };
79
91
 
80
92
  return { deps: { ...deps, ...overrides }, calls };
@@ -87,6 +99,7 @@ test("start() advances and records active unit", async () => {
87
99
  const result = await orchestrator.start({ basePath: "/tmp/project", trigger: "manual" });
88
100
 
89
101
  assert.equal(result.kind, "advanced");
102
+ assert.deepEqual(result.unit, { unitType: "execute-task", unitId: "T01" });
90
103
  const status = orchestrator.getStatus();
91
104
  assert.equal(status.phase, "running");
92
105
  assert.deepEqual(status.activeUnit, { unitType: "execute-task", unitId: "T01" });
@@ -95,9 +108,10 @@ test("start() advances and records active unit", async () => {
95
108
  });
96
109
 
97
110
  test("advance() returns blocked when health gate denies", async () => {
98
- const { deps } = makeDeps({
111
+ const { deps, calls } = makeDeps({
99
112
  health: {
100
- async preAdvanceGate() { return { allow: false, reason: "doctor-block" }; },
113
+ checkResourcesStale: () => null,
114
+ async preAdvanceGate() { return { kind: "fail", reason: "doctor-block" }; },
101
115
  async postAdvanceRecord() {},
102
116
  },
103
117
  });
@@ -107,6 +121,69 @@ test("advance() returns blocked when health gate denies", async () => {
107
121
 
108
122
  assert.equal(result.kind, "blocked");
109
123
  assert.equal(result.reason, "doctor-block");
124
+ assert.equal(result.action, "pause");
125
+ assert.ok(calls.includes("gate:pre-dispatch-health-gate:manual-attention"));
126
+ });
127
+
128
+ test("advance() returns blocked stop when resources are stale", async () => {
129
+ const { deps, calls } = makeDeps({
130
+ health: {
131
+ checkResourcesStale: () => "resources changed since session start",
132
+ async preAdvanceGate() { return { kind: "pass" }; },
133
+ async postAdvanceRecord() {},
134
+ },
135
+ });
136
+ const orchestrator = createAutoOrchestrator(deps);
137
+
138
+ const result = await orchestrator.advance();
139
+
140
+ assert.equal(result.kind, "blocked");
141
+ assert.equal(result.reason, "resources changed since session start");
142
+ assert.equal(result.action, "stop");
143
+ assert.ok(calls.includes("gate:resource-version-guard:fail"));
144
+ assert.ok(!calls.includes("health.pre"));
145
+ assert.ok(!calls.includes("state.reconcile"));
146
+ });
147
+
148
+ test("advance() continues past pre-dispatch health gate when it throws", async () => {
149
+ const { deps, calls } = makeDeps({
150
+ health: {
151
+ checkResourcesStale: () => null,
152
+ async preAdvanceGate() { return { kind: "threw", error: new Error("boom") }; },
153
+ async postAdvanceRecord() {},
154
+ },
155
+ });
156
+ const orchestrator = createAutoOrchestrator(deps);
157
+
158
+ const result = await orchestrator.advance();
159
+
160
+ assert.equal(result.kind, "advanced");
161
+ assert.ok(calls.includes("gate:pre-dispatch-health-gate:manual-attention"));
162
+ assert.ok(calls.includes("state.reconcile"));
163
+ assert.ok(calls.includes("dispatch.decide"));
164
+ });
165
+
166
+ test("advance() forwards fixesApplied into pre-dispatch-health-gate pass findings", async () => {
167
+ let observed = "";
168
+ const { deps } = makeDeps({
169
+ health: {
170
+ checkResourcesStale: () => null,
171
+ async preAdvanceGate() { return { kind: "pass", fixesApplied: ["fix-a", "fix-b"] }; },
172
+ async postAdvanceRecord() {},
173
+ },
174
+ uokGate: {
175
+ async emit(input) {
176
+ if (input.gateId === "pre-dispatch-health-gate" && input.outcome === "pass") {
177
+ observed = input.findings ?? "";
178
+ }
179
+ },
180
+ },
181
+ });
182
+ const orchestrator = createAutoOrchestrator(deps);
183
+
184
+ await orchestrator.advance();
185
+
186
+ assert.equal(observed, "fix-a, fix-b");
110
187
  });
111
188
 
112
189
  test("advance() follows the ADR-015 invariant sequence before journaling advance", async () => {
@@ -116,9 +193,13 @@ test("advance() follows the ADR-015 invariant sequence before journaling advance
116
193
  const result = await orchestrator.advance();
117
194
 
118
195
  assert.equal(result.kind, "advanced");
196
+ assert.deepEqual(result.unit, { unitType: "execute-task", unitId: "T01" });
119
197
  assert.deepEqual(calls, [
120
198
  "runtime.lock",
199
+ "health.stale",
200
+ "gate:resource-version-guard:pass",
121
201
  "health.pre",
202
+ "gate:pre-dispatch-health-gate:pass",
122
203
  "state.reconcile",
123
204
  "dispatch.decide",
124
205
  "tool.compile",
@@ -144,6 +225,7 @@ test("advance() blocks before dispatch when State Reconciliation blocks", async
144
225
 
145
226
  assert.equal(result.kind, "blocked");
146
227
  assert.equal(result.reason, "state drift blocked");
228
+ assert.equal(result.action, "pause");
147
229
  assert.ok(!calls.includes("dispatch.decide"));
148
230
  assert.ok(calls.includes("journal:advance-blocked"));
149
231
  });
@@ -163,6 +245,7 @@ test("advance() blocks before Runtime persistence when Tool Contract fails", asy
163
245
 
164
246
  assert.equal(result.kind, "blocked");
165
247
  assert.equal(result.reason, "unknown Unit");
248
+ assert.equal(result.action, "pause");
166
249
  assert.ok(!calls.includes("worktree.prepare"));
167
250
  assert.ok(!calls.includes("journal:advance"));
168
251
  assert.ok(calls.includes("journal:advance-blocked"));
@@ -185,6 +268,7 @@ test("advance() blocks before Runtime persistence when Worktree Safety fails", a
185
268
 
186
269
  assert.equal(result.kind, "blocked");
187
270
  assert.equal(result.reason, "worktree invalid");
271
+ assert.equal(result.action, "pause");
188
272
  assert.ok(!calls.includes("journal:advance"));
189
273
  assert.ok(!calls.includes("worktree.sync"));
190
274
  assert.ok(calls.includes("journal:advance-blocked"));
@@ -204,6 +288,51 @@ test("advance() stops when dispatch has no next unit", async () => {
204
288
  assert.equal(orchestrator.getStatus().phase, "stopped");
205
289
  });
206
290
 
291
+ test("advance() surfaces dispatch blocker reason instead of generic no remaining units", async () => {
292
+ const { deps, calls } = makeDeps({
293
+ dispatch: {
294
+ async decideNextUnit() {
295
+ return {
296
+ kind: "blocked",
297
+ reason: "Milestone M001 validation verdict is needs-remediation but all slices are complete.",
298
+ action: "pause",
299
+ };
300
+ },
301
+ },
302
+ });
303
+ const orchestrator = createAutoOrchestrator(deps);
304
+
305
+ const result = await orchestrator.advance();
306
+
307
+ assert.equal(result.kind, "blocked");
308
+ if (result.kind !== "blocked") return;
309
+ assert.equal(result.reason, "Milestone M001 validation verdict is needs-remediation but all slices are complete.");
310
+ assert.equal(result.action, "pause");
311
+ assert.ok(calls.includes("journal:advance-blocked"));
312
+ assert.ok(!calls.includes("journal:advance-stopped"));
313
+ });
314
+
315
+ test("resume() returns blocked when advance detects a dispatch blocker", async () => {
316
+ const { deps } = makeDeps({
317
+ dispatch: {
318
+ async decideNextUnit() {
319
+ return {
320
+ kind: "blocked",
321
+ reason: "remediation required",
322
+ action: "pause",
323
+ };
324
+ },
325
+ },
326
+ });
327
+ const orchestrator = createAutoOrchestrator(deps);
328
+
329
+ const result = await orchestrator.resume();
330
+
331
+ assert.equal(result.kind, "blocked");
332
+ if (result.kind !== "blocked") return;
333
+ assert.equal(result.reason, "remediation required");
334
+ });
335
+
207
336
  test("advance() uses recovery on error", async () => {
208
337
  const { deps, calls } = makeDeps({
209
338
  runtime: {
@@ -232,8 +361,10 @@ test("advance() is idempotent for the same active unit", async () => {
232
361
  const second = await orchestrator.advance();
233
362
 
234
363
  assert.equal(first.kind, "advanced");
364
+ assert.deepEqual(first.unit, { unitType: "execute-task", unitId: "T01" });
235
365
  assert.equal(second.kind, "blocked");
236
366
  assert.equal(second.reason, "idempotent advance: unit already active");
367
+ assert.equal(second.action, "stop");
237
368
 
238
369
  const prepareCalls = calls.filter((c) => c === "worktree.prepare").length;
239
370
  assert.equal(prepareCalls, 1);
@@ -468,3 +599,355 @@ test("stop() cleans up worktree and transitions to stopped", async () => {
468
599
  assert.ok(calls.includes("journal:stop"));
469
600
  assert.ok(calls.includes("notify:stop"));
470
601
  });
602
+
603
+ // ────────────────────────────────────────────────────────────────────────
604
+ // Stuck-loop ring buffer (issue #5787)
605
+ // ────────────────────────────────────────────────────────────────────────
606
+
607
+ test("STUCK_WINDOW_SIZE matches the legacy auto/phases.ts constant", () => {
608
+ assert.equal(STUCK_WINDOW_SIZE, 6);
609
+ });
610
+
611
+ test("stuck-loop: empty ring on a freshly constructed orchestrator advances normally", async () => {
612
+ const { deps } = makeDeps();
613
+ const orchestrator = createAutoOrchestrator(deps);
614
+
615
+ const result = await orchestrator.advance();
616
+
617
+ assert.equal(result.kind, "advanced");
618
+ });
619
+
620
+ test("stuck-loop: partial fill of mixed units does not block", async () => {
621
+ // Alternate A/B for STUCK_WINDOW_SIZE rounds. No single key saturates the
622
+ // window, so neither idempotency nor stuck-loop should fire.
623
+ let i = 0;
624
+ const sequence = ["A", "B", "A", "B", "A", "B"];
625
+ const { deps } = makeDeps({
626
+ dispatch: {
627
+ async decideNextUnit() {
628
+ const id = sequence[i++ % sequence.length];
629
+ return { unitType: "execute-task", unitId: id, reason: "ready", preconditions: [] };
630
+ },
631
+ },
632
+ });
633
+ const orchestrator = createAutoOrchestrator(deps);
634
+
635
+ for (let round = 0; round < STUCK_WINDOW_SIZE; round++) {
636
+ const result = await orchestrator.advance();
637
+ assert.equal(result.kind, "advanced", `round ${round} should advance, got ${result.kind}`);
638
+ }
639
+ });
640
+
641
+ test("stuck-loop: ring saturated with same unit blocks with action 'stop' and stuck-loop reason", async () => {
642
+ // Dispatch picks the same unit every time. The first advance succeeds.
643
+ // Calls 2..STUCK_WINDOW_SIZE-1 are idempotency-blocked while the ring fills.
644
+ // The STUCK_WINDOW_SIZE'th call sees a saturated ring and returns stuck-loop.
645
+ const { deps } = makeDeps();
646
+ const orchestrator = createAutoOrchestrator(deps);
647
+
648
+ const results: Awaited<ReturnType<typeof orchestrator.advance>>[] = [];
649
+ for (let i = 0; i < STUCK_WINDOW_SIZE; i++) {
650
+ results.push(await orchestrator.advance());
651
+ }
652
+
653
+ // First call advances.
654
+ assert.equal(results[0].kind, "advanced");
655
+
656
+ // Intermediate calls are blocked by idempotency (not stuck-loop yet).
657
+ for (let i = 1; i < STUCK_WINDOW_SIZE - 1; i++) {
658
+ const r = results[i];
659
+ assert.equal(r.kind, "blocked", `round ${i} should be blocked`);
660
+ if (r.kind !== "blocked") return;
661
+ assert.equal(r.reason, "idempotent advance: unit already active");
662
+ assert.equal(r.action, "stop");
663
+ }
664
+
665
+ // The final call (ring now holds STUCK_WINDOW_SIZE copies) returns stuck-loop.
666
+ const last = results[STUCK_WINDOW_SIZE - 1];
667
+ assert.equal(last.kind, "blocked");
668
+ if (last.kind !== "blocked") return;
669
+ assert.equal(last.action, "stop");
670
+ assert.equal(last.reason, `stuck-loop: execute-task:T01 picked ${STUCK_WINDOW_SIZE} times`);
671
+ });
672
+
673
+ test("stuck-loop: idempotency block continues to fire with its own reason before saturation", async () => {
674
+ // Two identical calls should produce idempotent (not stuck-loop). Ensures the
675
+ // existing idempotency block is not absorbed by the new check.
676
+ const { deps } = makeDeps();
677
+ const orchestrator = createAutoOrchestrator(deps);
678
+
679
+ const first = await orchestrator.advance();
680
+ const second = await orchestrator.advance();
681
+
682
+ assert.equal(first.kind, "advanced");
683
+ assert.equal(second.kind, "blocked");
684
+ assert.equal(second.reason, "idempotent advance: unit already active");
685
+ assert.equal(second.action, "stop");
686
+ });
687
+
688
+ test("stuck-loop: start() resets the ring so a fresh saturation cycle is required", async () => {
689
+ // Fill the ring to one short of saturation, then start() — the ring should
690
+ // be cleared, and the next advance must succeed instead of going stuck.
691
+ const { deps } = makeDeps();
692
+ const orchestrator = createAutoOrchestrator(deps);
693
+
694
+ for (let i = 0; i < STUCK_WINDOW_SIZE - 1; i++) {
695
+ await orchestrator.advance();
696
+ }
697
+
698
+ const restarted = await orchestrator.start({ basePath: "/tmp/project", trigger: "manual" });
699
+ assert.equal(restarted.kind, "advanced");
700
+
701
+ // Immediately after start(), the next advance is idempotent (one element in
702
+ // ring), not stuck-loop, confirming the ring was reset.
703
+ const next = await orchestrator.advance();
704
+ assert.equal(next.kind, "blocked");
705
+ assert.equal(next.reason, "idempotent advance: unit already active");
706
+ });
707
+
708
+ test("stuck-loop: resume() resets the ring", async () => {
709
+ const { deps } = makeDeps();
710
+ const orchestrator = createAutoOrchestrator(deps);
711
+
712
+ for (let i = 0; i < STUCK_WINDOW_SIZE - 1; i++) {
713
+ await orchestrator.advance();
714
+ }
715
+
716
+ const resumed = await orchestrator.resume();
717
+ assert.equal(resumed.kind, "advanced");
718
+
719
+ const next = await orchestrator.advance();
720
+ assert.equal(next.kind, "blocked");
721
+ assert.equal(next.reason, "idempotent advance: unit already active");
722
+ });
723
+
724
+ test("stuck-loop: stop() resets the ring", async () => {
725
+ const { deps } = makeDeps();
726
+ const orchestrator = createAutoOrchestrator(deps);
727
+
728
+ for (let i = 0; i < STUCK_WINDOW_SIZE - 1; i++) {
729
+ await orchestrator.advance();
730
+ }
731
+
732
+ const stopped = await orchestrator.stop("user-request");
733
+ assert.equal(stopped.kind, "stopped");
734
+
735
+ // Ring is cleared by stop(). A subsequent advance is a fresh first-touch.
736
+ const next = await orchestrator.advance();
737
+ assert.equal(next.kind, "advanced");
738
+ });
739
+
740
+ test("stuck-loop: journal records the stuck-loop reason on advance-blocked", async () => {
741
+ const { deps, calls } = makeDeps();
742
+ const orchestrator = createAutoOrchestrator(deps);
743
+
744
+ for (let i = 0; i < STUCK_WINDOW_SIZE; i++) {
745
+ await orchestrator.advance();
746
+ }
747
+
748
+ assert.ok(calls.includes("journal:advance-blocked"));
749
+ });
750
+
751
+ // ─── #5789 parity: wired dispatch adapter mirrors runDispatch's resolveDispatch call ───
752
+
753
+ test("wired DispatchAdapter forwards session-derived dispatch inputs identically to runDispatch", async () => {
754
+ const stateSnapshot = makeState();
755
+
756
+ // Install a capturing registry so we observe the DispatchContext both code paths
757
+ // build, and force a deterministic dispatch action so the parity assertion is
758
+ // about *inputs*, not rule evaluation.
759
+ const captured: DispatchContext[] = [];
760
+ const captureRule: UnifiedRule = {
761
+ name: "test-capture",
762
+ when: "dispatch",
763
+ evaluation: "first-match",
764
+ where: async (ctx: DispatchContext) => {
765
+ captured.push(ctx);
766
+ return {
767
+ action: "dispatch" as const,
768
+ unitType: "execute-task",
769
+ unitId: "T01",
770
+ prompt: "parity-fixture",
771
+ };
772
+ },
773
+ then: (r: unknown) => r,
774
+ };
775
+ setRegistry(new RuleRegistry([captureRule]));
776
+
777
+ try {
778
+ // Mock ExtensionContext + ExtensionAPI with the surface the wired adapter touches.
779
+ const fakeModelRegistry = {
780
+ getAll: () => [],
781
+ getProviderAuthMode: (_provider: string) => "apiKey" as const,
782
+ };
783
+ const ctx = {
784
+ model: {
785
+ provider: "anthropic",
786
+ baseUrl: "https://api.anthropic.com",
787
+ contextWindow: 200_000,
788
+ },
789
+ modelRegistry: fakeModelRegistry,
790
+ } as any;
791
+ const pi = {
792
+ getActiveTools: () => ["read_file", "write_file"],
793
+ } as any;
794
+ const basePath = "/tmp/parity-fixture";
795
+
796
+ // Path A — wired adapter (what createWiredAutoOrchestrationModule uses).
797
+ const adapter = createWiredDispatchAdapter(ctx, pi, basePath);
798
+ const adapterResult = await adapter.decideNextUnit({ stateSnapshot });
799
+
800
+ // Path B — direct resolveDispatch call mirroring phases.ts:runDispatch.
801
+ // Inline the same derivations runDispatch uses so any drift here is a parity break.
802
+ const prefs = undefined; // loadEffectiveGSDPreferences returns null for /tmp/parity-fixture.
803
+ const provider = ctx.model?.provider;
804
+ const authMode = provider && typeof ctx.modelRegistry?.getProviderAuthMode === "function"
805
+ ? ctx.modelRegistry.getProviderAuthMode(provider)
806
+ : undefined;
807
+ const activeTools = typeof pi.getActiveTools === "function" ? pi.getActiveTools() : [];
808
+ const structuredQuestionsAvailable: "true" | "false" =
809
+ prefs !== undefined && (prefs as { planning_depth?: string }).planning_depth === "deep"
810
+ ? "false"
811
+ : supportsStructuredQuestions(activeTools, {
812
+ authMode,
813
+ baseUrl: ctx.model?.baseUrl,
814
+ })
815
+ ? "true"
816
+ : "false";
817
+
818
+ const builtDirectCtx: DispatchContext = {
819
+ basePath,
820
+ mid: stateSnapshot.activeMilestone!.id,
821
+ midTitle: stateSnapshot.activeMilestone!.title,
822
+ state: stateSnapshot,
823
+ prefs,
824
+ structuredQuestionsAvailable,
825
+ sessionContextWindow: ctx.model?.contextWindow,
826
+ sessionProvider: ctx.model?.provider,
827
+ modelRegistry: ctx.modelRegistry,
828
+ };
829
+ const directAction = await resolveDispatch(builtDirectCtx);
830
+
831
+ // Two contexts captured: one per resolveDispatch call.
832
+ assert.equal(captured.length, 2, "expected two captured dispatch contexts");
833
+ const [adapterCtx, directCtx] = captured;
834
+
835
+ // Parity assertion: session-derived fields are identical.
836
+ assert.equal(adapterCtx.structuredQuestionsAvailable, directCtx.structuredQuestionsAvailable);
837
+ assert.equal(adapterCtx.sessionContextWindow, directCtx.sessionContextWindow);
838
+ assert.equal(adapterCtx.sessionProvider, directCtx.sessionProvider);
839
+ assert.equal(adapterCtx.modelRegistry, directCtx.modelRegistry);
840
+ assert.equal(adapterCtx.basePath, directCtx.basePath);
841
+ assert.equal(adapterCtx.mid, directCtx.mid);
842
+ assert.equal(adapterCtx.midTitle, directCtx.midTitle);
843
+
844
+ // Dispatch action equality: both flows reach the same dispatch decision.
845
+ if (!adapterResult || !("unitType" in adapterResult)) {
846
+ assert.fail("expected adapter result to be a dispatch decision");
847
+ }
848
+ assert.equal(adapterResult.unitType, "execute-task");
849
+ assert.equal(adapterResult.unitId, "T01");
850
+ assert.equal(adapterResult.reason, "test-capture");
851
+ assert.equal(directAction.action, "dispatch");
852
+ if (directAction.action === "dispatch") {
853
+ assert.equal(directAction.unitType, adapterResult.unitType);
854
+ assert.equal(directAction.unitId, adapterResult.unitId);
855
+ assert.equal(directAction.matchedRule, adapterResult.reason);
856
+ }
857
+ } finally {
858
+ resetRegistry();
859
+ }
860
+ });
861
+
862
+ test("wired DispatchAdapter prefers caller-supplied dispatch inputs over ctx-derived values", async () => {
863
+ const stateSnapshot = makeState();
864
+ const captured: DispatchContext[] = [];
865
+ const captureRule: UnifiedRule = {
866
+ name: "test-capture-overrides",
867
+ when: "dispatch",
868
+ evaluation: "first-match",
869
+ where: async (ctx: DispatchContext) => {
870
+ captured.push(ctx);
871
+ return {
872
+ action: "dispatch" as const,
873
+ unitType: "execute-task",
874
+ unitId: "T01",
875
+ prompt: "override-fixture",
876
+ };
877
+ },
878
+ then: (r: unknown) => r,
879
+ };
880
+ setRegistry(new RuleRegistry([captureRule]));
881
+
882
+ try {
883
+ const ctxModelRegistry = {
884
+ getAll: () => [],
885
+ getProviderAuthMode: (_provider: string) => "apiKey" as const,
886
+ };
887
+ const overrideModelRegistry = {
888
+ getAll: () => [],
889
+ getProviderAuthMode: (_provider: string) => "oauth" as const,
890
+ };
891
+ const ctx = {
892
+ model: {
893
+ provider: "anthropic",
894
+ baseUrl: "https://api.anthropic.com",
895
+ contextWindow: 200_000,
896
+ },
897
+ modelRegistry: ctxModelRegistry,
898
+ } as any;
899
+ const pi = {
900
+ getActiveTools: () => [],
901
+ } as any;
902
+ const adapter = createWiredDispatchAdapter(ctx, pi, "/tmp/parity-fixture");
903
+
904
+ const result = await adapter.decideNextUnit({
905
+ stateSnapshot,
906
+ structuredQuestionsAvailable: "true",
907
+ sessionContextWindow: 500_000,
908
+ sessionProvider: "openai",
909
+ modelRegistry: overrideModelRegistry,
910
+ });
911
+
912
+ assert.ok(result);
913
+ assert.equal(captured.length, 1, "expected one captured dispatch context");
914
+ assert.equal(captured[0].structuredQuestionsAvailable, "true");
915
+ assert.equal(captured[0].sessionContextWindow, 500_000);
916
+ assert.equal(captured[0].sessionProvider, "openai");
917
+ assert.equal(captured[0].modelRegistry, overrideModelRegistry);
918
+ } finally {
919
+ resetRegistry();
920
+ }
921
+ });
922
+
923
+ test("wired DispatchAdapter preserves stop reason as a blocked decision", async () => {
924
+ const stateSnapshot = makeState();
925
+ const stopRule: UnifiedRule = {
926
+ name: "test-stop",
927
+ when: "dispatch",
928
+ evaluation: "first-match",
929
+ where: async () => ({
930
+ action: "stop" as const,
931
+ reason: "remediation blocker",
932
+ level: "warning" as const,
933
+ }),
934
+ then: (r: unknown) => r,
935
+ };
936
+ setRegistry(new RuleRegistry([stopRule]));
937
+
938
+ try {
939
+ const ctx = { model: {}, modelRegistry: { getAll: () => [] } } as any;
940
+ const pi = { getActiveTools: () => [] } as any;
941
+ const adapter = createWiredDispatchAdapter(ctx, pi, "/tmp/parity-fixture");
942
+
943
+ const result = await adapter.decideNextUnit({ stateSnapshot });
944
+
945
+ assert.deepEqual(result, {
946
+ kind: "blocked",
947
+ reason: "remediation blocker",
948
+ action: "pause",
949
+ });
950
+ } finally {
951
+ resetRegistry();
952
+ }
953
+ });
@@ -43,7 +43,7 @@ test("cleanupAfterLoopExit preserves paused auto badge after provider pause", as
43
43
  }
44
44
  });
45
45
 
46
- test("cleanupAfterLoopExit clears status without replacing the last auto surface", async () => {
46
+ test("cleanupAfterLoopExit clears status and progress widget without replacing outcome surface", async () => {
47
47
  const statusCalls: unknown[] = [];
48
48
  const widgetCalls: unknown[] = [];
49
49
 
@@ -64,8 +64,8 @@ test("cleanupAfterLoopExit clears status without replacing the last auto surface
64
64
  assert.deepEqual(statusCalls, [["gsd-auto", undefined]]);
65
65
  assert.equal(
66
66
  widgetCalls.some((args) => Array.isArray(args) && args[0] === "gsd-progress" && args[1] === undefined),
67
- false,
68
- "cleanup must not clear the last meaningful auto progress surface",
67
+ true,
68
+ "cleanup must clear the stale auto progress widget",
69
69
  );
70
70
  assert.equal(
71
71
  widgetCalls.some((args) => Array.isArray(args) && args[0] === "gsd-outcome"),
@@ -79,7 +79,7 @@ test("cleanupAfterLoopExit clears status without replacing the last auto surface
79
79
  }
80
80
  });
81
81
 
82
- test("cleanupAfterLoopExit preserves completion roll-up after stopAuto reset", async () => {
82
+ test("cleanupAfterLoopExit clears progress widget after stopAuto reset", async () => {
83
83
  const statusCalls: unknown[] = [];
84
84
  const widgetCalls: unknown[] = [];
85
85
 
@@ -103,8 +103,8 @@ test("cleanupAfterLoopExit preserves completion roll-up after stopAuto reset", a
103
103
  assert.deepEqual(statusCalls, [["gsd-auto", undefined]]);
104
104
  assert.equal(
105
105
  widgetCalls.some((args) => Array.isArray(args) && args[0] === "gsd-progress" && args[1] === undefined),
106
- false,
107
- "completion cleanup must not clear the roll-up progress widget",
106
+ true,
107
+ "completion cleanup must clear the stale progress widget",
108
108
  );
109
109
  assert.equal(
110
110
  widgetCalls.some((args) => Array.isArray(args) && args[0] === "gsd-outcome"),
@@ -159,14 +159,15 @@ test("cleanupAfterLoopExit keeps cleanup best-effort when lifecycle restore thro
159
159
  const previousCwd = process.cwd();
160
160
  let restoreCalls = 0;
161
161
  // ADR-016 phase 3 (#5693): the real `restoreToProjectRoot` assigns
162
- // `s.basePath = s.originalBasePath` BEFORE any throwable work
162
+ // `s.basePath = s.originalBasePath` AND chdir's BEFORE any throwable work
163
163
  // (rebuildGitService, cache invalidation). Mirror that ordering in the
164
- // mock so the throw scenario reflects production: basePath is restored
165
- // even when the verb throws partway through.
164
+ // mock so the throw scenario reflects production: basePath and cwd are
165
+ // restored even when the verb throws partway through.
166
166
  t.mock.method(WorktreeLifecycle.prototype, "restoreToProjectRoot", function (this: WorktreeLifecycle) {
167
167
  restoreCalls += 1;
168
- (this as unknown as { s: { basePath: string; originalBasePath: string } })
169
- .s.basePath = (this as unknown as { s: { originalBasePath: string } }).s.originalBasePath;
168
+ const sRef = this as unknown as { s: { basePath: string; originalBasePath: string } };
169
+ sRef.s.basePath = sRef.s.originalBasePath;
170
+ try { process.chdir(sRef.s.basePath); } catch { /* mirror real verb's best-effort */ }
170
171
  throw new Error("restore failed");
171
172
  });
172
173