@opengsd/gsd-pi 1.2.0-dev.955e4da0 → 1.2.0-dev.d6c5343c

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 (424) hide show
  1. package/dist/cli-style.d.ts +17 -0
  2. package/dist/cli-style.js +28 -0
  3. package/dist/cli.js +1 -1
  4. package/dist/headless-events.d.ts +4 -2
  5. package/dist/headless-events.js +14 -34
  6. package/dist/models-resolver.d.ts +3 -13
  7. package/dist/models-resolver.js +3 -22
  8. package/dist/resource-loader.js +2 -14
  9. package/dist/resources/.managed-resources-content-hash +1 -1
  10. package/dist/resources/GSD-WORKFLOW.md +5 -4
  11. package/dist/resources/extensions/async-jobs/async-bash-tool.js +30 -64
  12. package/dist/resources/extensions/async-jobs/await-tool.js +80 -12
  13. package/dist/resources/extensions/async-jobs/index.js +65 -0
  14. package/dist/resources/extensions/async-jobs/job-manager.js +12 -1
  15. package/dist/resources/extensions/bg-shell/bg-shell-command.js +6 -6
  16. package/dist/resources/extensions/bg-shell/bg-shell-tool.js +10 -7
  17. package/dist/resources/extensions/bg-shell/overlay.js +9 -6
  18. package/dist/resources/extensions/bg-shell/process-manager.js +54 -25
  19. package/dist/resources/extensions/bg-shell/readiness-detector.js +11 -0
  20. package/dist/resources/extensions/bg-shell/utilities.js +3 -0
  21. package/dist/resources/extensions/browser-tools/engine/managed-gsd-browser.js +209 -88
  22. package/dist/resources/extensions/browser-tools/engine/selection.js +73 -5
  23. package/dist/resources/extensions/browser-tools/index.js +69 -12
  24. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +30 -4
  25. package/dist/resources/extensions/gsd/auto/custom-verify-retry-store.js +17 -2
  26. package/dist/resources/extensions/gsd/auto/detect-stuck.js +33 -13
  27. package/dist/resources/extensions/gsd/auto/dispatch-history.js +105 -0
  28. package/dist/resources/extensions/gsd/auto/dispatch-key.js +37 -0
  29. package/dist/resources/extensions/gsd/auto/loop.js +4 -1
  30. package/dist/resources/extensions/gsd/auto/orchestrator.js +61 -44
  31. package/dist/resources/extensions/gsd/auto/phases.js +2 -2
  32. package/dist/resources/extensions/gsd/auto-direct-dispatch.js +8 -32
  33. package/dist/resources/extensions/gsd/auto-dispatch.js +40 -57
  34. package/dist/resources/extensions/gsd/auto-model-selection.js +25 -6
  35. package/dist/resources/extensions/gsd/auto-post-unit.js +23 -8
  36. package/dist/resources/extensions/gsd/auto-prompts.js +81 -19
  37. package/dist/resources/extensions/gsd/auto-start.js +18 -15
  38. package/dist/resources/extensions/gsd/auto-tool-tracking.js +18 -0
  39. package/dist/resources/extensions/gsd/auto-unit-tool-scope.js +12 -20
  40. package/dist/resources/extensions/gsd/auto-verification.js +9 -28
  41. package/dist/resources/extensions/gsd/auto-worktree.js +30 -90
  42. package/dist/resources/extensions/gsd/auto.js +4 -13
  43. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +3 -2
  44. package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +23 -6
  45. package/dist/resources/extensions/gsd/bootstrap/register-extension.js +19 -0
  46. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +212 -48
  47. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +303 -77
  48. package/dist/resources/extensions/gsd/branch-patterns.js +2 -0
  49. package/dist/resources/extensions/gsd/browser-daemon-auto-prep.js +83 -0
  50. package/dist/resources/extensions/gsd/browser-evidence.js +8 -2
  51. package/dist/resources/extensions/gsd/captures.js +4 -6
  52. package/dist/resources/extensions/gsd/consent-question.js +337 -0
  53. package/dist/resources/extensions/gsd/consent-verdict.js +63 -0
  54. package/dist/resources/extensions/gsd/constants.js +0 -2
  55. package/dist/resources/extensions/gsd/crash-recovery.js +4 -12
  56. package/dist/resources/extensions/gsd/db/queries.js +26 -0
  57. package/dist/resources/extensions/gsd/dispatch-guard.js +10 -35
  58. package/dist/resources/extensions/gsd/doctor-environment.js +2 -6
  59. package/dist/resources/extensions/gsd/doctor-format.js +9 -6
  60. package/dist/resources/extensions/gsd/doctor-runtime-checks.js +13 -15
  61. package/dist/resources/extensions/gsd/engine-hook-contract.js +70 -0
  62. package/dist/resources/extensions/gsd/error-classifier.js +9 -0
  63. package/dist/resources/extensions/gsd/exec-sandbox.js +30 -10
  64. package/dist/resources/extensions/gsd/files.js +33 -19
  65. package/dist/resources/extensions/gsd/guidance.js +158 -0
  66. package/dist/resources/extensions/gsd/guided-flow.js +17 -2
  67. package/dist/resources/extensions/gsd/markdown-renderer.js +10 -0
  68. package/dist/resources/extensions/gsd/mcp-filter.js +2 -19
  69. package/dist/resources/extensions/gsd/mcp-tool-name.js +5 -13
  70. package/dist/resources/extensions/gsd/memory-consolidation-scanner.js +1 -1
  71. package/dist/resources/extensions/gsd/migrate/safety.js +4 -1
  72. package/dist/resources/extensions/gsd/milestone-closeout.js +13 -23
  73. package/dist/resources/extensions/gsd/notification-store.js +11 -4
  74. package/dist/resources/extensions/gsd/parallel-monitor-overlay.js +6 -4
  75. package/dist/resources/extensions/gsd/parsers-legacy.js +16 -4
  76. package/dist/resources/extensions/gsd/paths.js +27 -0
  77. package/dist/resources/extensions/gsd/pre-execution-checks.js +91 -3
  78. package/dist/resources/extensions/gsd/preferences-models.js +14 -48
  79. package/dist/resources/extensions/gsd/prompts/complete-slice.md +2 -2
  80. package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  81. package/dist/resources/extensions/gsd/prompts/refine-slice.md +1 -1
  82. package/dist/resources/extensions/gsd/prompts/replan-slice.md +1 -1
  83. package/dist/resources/extensions/gsd/prompts/run-uat.md +6 -4
  84. package/dist/resources/extensions/gsd/prompts/system.md +5 -2
  85. package/dist/resources/extensions/gsd/provider-error-guidance.js +1 -5
  86. package/dist/resources/extensions/gsd/provider-switch-observer.js +1 -1
  87. package/dist/resources/extensions/gsd/publication.js +87 -0
  88. package/dist/resources/extensions/gsd/reactive-graph.js +8 -1
  89. package/dist/resources/extensions/gsd/recovery-classification.js +37 -94
  90. package/dist/resources/extensions/gsd/safety/destructive-confirmation.js +108 -0
  91. package/dist/resources/extensions/gsd/state.js +6 -20
  92. package/dist/resources/extensions/gsd/stop-notice.js +57 -0
  93. package/dist/resources/extensions/gsd/tool-presentation-plan.js +4 -4
  94. package/dist/resources/extensions/gsd/tool-surface-readiness.js +56 -0
  95. package/dist/resources/extensions/gsd/tools/complete-slice.js +20 -10
  96. package/dist/resources/extensions/gsd/tools/exec-tool.js +9 -7
  97. package/dist/resources/extensions/gsd/tools/plan-slice.js +12 -6
  98. package/dist/resources/extensions/gsd/uat-policy.js +42 -16
  99. package/dist/resources/extensions/gsd/unit-closeout.js +138 -0
  100. package/dist/resources/extensions/gsd/unit-context-composer.js +74 -1
  101. package/dist/resources/extensions/gsd/unit-context-manifest.js +4 -27
  102. package/dist/resources/extensions/gsd/unit-registry.js +337 -0
  103. package/dist/resources/extensions/gsd/unit-tool-contracts.js +9 -182
  104. package/dist/resources/extensions/gsd/verdict-parser.js +1 -1
  105. package/dist/resources/extensions/gsd/web-app-uat.js +45 -8
  106. package/dist/resources/extensions/gsd/workflow-tool-surface.js +1 -1
  107. package/dist/resources/extensions/gsd/worktree-git-recovery.js +15 -9
  108. package/dist/resources/extensions/gsd/worktree-lifecycle.js +3 -2
  109. package/dist/resources/extensions/gsd/worktree-root.js +11 -0
  110. package/dist/resources/extensions/gsd/worktree-session-state.js +4 -5
  111. package/dist/resources/extensions/search-the-web/native-search.js +5 -3
  112. package/dist/resources/extensions/shared/browser-contract.js +59 -0
  113. package/dist/resources/extensions/shared/gsd-browser-cli.js +96 -5
  114. package/dist/resources/shared/package.json +3 -0
  115. package/dist/resources/skills/create-skill/references/executable-code.md +1 -1
  116. package/dist/resources/skills/create-skill/workflows/add-reference.md +8 -3
  117. package/dist/resources/skills/create-skill/workflows/add-script.md +4 -2
  118. package/dist/resources/skills/create-skill/workflows/add-template.md +3 -1
  119. package/dist/resources/skills/create-skill/workflows/add-workflow.md +8 -3
  120. package/dist/resources/skills/create-skill/workflows/upgrade-to-router.md +10 -5
  121. package/dist/resources/skills/create-skill/workflows/verify-skill.md +9 -4
  122. package/dist/resources/skills/spike-wrap-up/SKILL.md +9 -9
  123. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  124. package/dist/web/standalone/.next/BUILD_ID +1 -1
  125. package/dist/web/standalone/.next/app-path-routes-manifest.json +12 -12
  126. package/dist/web/standalone/.next/build-manifest.json +3 -3
  127. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  128. package/dist/web/standalone/.next/react-loadable-manifest.json +1 -1
  129. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  130. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  131. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  132. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  133. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  134. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  135. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  136. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  137. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  138. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  139. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  140. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  141. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  142. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  143. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  144. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  145. package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
  146. package/dist/web/standalone/.next/server/app/api/update/route.js.nft.json +1 -1
  147. package/dist/web/standalone/.next/server/app/index.html +1 -1
  148. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  149. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  150. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  151. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  152. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  153. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  154. package/dist/web/standalone/.next/server/app-paths-manifest.json +12 -12
  155. package/dist/web/standalone/.next/server/chunks/5124.js +1 -1
  156. package/dist/web/standalone/.next/server/chunks/8357.js +1 -1
  157. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  158. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  159. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  160. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  161. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  162. package/dist/web/standalone/.next/static/chunks/{796.cf859a427a2cb2ac.js → 796.e0bdc932325d7e03.js} +1 -1
  163. package/dist/web/standalone/.next/static/chunks/{webpack-fbea77b5f9953368.js → webpack-f0285ce91d4ec9ef.js} +1 -1
  164. package/dist/web/standalone/package.json +1 -1
  165. package/dist/worktree-cli.js +3 -6
  166. package/dist/worktree-status-banner.js +7 -15
  167. package/package.json +1 -1
  168. package/packages/cloud-mcp-gateway/package.json +2 -2
  169. package/packages/contracts/dist/rpc.d.ts +1 -0
  170. package/packages/contracts/dist/rpc.d.ts.map +1 -1
  171. package/packages/contracts/dist/rpc.js.map +1 -1
  172. package/packages/contracts/dist/workflow.d.ts +4 -0
  173. package/packages/contracts/dist/workflow.d.ts.map +1 -1
  174. package/packages/contracts/dist/workflow.js.map +1 -1
  175. package/packages/contracts/package.json +1 -1
  176. package/packages/daemon/package.json +4 -4
  177. package/packages/gsd-agent-core/package.json +5 -5
  178. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts +5 -0
  179. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  180. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js +5 -0
  181. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js.map +1 -1
  182. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  183. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js +7 -0
  184. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  185. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/input-controller.d.ts.map +1 -1
  186. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/input-controller.js +8 -1
  187. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/input-controller.js.map +1 -1
  188. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-chat-render.d.ts.map +1 -1
  189. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-chat-render.js +11 -1
  190. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-chat-render.js.map +1 -1
  191. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-auth.d.ts.map +1 -1
  192. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-auth.js +4 -4
  193. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-auth.js.map +1 -1
  194. package/packages/gsd-agent-modes/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  195. package/packages/gsd-agent-modes/dist/modes/rpc/rpc-mode.js +3 -1
  196. package/packages/gsd-agent-modes/dist/modes/rpc/rpc-mode.js.map +1 -1
  197. package/packages/gsd-agent-modes/package.json +7 -7
  198. package/packages/mcp-server/dist/cli.js +6 -3
  199. package/packages/mcp-server/dist/cli.js.map +1 -1
  200. package/packages/mcp-server/dist/workflow-tools.d.ts +8 -0
  201. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  202. package/packages/mcp-server/dist/workflow-tools.js +17 -1
  203. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  204. package/packages/mcp-server/package.json +3 -3
  205. package/packages/native/package.json +1 -1
  206. package/packages/pi-agent-core/dist/harness/env/nodejs.d.ts +1 -0
  207. package/packages/pi-agent-core/dist/harness/env/nodejs.d.ts.map +1 -1
  208. package/packages/pi-agent-core/dist/harness/env/nodejs.js +34 -3
  209. package/packages/pi-agent-core/dist/harness/env/nodejs.js.map +1 -1
  210. package/packages/pi-agent-core/dist/index.d.ts +1 -0
  211. package/packages/pi-agent-core/dist/index.d.ts.map +1 -1
  212. package/packages/pi-agent-core/dist/index.js +3 -0
  213. package/packages/pi-agent-core/dist/index.js.map +1 -1
  214. package/packages/pi-agent-core/package.json +1 -1
  215. package/packages/pi-ai/README.md +1 -0
  216. package/packages/pi-ai/dist/image-models.generated.d.ts +2 -2
  217. package/packages/pi-ai/dist/image-models.generated.js +6 -6
  218. package/packages/pi-ai/dist/image-models.generated.js.map +1 -1
  219. package/packages/pi-ai/dist/models.generated.d.ts +35 -125
  220. package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
  221. package/packages/pi-ai/dist/models.generated.js +46 -120
  222. package/packages/pi-ai/dist/models.generated.js.map +1 -1
  223. package/packages/pi-ai/package.json +3 -2
  224. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts +2 -2
  225. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts.map +1 -1
  226. package/packages/pi-coding-agent/dist/core/auth-storage.js +19 -13
  227. package/packages/pi-coding-agent/dist/core/auth-storage.js.map +1 -1
  228. package/packages/pi-coding-agent/dist/core/provider-readiness.d.ts.map +1 -1
  229. package/packages/pi-coding-agent/dist/core/provider-readiness.js +13 -6
  230. package/packages/pi-coding-agent/dist/core/provider-readiness.js.map +1 -1
  231. package/packages/pi-coding-agent/dist/core/tools/bash.d.ts +11 -0
  232. package/packages/pi-coding-agent/dist/core/tools/bash.d.ts.map +1 -1
  233. package/packages/pi-coding-agent/dist/core/tools/bash.js +53 -11
  234. package/packages/pi-coding-agent/dist/core/tools/bash.js.map +1 -1
  235. package/packages/pi-coding-agent/dist/index.d.ts +1 -1
  236. package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
  237. package/packages/pi-coding-agent/dist/index.js +1 -1
  238. package/packages/pi-coding-agent/dist/index.js.map +1 -1
  239. package/packages/pi-coding-agent/dist/utils/shell.d.ts +28 -2
  240. package/packages/pi-coding-agent/dist/utils/shell.d.ts.map +1 -1
  241. package/packages/pi-coding-agent/dist/utils/shell.js +56 -10
  242. package/packages/pi-coding-agent/dist/utils/shell.js.map +1 -1
  243. package/packages/pi-coding-agent/package.json +7 -7
  244. package/packages/pi-tui/dist/tui.d.ts.map +1 -1
  245. package/packages/pi-tui/dist/tui.js +9 -0
  246. package/packages/pi-tui/dist/tui.js.map +1 -1
  247. package/packages/pi-tui/package.json +2 -2
  248. package/packages/rpc-client/package.json +2 -2
  249. package/pkg/package.json +1 -1
  250. package/src/resources/GSD-WORKFLOW.md +5 -4
  251. package/src/resources/extensions/async-jobs/async-bash-cancel.test.ts +360 -0
  252. package/src/resources/extensions/async-jobs/async-bash-tool.ts +33 -56
  253. package/src/resources/extensions/async-jobs/await-tool.test.ts +139 -0
  254. package/src/resources/extensions/async-jobs/await-tool.ts +82 -12
  255. package/src/resources/extensions/async-jobs/index.ts +79 -0
  256. package/src/resources/extensions/async-jobs/job-manager.ts +21 -1
  257. package/src/resources/extensions/bg-shell/bg-shell-command.ts +6 -6
  258. package/src/resources/extensions/bg-shell/bg-shell-tool.ts +10 -6
  259. package/src/resources/extensions/bg-shell/overlay.ts +9 -5
  260. package/src/resources/extensions/bg-shell/process-manager.ts +50 -25
  261. package/src/resources/extensions/bg-shell/readiness-detector.ts +12 -0
  262. package/src/resources/extensions/bg-shell/tests/lifecycle-and-utilities.test.ts +48 -1
  263. package/src/resources/extensions/bg-shell/utilities.ts +3 -0
  264. package/src/resources/extensions/browser-tools/engine/managed-gsd-browser.ts +265 -98
  265. package/src/resources/extensions/browser-tools/engine/selection.ts +90 -4
  266. package/src/resources/extensions/browser-tools/index.ts +71 -13
  267. package/src/resources/extensions/browser-tools/tests/browser-engine-selection.test.mjs +83 -13
  268. package/src/resources/extensions/browser-tools/tests/gsd-browser-launch-config.test.mjs +29 -1
  269. package/src/resources/extensions/browser-tools/tests/managed-gsd-browser-tools.test.mjs +136 -0
  270. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +34 -4
  271. package/src/resources/extensions/gsd/auto/custom-verify-retry-store.ts +21 -3
  272. package/src/resources/extensions/gsd/auto/detect-stuck.ts +32 -9
  273. package/src/resources/extensions/gsd/auto/dispatch-history.ts +152 -0
  274. package/src/resources/extensions/gsd/auto/dispatch-key.ts +39 -0
  275. package/src/resources/extensions/gsd/auto/loop.ts +4 -1
  276. package/src/resources/extensions/gsd/auto/orchestrator.ts +70 -46
  277. package/src/resources/extensions/gsd/auto/phases.ts +2 -2
  278. package/src/resources/extensions/gsd/auto-direct-dispatch.ts +8 -32
  279. package/src/resources/extensions/gsd/auto-dispatch.ts +38 -52
  280. package/src/resources/extensions/gsd/auto-model-selection.ts +25 -5
  281. package/src/resources/extensions/gsd/auto-post-unit.ts +25 -8
  282. package/src/resources/extensions/gsd/auto-prompts.ts +118 -35
  283. package/src/resources/extensions/gsd/auto-start.ts +18 -17
  284. package/src/resources/extensions/gsd/auto-tool-tracking.ts +19 -0
  285. package/src/resources/extensions/gsd/auto-unit-tool-scope.ts +14 -21
  286. package/src/resources/extensions/gsd/auto-verification.ts +8 -26
  287. package/src/resources/extensions/gsd/auto-worktree.ts +30 -93
  288. package/src/resources/extensions/gsd/auto.ts +8 -15
  289. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +3 -5
  290. package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +23 -6
  291. package/src/resources/extensions/gsd/bootstrap/register-extension.ts +24 -0
  292. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +251 -47
  293. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +352 -84
  294. package/src/resources/extensions/gsd/branch-patterns.ts +3 -0
  295. package/src/resources/extensions/gsd/browser-daemon-auto-prep.ts +108 -0
  296. package/src/resources/extensions/gsd/browser-evidence.ts +18 -2
  297. package/src/resources/extensions/gsd/captures.ts +4 -6
  298. package/src/resources/extensions/gsd/consent-question.ts +416 -0
  299. package/src/resources/extensions/gsd/consent-verdict.ts +86 -0
  300. package/src/resources/extensions/gsd/constants.ts +0 -3
  301. package/src/resources/extensions/gsd/crash-recovery.ts +3 -9
  302. package/src/resources/extensions/gsd/db/queries.ts +37 -0
  303. package/src/resources/extensions/gsd/dispatch-guard.ts +8 -31
  304. package/src/resources/extensions/gsd/doctor-environment.ts +2 -7
  305. package/src/resources/extensions/gsd/doctor-format.ts +12 -7
  306. package/src/resources/extensions/gsd/doctor-runtime-checks.ts +13 -15
  307. package/src/resources/extensions/gsd/engine-hook-contract.ts +79 -0
  308. package/src/resources/extensions/gsd/error-classifier.ts +11 -0
  309. package/src/resources/extensions/gsd/exec-sandbox.ts +49 -9
  310. package/src/resources/extensions/gsd/files.ts +33 -12
  311. package/src/resources/extensions/gsd/guidance.ts +217 -0
  312. package/src/resources/extensions/gsd/guided-flow.ts +16 -2
  313. package/src/resources/extensions/gsd/markdown-renderer.ts +11 -0
  314. package/src/resources/extensions/gsd/mcp-filter.ts +2 -23
  315. package/src/resources/extensions/gsd/mcp-tool-name.ts +6 -11
  316. package/src/resources/extensions/gsd/memory-consolidation-scanner.ts +1 -1
  317. package/src/resources/extensions/gsd/migrate/safety.ts +4 -1
  318. package/src/resources/extensions/gsd/milestone-closeout.ts +13 -23
  319. package/src/resources/extensions/gsd/notification-store.ts +26 -3
  320. package/src/resources/extensions/gsd/parallel-monitor-overlay.ts +6 -4
  321. package/src/resources/extensions/gsd/parsers-legacy.ts +16 -4
  322. package/src/resources/extensions/gsd/paths.ts +33 -0
  323. package/src/resources/extensions/gsd/pre-execution-checks.ts +109 -3
  324. package/src/resources/extensions/gsd/preferences-models.ts +12 -47
  325. package/src/resources/extensions/gsd/prompts/complete-slice.md +2 -2
  326. package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  327. package/src/resources/extensions/gsd/prompts/refine-slice.md +1 -1
  328. package/src/resources/extensions/gsd/prompts/replan-slice.md +1 -1
  329. package/src/resources/extensions/gsd/prompts/run-uat.md +6 -4
  330. package/src/resources/extensions/gsd/prompts/system.md +5 -2
  331. package/src/resources/extensions/gsd/provider-error-guidance.ts +4 -9
  332. package/src/resources/extensions/gsd/provider-switch-observer.ts +1 -1
  333. package/src/resources/extensions/gsd/publication.ts +122 -0
  334. package/src/resources/extensions/gsd/reactive-graph.ts +11 -1
  335. package/src/resources/extensions/gsd/recovery-classification.ts +42 -96
  336. package/src/resources/extensions/gsd/safety/destructive-confirmation.ts +134 -0
  337. package/src/resources/extensions/gsd/state.ts +9 -21
  338. package/src/resources/extensions/gsd/stop-notice.ts +75 -0
  339. package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +22 -0
  340. package/src/resources/extensions/gsd/tests/auto-orchestrator.test.ts +101 -26
  341. package/src/resources/extensions/gsd/tests/browser-automation-contract-fixture.ts +39 -0
  342. package/src/resources/extensions/gsd/tests/browser-contract.test.ts +44 -0
  343. package/src/resources/extensions/gsd/tests/browser-daemon-auto-prep.test.ts +144 -0
  344. package/src/resources/extensions/gsd/tests/checkout-branch-stash-guard.test.ts +66 -1
  345. package/src/resources/extensions/gsd/tests/clear-stale-autostart.test.ts +22 -0
  346. package/src/resources/extensions/gsd/tests/commands-verdict.test.ts +8 -7
  347. package/src/resources/extensions/gsd/tests/complete-slice-verification-gate.test.ts +42 -0
  348. package/src/resources/extensions/gsd/tests/consent-question.test.ts +336 -0
  349. package/src/resources/extensions/gsd/tests/custom-verify-retry-store.test.ts +67 -0
  350. package/src/resources/extensions/gsd/tests/deep-project-auto-loop.test.ts +10 -10
  351. package/src/resources/extensions/gsd/tests/destructive-confirmation.test.ts +303 -0
  352. package/src/resources/extensions/gsd/tests/dispatch-history.test.ts +273 -0
  353. package/src/resources/extensions/gsd/tests/dispatch-run-uat-browser-tools.test.ts +2 -1
  354. package/src/resources/extensions/gsd/tests/dynamic-bash-no-cap.test.ts +132 -0
  355. package/src/resources/extensions/gsd/tests/engine-hook-contract.test.ts +148 -0
  356. package/src/resources/extensions/gsd/tests/exec-graceful-kill.test.ts +193 -0
  357. package/src/resources/extensions/gsd/tests/exec-tool.test.ts +29 -1
  358. package/src/resources/extensions/gsd/tests/extension-bootstrap-isolation.test.ts +35 -1
  359. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +27 -0
  360. package/src/resources/extensions/gsd/tests/guidance.test.ts +148 -0
  361. package/src/resources/extensions/gsd/tests/integration/auto-worktree-milestone-merge.test.ts +53 -11
  362. package/src/resources/extensions/gsd/tests/integration/auto-worktree.test.ts +73 -58
  363. package/src/resources/extensions/gsd/tests/integration/gsd-integration-fixture.ts +80 -0
  364. package/src/resources/extensions/gsd/tests/integration/run-uat.test.ts +199 -0
  365. package/src/resources/extensions/gsd/tests/mcp-project-config.test.ts +3 -1
  366. package/src/resources/extensions/gsd/tests/model-unittype-mapping.test.ts +32 -1
  367. package/src/resources/extensions/gsd/tests/notification-store.test.ts +32 -0
  368. package/src/resources/extensions/gsd/tests/oauth-api-model-routing.test.ts +167 -0
  369. package/src/resources/extensions/gsd/tests/parallel-research-dispatch.test.ts +18 -0
  370. package/src/resources/extensions/gsd/tests/parsers-legacy-importers.test.ts +139 -0
  371. package/src/resources/extensions/gsd/tests/pre-execution-checks.test.ts +193 -1
  372. package/src/resources/extensions/gsd/tests/prompt-db.test.ts +124 -6
  373. package/src/resources/extensions/gsd/tests/provider-error-guidance.test.ts +3 -3
  374. package/src/resources/extensions/gsd/tests/publication.test.ts +120 -0
  375. package/src/resources/extensions/gsd/tests/register-hooks-depth-verification.test.ts +157 -0
  376. package/src/resources/extensions/gsd/tests/runtime-invariant-modules.test.ts +1 -0
  377. package/src/resources/extensions/gsd/tests/stop-notice.test.ts +70 -0
  378. package/src/resources/extensions/gsd/tests/token-tool-gating.test.ts +76 -0
  379. package/src/resources/extensions/gsd/tests/tool-invocation-error-loop-break.test.ts +8 -0
  380. package/src/resources/extensions/gsd/tests/tool-surface-readiness.test.ts +155 -0
  381. package/src/resources/extensions/gsd/tests/uat-policy.test.ts +112 -29
  382. package/src/resources/extensions/gsd/tests/unit-closeout.test.ts +209 -0
  383. package/src/resources/extensions/gsd/tests/unit-context-composer.test.ts +67 -2
  384. package/src/resources/extensions/gsd/tests/unit-registry.test.ts +163 -0
  385. package/src/resources/extensions/gsd/tests/web-app-uat.test.ts +44 -1
  386. package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +2 -2
  387. package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +2 -2
  388. package/src/resources/extensions/gsd/tests/write-gate-seam.test.ts +358 -0
  389. package/src/resources/extensions/gsd/tests/write-gate.test.ts +67 -1
  390. package/src/resources/extensions/gsd/tool-presentation-plan.ts +4 -4
  391. package/src/resources/extensions/gsd/tool-surface-readiness.ts +76 -0
  392. package/src/resources/extensions/gsd/tools/complete-slice.ts +20 -10
  393. package/src/resources/extensions/gsd/tools/exec-tool.ts +8 -7
  394. package/src/resources/extensions/gsd/tools/plan-slice.ts +12 -6
  395. package/src/resources/extensions/gsd/uat-policy.ts +62 -16
  396. package/src/resources/extensions/gsd/unit-closeout.ts +201 -0
  397. package/src/resources/extensions/gsd/unit-context-composer.ts +111 -1
  398. package/src/resources/extensions/gsd/unit-context-manifest.ts +4 -28
  399. package/src/resources/extensions/gsd/unit-registry.ts +412 -0
  400. package/src/resources/extensions/gsd/unit-tool-contracts.ts +27 -192
  401. package/src/resources/extensions/gsd/verdict-parser.ts +1 -1
  402. package/src/resources/extensions/gsd/web-app-uat.ts +51 -8
  403. package/src/resources/extensions/gsd/workflow-tool-surface.ts +4 -1
  404. package/src/resources/extensions/gsd/worktree-git-recovery.ts +15 -9
  405. package/src/resources/extensions/gsd/worktree-lifecycle.ts +3 -8
  406. package/src/resources/extensions/gsd/worktree-root.ts +12 -0
  407. package/src/resources/extensions/gsd/worktree-session-state.ts +3 -5
  408. package/src/resources/extensions/search-the-web/native-search.ts +5 -3
  409. package/src/resources/extensions/shared/browser-contract.ts +66 -0
  410. package/src/resources/extensions/shared/gsd-browser-cli.ts +119 -5
  411. package/src/resources/shared/package.json +3 -0
  412. package/src/resources/skills/create-skill/references/executable-code.md +1 -1
  413. package/src/resources/skills/create-skill/workflows/add-reference.md +8 -3
  414. package/src/resources/skills/create-skill/workflows/add-script.md +4 -2
  415. package/src/resources/skills/create-skill/workflows/add-template.md +3 -1
  416. package/src/resources/skills/create-skill/workflows/add-workflow.md +8 -3
  417. package/src/resources/skills/create-skill/workflows/upgrade-to-router.md +10 -5
  418. package/src/resources/skills/create-skill/workflows/verify-skill.md +9 -4
  419. package/src/resources/skills/spike-wrap-up/SKILL.md +9 -9
  420. package/dist/resources/extensions/gsd/user-input-boundary.js +0 -218
  421. package/src/resources/extensions/gsd/tests/user-input-boundary.test.ts +0 -173
  422. package/src/resources/extensions/gsd/user-input-boundary.ts +0 -216
  423. /package/dist/web/standalone/.next/static/{C24pqUd-aru-l0Dp0gLZP → jmTLg6xZmAuq_LIqKOxrH}/_buildManifest.js +0 -0
  424. /package/dist/web/standalone/.next/static/{C24pqUd-aru-l0Dp0gLZP → jmTLg6xZmAuq_LIqKOxrH}/_ssgManifest.js +0 -0
@@ -42,21 +42,14 @@ import { isDbAvailable, getSlice, getTask, } from "../gsd-db.js";
42
42
  import { refreshWorkflowDatabaseFromDisk } from "../db-workspace.js";
43
43
  import { getErrorMessage } from "../error-utils.js";
44
44
  import { logWarning } from "../workflow-logger.js";
45
+ import { normalizeRealPath } from "../paths.js";
46
+ import { buildDispatchKey, createDispatchHistory, STUCK_WINDOW_SIZE, } from "./dispatch-history.js";
45
47
  import { existsSync, readFileSync } from "node:fs";
46
48
  import { join } from "node:path";
47
49
  import { evaluateAllCompleteSettlement } from "../milestone-settlement.js";
48
50
  function now() {
49
51
  return Date.now();
50
52
  }
51
- /**
52
- * Size of the dispatch-decision ring buffer used by the Auto Orchestration
53
- * module's stuck-loop detector. When the same `${unitType}:${unitId}` key
54
- * fills the window, advance() blocks with `action: "stop"`.
55
- *
56
- * Mirrors the legacy `STUCK_WINDOW_SIZE` in auto/phases.ts so behaviour is
57
- * preserved across the eventual cutover (issue #5791).
58
- */
59
- export const STUCK_WINDOW_SIZE = 6;
60
53
  function noRemainingUnitsOutcome(stateSnapshot) {
61
54
  if (stateSnapshot.phase === "complete") {
62
55
  return {
@@ -244,7 +237,9 @@ export class AutoOrchestrator {
244
237
  seq = 0;
245
238
  lastAdvanceKey = null;
246
239
  lastFinalizedUnitKey = null;
247
- dispatchKeyWindow = [];
240
+ // Dispatch History module (#482): the dispatch-decision window with
241
+ // cross-session DB rehydration and full detect-stuck rules.
242
+ dispatchHistory;
248
243
  // ADR-030 Phase Transition Invariant: the prior advance's reconciled Phase,
249
244
  // the "from" endpoint of the edge check. In-memory; reset on start/resume/stop
250
245
  // so the first advance of a session has no edge to assert.
@@ -260,6 +255,13 @@ export class AutoOrchestrator {
260
255
  this.runtimeBasePath = context.runtimeBasePath;
261
256
  this.s = context.session;
262
257
  this.flowId = `auto-orchestrator-${Date.now()}`;
258
+ this.dispatchHistory = createDispatchHistory({
259
+ windowSize: STUCK_WINDOW_SIZE,
260
+ // Same stable scope the auto-loop uses for stuck-state persistence so
261
+ // rehydration reads the rows the dispatch ledger wrote for this project.
262
+ resolveScopeId: () => normalizeRealPath(this.s.scope?.workspace.projectRoot ??
263
+ (this.s.originalBasePath || this.s.basePath || this.runtimeBasePath)) || null,
264
+ });
263
265
  }
264
266
  // ── Live base-path resolution (was the wiring factory's getLiveDispatchBasePath) ──
265
267
  getLiveDispatchBasePath() {
@@ -612,7 +614,7 @@ export class AutoOrchestrator {
612
614
  * skipped result) instead of stopping.
613
615
  */
614
616
  tryStuckArtifactRecovery(unitType, unitId) {
615
- const key = `${unitType}:${unitId}`;
617
+ const key = buildDispatchKey(unitType, unitId);
616
618
  if (this.lastStuckRecoveryKey === key)
617
619
  return false; // already tried this episode
618
620
  const basePath = this.getLiveDispatchBasePath();
@@ -627,7 +629,7 @@ export class AutoOrchestrator {
627
629
  return false;
628
630
  this.lastStuckRecoveryKey = key;
629
631
  invalidateAllCaches();
630
- this.dispatchKeyWindow = [];
632
+ this.dispatchHistory.clearOnRecovery();
631
633
  this.lastAdvanceKey = null;
632
634
  this.lastFinalizedUnitKey = null;
633
635
  return true;
@@ -653,7 +655,12 @@ export class AutoOrchestrator {
653
655
  async start(_sessionContext) {
654
656
  this.lastAdvanceKey = null;
655
657
  this.lastFinalizedUnitKey = null;
656
- this.dispatchKeyWindow = [];
658
+ // #482: the DB dispatch ledger is the source of truth across sessions.
659
+ // Discard any in-memory window and rebuild it from the ledger so a unit
660
+ // that was re-dispatched in previous sessions is detected as stuck here
661
+ // instead of silently re-dispatching forever.
662
+ this.dispatchHistory.clearOnRecovery();
663
+ this.dispatchHistory.rehydrate();
657
664
  this.lastStuckRecoveryKey = null;
658
665
  this.lastDerivedPhase = null;
659
666
  this.status.phase = "running";
@@ -753,7 +760,7 @@ export class AutoOrchestrator {
753
760
  this.status.phase = "paused";
754
761
  this.status.activeUnit = undefined;
755
762
  this.lastAdvanceKey = null;
756
- this.dispatchKeyWindow = [];
763
+ this.dispatchHistory.clearOnRecovery();
757
764
  this.bumpTransition();
758
765
  this.journalTransition({ name: "advance-blocked", reason: settlementBlock.reason });
759
766
  this.postAdvanceRecord(settlementBlock);
@@ -769,7 +776,7 @@ export class AutoOrchestrator {
769
776
  this.status.phase = "stopped";
770
777
  this.status.activeUnit = undefined;
771
778
  this.lastAdvanceKey = null;
772
- this.dispatchKeyWindow = [];
779
+ this.dispatchHistory.clearOnRecovery();
773
780
  this.bumpTransition();
774
781
  this.journalTransition({ name: "advance-stopped", reason: stopped.reason });
775
782
  this.postAdvanceRecord(stopped);
@@ -817,16 +824,12 @@ export class AutoOrchestrator {
817
824
  this.postAdvanceRecord(blocked);
818
825
  return blocked;
819
826
  }
820
- const nextKey = `${decision.unitType}:${decision.unitId}`;
821
- // Record every dispatch decision in the ring buffer before pre-flight
827
+ // Record every dispatch decision in the history window before pre-flight
822
828
  // checks so the stuck-loop detector observes the full decision history
823
829
  // (including decisions that idempotency would otherwise short-circuit).
824
- // The ring is capped at STUCK_WINDOW_SIZE and evicts oldest-first.
825
- this.dispatchKeyWindow.push(nextKey);
826
- if (this.dispatchKeyWindow.length > STUCK_WINDOW_SIZE) {
827
- this.dispatchKeyWindow.shift();
828
- }
829
- const matchingCount = this.dispatchKeyWindow.filter((k) => k === nextKey).length;
830
+ // The window is capped at STUCK_WINDOW_SIZE and evicts oldest-first.
831
+ const nextKey = this.dispatchHistory.recordDispatch(decision.unitType, decision.unitId);
832
+ const matchingCount = this.dispatchHistory.countMatching(nextKey);
830
833
  if (this.lastFinalizedUnitKey === nextKey) {
831
834
  // #442: the unit re-dispatched immediately after finalizing may have
832
835
  // actually completed on disk with a stale DB. Verify + recover before
@@ -858,22 +861,30 @@ export class AutoOrchestrator {
858
861
  // checks coexist: idempotency for the common immediate-repeat case,
859
862
  // stuck-loop for the saturated-window case.
860
863
  if (this.lastAdvanceKey === nextKey && matchingCount < STUCK_WINDOW_SIZE) {
864
+ // Unit already active — benign no-op. Return skipped so the loop re-polls
865
+ // without cancelling the in-flight unit (blocked+pause would force-cancel it).
861
866
  this.clearPendingDispatch();
862
- const blocked = { kind: "blocked", reason: "idempotent advance: unit already active", action: "pause" };
867
+ const skipped = { kind: "skipped", reason: "idempotent advance: unit already active" };
863
868
  this.journalTransition({
864
- name: "advance-blocked",
865
- reason: blocked.reason,
869
+ name: "advance-skipped",
870
+ reason: skipped.reason,
866
871
  unitType: decision.unitType,
867
872
  unitId: decision.unitId,
868
873
  });
869
- this.postAdvanceRecord(blocked);
870
- return blocked;
874
+ this.postAdvanceRecord(skipped);
875
+ return skipped;
871
876
  }
872
- // Stuck-loop detection: when the ring is saturated with copies of
873
- // `nextKey` (count >= STUCK_WINDOW_SIZE), the orchestrator has been
874
- // picking the same unit across the whole window and must hard-stop with
875
- // a diagnosable reason.
876
- if (matchingCount >= STUCK_WINDOW_SIZE) {
877
+ // Stuck-loop detection: when the window is saturated with copies of
878
+ // `nextKey` (count >= STUCK_WINDOW_SIZE), consult the Dispatch History
879
+ // module's full detect-stuck rule set for the verdict instead of the old
880
+ // bare saturation count. This keeps the saturation threshold (the window
881
+ // deliberately records benign idempotent repeats, so earlier-firing
882
+ // rules would false-positive on pause/resume re-advances) while gaining
883
+ // retry-budget suppression and diagnosable rule reasons. A saturated
884
+ // window with no verdict means the dispatch ledger says we are inside
885
+ // the unit's retry-backoff budget — let the retry proceed.
886
+ const stuckVerdict = matchingCount >= STUCK_WINDOW_SIZE ? this.dispatchHistory.detectStuck() : null;
887
+ if (stuckVerdict) {
877
888
  // #442: before declaring a stuck loop, verify the unit didn't actually
878
889
  // complete on disk (stale DB) and recover if so — legacy graduated
879
890
  // stuck-recovery parity. Otherwise hard-stop with a diagnosable reason.
@@ -884,7 +895,7 @@ export class AutoOrchestrator {
884
895
  this.clearPendingDispatch();
885
896
  const blocked = {
886
897
  kind: "blocked",
887
- reason: `stuck-loop: ${nextKey} picked ${matchingCount} times`,
898
+ reason: `stuck-loop: ${stuckVerdict.reason}`,
888
899
  action: "stop",
889
900
  };
890
901
  this.journalTransition({
@@ -974,7 +985,7 @@ export class AutoOrchestrator {
974
985
  if (result.kind === "stopped") {
975
986
  this.lastAdvanceKey = null;
976
987
  this.lastFinalizedUnitKey = null;
977
- this.dispatchKeyWindow = [];
988
+ this.dispatchHistory.clearOnRecovery();
978
989
  this.status.activeUnit = undefined;
979
990
  }
980
991
  this.bumpTransition();
@@ -1003,8 +1014,14 @@ export class AutoOrchestrator {
1003
1014
  async resume() {
1004
1015
  this.lastAdvanceKey = null;
1005
1016
  this.lastFinalizedUnitKey = null;
1006
- // Preserve dispatchKeyWindow across resume so stuck-loop detection
1007
- // accumulates across pause/resume cycles rather than resetting each time.
1017
+ // Preserve the dispatch-history window across an in-process resume so
1018
+ // stuck-loop detection accumulates across pause/resume cycles rather than
1019
+ // resetting each time (#572 regression). When the window is empty (fresh
1020
+ // orchestrator resuming a prior session), rehydrate it from the DB
1021
+ // dispatch ledger so cross-session re-dispatch loops are detected (#482).
1022
+ if (this.dispatchHistory.getRecentWindow().length === 0) {
1023
+ this.dispatchHistory.rehydrate();
1024
+ }
1008
1025
  this.lastStuckRecoveryKey = null;
1009
1026
  // ADR-030: drop the prior "from" — the first advance after resume has no
1010
1027
  // edge to assert (avoids a false illegal-edge across the pause boundary).
@@ -1025,10 +1042,10 @@ export class AutoOrchestrator {
1025
1042
  this.lastAdvanceKey = null;
1026
1043
  this.lastFinalizedUnitKey = null;
1027
1044
  this.lastDerivedPhase = null;
1028
- // Preserve dispatchKeyWindow on pause so stuck-loop detection accumulates
1029
- // across pause/resume cycles. Only clear on a hard stop.
1045
+ // Preserve the dispatch-history window on pause so stuck-loop detection
1046
+ // accumulates across pause/resume cycles. Only clear on a hard stop.
1030
1047
  if (reason !== "pause") {
1031
- this.dispatchKeyWindow = [];
1048
+ this.dispatchHistory.clearOnRecovery();
1032
1049
  }
1033
1050
  this.lastStuckRecoveryKey = null;
1034
1051
  this.bumpTransition();
@@ -1040,9 +1057,9 @@ export class AutoOrchestrator {
1040
1057
  return { ...this.status, activeUnit: this.status.activeUnit ? { ...this.status.activeUnit } : undefined };
1041
1058
  }
1042
1059
  async completeActiveUnit(unit) {
1043
- const unitKey = `${unit.unitType}:${unit.unitId}`;
1060
+ const unitKey = buildDispatchKey(unit.unitType, unit.unitId);
1044
1061
  const activeUnitKey = this.status.activeUnit
1045
- ? `${this.status.activeUnit.unitType}:${this.status.activeUnit.unitId}`
1062
+ ? buildDispatchKey(this.status.activeUnit.unitType, this.status.activeUnit.unitId)
1046
1063
  : null;
1047
1064
  if (activeUnitKey !== unitKey)
1048
1065
  return;
@@ -1059,9 +1076,9 @@ export class AutoOrchestrator {
1059
1076
  });
1060
1077
  }
1061
1078
  async retryActiveUnit(unit) {
1062
- const unitKey = `${unit.unitType}:${unit.unitId}`;
1079
+ const unitKey = buildDispatchKey(unit.unitType, unit.unitId);
1063
1080
  const activeUnitKey = this.status.activeUnit
1064
- ? `${this.status.activeUnit.unitType}:${this.status.activeUnit.unitId}`
1081
+ ? buildDispatchKey(this.status.activeUnit.unitType, this.status.activeUnit.unitId)
1065
1082
  : null;
1066
1083
  if (activeUnitKey !== unitKey && this.lastFinalizedUnitKey !== unitKey)
1067
1084
  return;
@@ -10,10 +10,11 @@
10
10
  */
11
11
  import { importExtensionModule } from "@gsd/pi-coding-agent";
12
12
  import { USER_DRIVEN_DEEP_UNITS, isAwaitingUserInput, } from "../auto-post-unit.js";
13
- import { lastAssistantText } from "../user-input-boundary.js";
13
+ import { lastAssistantText } from "../consent-question.js";
14
14
  import { resolveEffectiveUnitIsolationMode } from "../preferences.js";
15
15
  import { MAX_RECOVERY_CHARS, BUDGET_THRESHOLDS, MAX_FINALIZE_TIMEOUTS, } from "./types.js";
16
16
  import { detectStuck } from "./detect-stuck.js";
17
+ import { STUCK_WINDOW_SIZE } from "./dispatch-history.js";
17
18
  import { runUnit } from "./run-unit.js";
18
19
  import { debugLog } from "../debug-logger.js";
19
20
  import { resolveWorktreeProjectRoot, normalizeWorktreePathForCompare } from "../worktree-root.js";
@@ -60,7 +61,6 @@ import { buildPhaseHandoffOutcome, setAutoActiveStatus, setAutoOutcomeWidget } f
60
61
  import { getConsecutiveDispatchBlocker } from "../dispatch-guard.js";
61
62
  import { captureRootDirtySnapshot, detectRootWriteLeak, formatRootWriteLeakMessage, } from "../root-write-leak-guard.js";
62
63
  import { classifyError, isTransient } from "../error-classifier.js";
63
- export const STUCK_WINDOW_SIZE = 6;
64
64
  const STUCK_RECOVERY_ATTEMPTS_KEY = "stuck_recovery_attempts";
65
65
  const ZERO_TOOL_PROVIDER_ERROR_PREFIX_RE = /^(?:api error(?::|$|\s*\()|provider error(?::|$|\s*\()|request failed\b|(?:http\s*)?(?:429|500|502|503)\b|\b(?:econnreset|etimedout|econnrefused|epipe)\b|socket hang up\b|fetch failed\b|(?:network|connection|server) error(?::|$)|connection (?:reset|refused)(?::|$|\s+by\b)|dns\b.*(?:fail|error|timeout)|unexpected eof\b|stream idle timeout\b|partial response received\b|stream_exhausted\b|terminated(?::|$)|(?:connection|stream|request)\b.{0,40}\bterminated\b|other side closed\b|rate.?limit(?:ed| exceeded| reached| error)|too many requests\b|you(?:'ve| have) (?:hit|reached) your (?:\w+ )?limit\b|.*\b(?:usage|session|weekly|daily|monthly|quota) limit\b|limit\b.{0,40}\bresets?\b|out of extra usage\b|service.?unavailable\b|internal(?: server)? error(?::|$)|internal(?:[_-]server)?[_-]error\b|server[_-]error\b|(?:provider|server|api|model|codex|claude|openai|anthropic|gemini)\b.{0,80}\boverloaded\b|overloaded\b.{0,80}\b(?:provider|server|api|model)\b|context (?:window|length) exceed|context window exceed)/i;
66
66
  const ZERO_TOOL_PROVIDER_ERROR_SIGNAL_RE = /(?:\b(?:http|status(?: code)?|code|error:)\s*(?:429|500|502|503)\b|\b(?:api|provider) error\s*[:(]?\s*(?:429|500|502|503)\b|\b(?:typeerror|error):\s*(?:fetch failed\b|socket hang up\b|terminated(?::|$)|connection (?:reset|refused)(?::|$|\s+by\b)|(?:network|connection|server) error(?::|$)|stream idle timeout\b|partial response received\b|unexpected eof\b)|\b(?:server_error|api_error|stream_exhausted(?:_without_result)?)\b|\b(?:econnreset|etimedout|econnrefused|epipe)\b|context (?:window|length) exceed|context window exceed)/i;
@@ -4,9 +4,8 @@
4
4
  */
5
5
  import { deriveState } from "./state.js";
6
6
  import { loadFile } from "./files.js";
7
- import { isDbAvailable, getMilestoneSlices } from "./gsd-db.js";
8
- import { parseRoadmap } from "./parsers-legacy.js";
9
- import { resolveMilestoneFile, resolveSliceFile, relSliceFile, } from "./paths.js";
7
+ import { isDbAvailable, getClosedSliceIds } from "./gsd-db.js";
8
+ import { resolveSliceFile, relSliceFile, } from "./paths.js";
10
9
  import { buildResearchSlicePrompt, buildResearchMilestonePrompt, buildPlanSlicePrompt, buildPlanMilestonePrompt, buildExecuteTaskPrompt, buildCompleteSlicePrompt, buildCompleteMilestonePrompt, buildValidateMilestonePrompt, buildReassessRoadmapPrompt, buildRunUatPrompt, buildReplanSlicePrompt, } from "./auto-prompts.js";
11
10
  import { loadEffectiveGSDPreferences } from "./preferences.js";
12
11
  import { pauseAuto } from "./auto.js";
@@ -135,21 +134,9 @@ export async function dispatchDirectPhase(ctx, pi, phase, base) {
135
134
  }
136
135
  case "reassess":
137
136
  case "reassess-roadmap": {
138
- // DB primary pathget completed slices, fall back to file parsing when DB has no data
139
- let completedSliceIds = [];
140
- if (isDbAvailable()) {
141
- completedSliceIds = getMilestoneSlices(mid).filter(s => s.status === "complete").map(s => s.id);
142
- }
143
- if (completedSliceIds.length === 0) {
144
- // File-based fallback: parse roadmap checkboxes
145
- const roadmapPath = resolveMilestoneFile(dispatchBase, mid, "ROADMAP");
146
- if (roadmapPath) {
147
- const roadmapContent = await loadFile(roadmapPath);
148
- if (roadmapContent) {
149
- completedSliceIds = parseRoadmap(roadmapContent).slices.filter(s => s.done).map(s => s.id);
150
- }
151
- }
152
- }
137
+ // DB-authoritative read (ADR-017)markdown projections are never
138
+ // consulted for dispatch decisions. No DB rows means no completed slices.
139
+ const completedSliceIds = isDbAvailable() ? getClosedSliceIds(mid) : [];
153
140
  if (completedSliceIds.length === 0) {
154
141
  ctx.ui.notify("Cannot dispatch reassess-roadmap: no completed slices.", "warning");
155
142
  return;
@@ -173,20 +160,9 @@ export async function dispatchDirectPhase(ctx, pi, phase, base) {
173
160
  // incomplete) slice. After slice completion, state.activeSlice advances
174
161
  // to the next incomplete slice, so we find the last done slice from the
175
162
  // roadmap instead (#1693).
176
- let uatCompletedSliceIds = [];
177
- if (isDbAvailable()) {
178
- uatCompletedSliceIds = getMilestoneSlices(mid).filter(s => s.status === "complete").map(s => s.id);
179
- }
180
- if (uatCompletedSliceIds.length === 0) {
181
- // File-based fallback: parse roadmap checkboxes
182
- const roadmapPath = resolveMilestoneFile(dispatchBase, mid, "ROADMAP");
183
- if (roadmapPath) {
184
- const roadmapContent = await loadFile(roadmapPath);
185
- if (roadmapContent) {
186
- uatCompletedSliceIds = parseRoadmap(roadmapContent).slices.filter(s => s.done).map(s => s.id);
187
- }
188
- }
189
- }
163
+ // DB-authoritative read (ADR-017) — no markdown fallback for dispatch
164
+ // decisions.
165
+ const uatCompletedSliceIds = isDbAvailable() ? getClosedSliceIds(mid) : [];
190
166
  if (uatCompletedSliceIds.length === 0) {
191
167
  ctx.ui.notify("Cannot dispatch run-uat: no completed slices.", "warning");
192
168
  return;
@@ -2,11 +2,10 @@
2
2
  // File Purpose: Declarative auto-mode dispatch rules and dispatch resolver.
3
3
  import { loadFile, extractUatType, loadActiveOverrides } from "./files.js";
4
4
  import { getUatBrowserToolSupportError } from "./uat-policy.js";
5
- import { isDbAvailable, getMilestoneSlices, getPendingGatesForTurn, markPendingGatesOmittedForTurn, getMilestone, insertArtifact, insertAssessment, setSliceSketchFlag, transaction, getAssessment, } from "./gsd-db.js";
5
+ import { isDbAvailable, getMilestoneSlices, getMilestoneSliceSummaries, getClosedSliceIds, getPendingGatesForTurn, markPendingGatesOmittedForTurn, getMilestone, insertArtifact, insertAssessment, setSliceSketchFlag, transaction, getAssessment, } from "./gsd-db.js";
6
6
  import { isClosedStatus } from "./status-guards.js";
7
7
  import { extractVerdict, isAcceptableUatVerdict } from "./verdict-parser.js";
8
8
  import { gsdRoot, resolveGsdPathContract, resolveMilestoneFile, resolveMilestonePath, resolveSliceFile, resolveSlicePath, resolveTaskFile, relTaskFile, relSliceFile, buildMilestoneFileName, buildSliceFileName, buildTaskFileName, gsdProjectionRoot, } from "./paths.js";
9
- import { parseRoadmap } from "./parsers-legacy.js";
10
9
  import { validateArtifact } from "./schemas/validate.js";
11
10
  import { existsSync, mkdirSync, readFileSync, writeFileSync, unlinkSync, readdirSync } from "node:fs";
12
11
  import { logWarning, logError } from "./workflow-logger.js";
@@ -19,10 +18,14 @@ import { selectReactiveDispatchBatch } from "./uok/execution-graph.js";
19
18
  import { getMilestonePipelineVariant } from "./milestone-scope-classifier.js";
20
19
  import { EXECUTION_ENTRY_PHASES, hasFinalizedMilestoneContext } from "./uok/plan-v2.js";
21
20
  import { isAutoActive } from "./auto.js";
22
- import { markDepthVerified } from "./bootstrap/write-gate.js";
21
+ // Host adapter explicitly: auto-dispatch runs in the extension host, and the
22
+ // ambient write-gate exports env-sniff the adapter per call (they are reserved
23
+ // for the workflow MCP child's dynamic-import surface).
24
+ import { hostWriteGateAdapter } from "./bootstrap/write-gate.js";
23
25
  import { ensureWorkflowPreferencesCaptured } from "./planning-depth.js";
24
26
  import { MILESTONE_ID_RE } from "./milestone-ids.js";
25
- import { getWorkflowTransportSupportError, getRequiredWorkflowToolsForAutoUnit, } from "./workflow-mcp.js";
27
+ import { getWorkflowTransportSupportError, getRequiredWorkflowToolsForAutoUnit, resolveWorkflowMcpProjectRoot, } from "./workflow-mcp.js";
28
+ import { prepareBrowserDaemonForUat } from "./browser-daemon-auto-prep.js";
26
29
  import { PROJECT_RESEARCH_INFLIGHT_MARKER, } from "./project-research-policy.js";
27
30
  import { isWorkflowPrefsCaptured, resolveDeepProjectSetupState, } from "./deep-project-setup-policy.js";
28
31
  import { annotateBackgroundable } from "./delegation-policy.js";
@@ -289,30 +292,13 @@ function persistSliceAssessmentBackfill(assessmentRelPath, mid, sliceId, content
289
292
  });
290
293
  }
291
294
  function backfillMissingAssessmentsFromSummaries(basePath, mid) {
292
- const completedSliceIds = new Set();
293
- if (isDbAvailable()) {
294
- for (const slice of getMilestoneSlices(mid)) {
295
- if (slice.status === "complete" || slice.status === "done") {
296
- completedSliceIds.add(slice.id);
297
- }
298
- }
299
- }
300
- else {
301
- const roadmapFile = resolveMilestoneFile(basePath, mid, "ROADMAP");
302
- if (!roadmapFile)
303
- return;
304
- try {
305
- const roadmap = parseRoadmap(readFileSync(roadmapFile, "utf-8"));
306
- for (const slice of roadmap.slices) {
307
- if (slice.done)
308
- completedSliceIds.add(slice.id);
309
- }
310
- }
311
- catch {
312
- return;
313
- }
314
- }
315
- for (const sliceId of completedSliceIds) {
295
+ // DB-authoritative (ADR-017): no markdown fallback. Without DB rows there
296
+ // is nothing to backfill.
297
+ if (!isDbAvailable())
298
+ return;
299
+ // Canonical closed vocabulary (complete/done/skipped/closed) — a skipped or
300
+ // closed slice with a SUMMARY gets the same assessment backfill treatment.
301
+ for (const sliceId of getClosedSliceIds(mid)) {
316
302
  const summaryPath = resolveSliceFile(basePath, mid, sliceId, "SUMMARY");
317
303
  if (!summaryPath || !existsSync(summaryPath))
318
304
  continue;
@@ -488,7 +474,7 @@ export const DISPATCH_RULES = [
488
474
  // deadlock. Deep planning is still user-driven even inside auto-mode,
489
475
  // so it must wait for explicit approval instead of taking this bypass.
490
476
  if (shouldBypassMilestoneDepthGateInAuto(prefs)) {
491
- markDepthVerified(mid, basePath);
477
+ hostWriteGateAdapter.markDepthVerified(mid, basePath);
492
478
  }
493
479
  return {
494
480
  action: "dispatch",
@@ -540,6 +526,16 @@ export const DISPATCH_RULES = [
540
526
  if (browserToolError) {
541
527
  return { action: "stop", reason: browserToolError, level: "warning" };
542
528
  }
529
+ const browserDaemonError = prepareBrowserDaemonForUat({
530
+ uatType,
531
+ sessionProvider,
532
+ sessionAuthMode,
533
+ sessionBaseUrl,
534
+ projectRoot: resolveWorkflowMcpProjectRoot(basePath),
535
+ });
536
+ if (browserDaemonError) {
537
+ return { action: "stop", reason: browserDaemonError, level: "warning" };
538
+ }
543
539
  // Cap run-uat dispatch attempts to prevent infinite replay (#3624).
544
540
  // Check before incrementing so an exhausted counter cannot create a
545
541
  // no-progress skip loop that starves later dispatch rules.
@@ -569,24 +565,11 @@ export const DISPATCH_RULES = [
569
565
  // Only applies when UAT dispatch is enabled
570
566
  if (!prefs?.uat_dispatch)
571
567
  return null;
572
- // DB-first: prefer closed slices from DB; fall back to ROADMAP on disk.
573
- let closedSliceIds;
574
- if (isDbAvailable()) {
575
- closedSliceIds = getMilestoneSlices(mid)
576
- .filter(s => isClosedStatus(s.status))
577
- .map(s => s.id);
578
- }
579
- else {
580
- // Filesystem fallback for degraded / unmigrated projects.
581
- // `slice.done` in the parsed ROADMAP is the disk-level closed signal.
582
- const roadmapFile = resolveMilestoneFile(basePath, mid, "ROADMAP");
583
- const roadmapContent = roadmapFile ? await loadFile(roadmapFile) : null;
584
- if (!roadmapContent)
585
- return null;
586
- const roadmap = parseRoadmap(roadmapContent);
587
- closedSliceIds = roadmap.slices.filter(s => s.done).map(s => s.id);
588
- }
589
- for (const sliceId of closedSliceIds) {
568
+ // DB-authoritative (ADR-017): closed slices come from the DB only; the
569
+ // ROADMAP projection is never parsed for gate decisions.
570
+ if (!isDbAvailable())
571
+ return null;
572
+ for (const sliceId of getClosedSliceIds(mid)) {
590
573
  const result = await readUatGateVerdict(basePath, mid, sliceId);
591
574
  if (!result)
592
575
  continue;
@@ -641,7 +624,7 @@ export const DISPATCH_RULES = [
641
624
  // H6 fix (#4973): keep the non-deep auto-mode bypass, but do not
642
625
  // pre-verify deep planning's user-facing milestone approval gate.
643
626
  if (shouldBypassMilestoneDepthGateInAuto(prefs)) {
644
- markDepthVerified(mid, basePath);
627
+ hostWriteGateAdapter.markDepthVerified(mid, basePath);
645
628
  }
646
629
  return {
647
630
  action: "dispatch",
@@ -823,7 +806,7 @@ export const DISPATCH_RULES = [
823
806
  // H6 fix (#4973): keep the non-deep auto-mode bypass, but do not
824
807
  // pre-verify deep planning's user-facing milestone approval gate.
825
808
  if (shouldBypassMilestoneDepthGateInAuto(prefs)) {
826
- markDepthVerified(mid, basePath);
809
+ hostWriteGateAdapter.markDepthVerified(mid, basePath);
827
810
  }
828
811
  return {
829
812
  action: "dispatch",
@@ -905,18 +888,18 @@ export const DISPATCH_RULES = [
905
888
  // behavior.
906
889
  if (await getMilestonePipelineVariant(mid) === "trivial")
907
890
  return null;
908
- // Load roadmap to find all slices
909
- const roadmapFile = resolveExistingExpectedArtifact("plan-milestone", mid, basePath) ??
910
- resolveMilestoneFile(basePath, mid, "ROADMAP");
911
- const roadmapContent = roadmapFile ? await loadFile(roadmapFile) : null;
912
- if (!roadmapContent)
891
+ // DB-authoritative slice list (ADR-017): the ROADMAP projection is
892
+ // never parsed for dispatch decisions. No DB / no rows → skip this rule.
893
+ if (!isDbAvailable())
894
+ return null;
895
+ const dbSlices = getMilestoneSliceSummaries(mid);
896
+ if (dbSlices.length === 0)
913
897
  return null;
914
- const roadmap = parseRoadmap(roadmapContent);
915
898
  // Find slices that need research (no RESEARCH file, dependencies done)
916
899
  const milestoneResearchFile = resolveExistingExpectedArtifact("research-milestone", mid, basePath) ??
917
900
  resolveMilestoneFile(basePath, mid, "RESEARCH");
918
901
  const researchReadySlices = [];
919
- for (const slice of roadmap.slices) {
902
+ for (const slice of dbSlices) {
920
903
  if (slice.done)
921
904
  continue;
922
905
  // Skip S01 when milestone research exists
@@ -926,7 +909,7 @@ export const DISPATCH_RULES = [
926
909
  if (resolveExistingExpectedArtifact("research-slice", `${mid}/${slice.id}`, basePath))
927
910
  continue;
928
911
  // Skip if dependencies aren't done (check for SUMMARY files)
929
- const depsComplete = (slice.depends ?? []).every((depId) => !!resolveExistingExpectedArtifact("complete-slice", `${mid}/${depId}`, basePath));
912
+ const depsComplete = slice.depends.every((depId) => !!resolveExistingExpectedArtifact("complete-slice", `${mid}/${depId}`, basePath));
930
913
  if (!depsComplete)
931
914
  continue;
932
915
  researchReadySlices.push({ id: slice.id, title: slice.title });
@@ -839,15 +839,34 @@ export function resolveModelId(modelId, availableModels, currentProvider) {
839
839
  if (providerMatch)
840
840
  return providerMatch;
841
841
  }
842
- // Prefer "anthropic" as the canonical provider for Anthropic models.
843
- // Transport-specific tiebreaker (ADR-012): intentionally keys on provider,
844
- // not api we want the plain Anthropic transport when multiple are available.
845
- const anthropicMatch = candidates.find(m => m.provider === "anthropic");
846
- if (anthropicMatch)
847
- return anthropicMatch;
842
+ // Subscription/OAuth routes beat pay-per-token API when the same model ID
843
+ // exists on multiple providers. Order matters — first match wins.
844
+ for (const provider of BARE_ID_SUBSCRIPTION_PROVIDER_PRECEDENCE) {
845
+ const match = candidates.find(m => m.provider === provider);
846
+ if (match)
847
+ return match;
848
+ }
848
849
  // Fall back to first non-extension candidate, or any candidate
849
850
  return candidates.find(m => !EXTENSION_PROVIDERS.has(m.provider)) ?? candidates[0];
850
851
  }
852
+ /**
853
+ * When a bare model ID exists on multiple providers, prefer subscription/OAuth
854
+ * routes over pay-per-token API keys. Matches PROVIDER_ROUTES in doctor-providers
855
+ * but applies when *both* sides are authenticated.
856
+ *
857
+ * Order rationale:
858
+ * - openai-codex before github-copilot: ChatGPT-native for shared GPT IDs
859
+ * - google-gemini-cli before github-copilot: first-party Gemini CLI
860
+ * - anthropic before github-copilot: first-party Claude API/OAuth over Copilot
861
+ * - github-copilot before openai/google: Copilot OAuth over platform API keys
862
+ */
863
+ export const BARE_ID_SUBSCRIPTION_PROVIDER_PRECEDENCE = [
864
+ "openai-codex",
865
+ "google-gemini-cli",
866
+ "anthropic",
867
+ "github-copilot",
868
+ "google-antigravity",
869
+ ];
851
870
  /**
852
871
  * Flat-rate providers charge the same per request regardless of model.
853
872
  * Dynamic routing provides no cost benefit — it only degrades quality (#3453).
@@ -16,7 +16,7 @@ import { deriveState } from "./state.js";
16
16
  import { logWarning, logError } from "./workflow-logger.js";
17
17
  import { loadFile, parseSummary, resolveAllOverrides } from "./files.js";
18
18
  import { loadPrompt } from "./prompt-loader.js";
19
- import { isAwaitingUserInput } from "./user-input-boundary.js";
19
+ import { isAwaitingUserInput } from "./consent-question.js";
20
20
  import { resolveMilestonePath, resolveSliceFile, resolveSlicePath, resolveTaskFile, resolveMilestoneFile, resolveTasksDir, buildTaskFileName, } from "./paths.js";
21
21
  import { invalidateAllCaches } from "./cache.js";
22
22
  import { rebuildState } from "./doctor.js";
@@ -30,8 +30,7 @@ import { createWorkspace, scopeMilestone } from "./workspace.js";
30
30
  import { normalizeWorktreePathForCompare } from "./worktree-root.js";
31
31
  import { isDbAvailable, getTask, getSlice, getMilestone, getMilestoneSlices, updateTaskStatus, _getAdapter, getVerificationEvidence } from "./gsd-db.js";
32
32
  import { getWorkflowDatabasePath, refreshWorkflowDatabaseFromDisk } from "./db-workspace.js";
33
- import { renderPlanCheckboxes, renderRoadmapFromDb } from "./markdown-renderer.js";
34
- import { parseRoadmap as parseLegacyRoadmap } from "./parsers-legacy.js";
33
+ import { renderPlanCheckboxes, renderRoadmapFromDb, roadmapRenderMarksSliceDone } from "./markdown-renderer.js";
35
34
  import { consumeSignal } from "./session-status-io.js";
36
35
  import { checkPostUnitHooks, consumeHookFailure, isRetryPending, consumeRetryTrigger, consumeGateBlock, persistHookState, resolveHookArtifactPath, } from "./post-unit-hooks.js";
37
36
  import { hasPendingCaptures, loadPendingCaptures, revertExecutorResolvedCaptures } from "./captures.js";
@@ -55,11 +54,12 @@ import { writeTurnGitTransaction } from "./uok/gitops.js";
55
54
  import { isClosedStatus } from "./status-guards.js";
56
55
  import { detectAbandonMilestone } from "./abandon-detect.js";
57
56
  import { getPendingGate } from "./bootstrap/write-gate.js";
58
- import { isDeterministicPolicyError } from "./auto-tool-tracking.js";
57
+ import { isDeterministicPolicyError, isToolUnavailableError } from "./auto-tool-tracking.js";
59
58
  import { formatConnectedStepStack, formatPostUnitStatusCard } from "./auto-status-message.js";
60
59
  import { clearProjectResearchInflightMarker, finalizeProjectResearchTimeout, } from "./project-research-policy.js";
61
60
  import { validateArtifact } from "./schemas/validate.js";
62
61
  import { verificationRetryKey } from "./auto/verification-retry-policy.js";
62
+ import { saveCustomVerifyRetryCounts } from "./auto/custom-verify-retry-store.js";
63
63
  import { getLedger } from "./metrics.js";
64
64
  import { getUnitCostSpikeAction, resolveUnitCostSpikeMultiplier } from "./auto-budget.js";
65
65
  import { resolveCanonicalMilestoneRoot } from "./worktree-manager.js";
@@ -722,7 +722,7 @@ export const USER_DRIVEN_DEEP_UNITS = new Set([
722
722
  "discuss-milestone",
723
723
  "research-decision",
724
724
  ]);
725
- export { isAwaitingUserInput } from "./user-input-boundary.js";
725
+ export { isAwaitingUserInput } from "./consent-question.js";
726
726
  function artifactValidationKind(unitType) {
727
727
  if (unitType === "discuss-project")
728
728
  return "project";
@@ -800,11 +800,13 @@ async function repairCompleteSliceRoadmapProjection(unitType, unitId, basePath)
800
800
  if (!summaryPath || !existsSync(summaryPath) || !uatPath || !existsSync(uatPath)) {
801
801
  return false;
802
802
  }
803
+ // Stale-render detection (ADR-017): the DB already says the slice is closed;
804
+ // this only checks whether the rendered ROADMAP projection reflects it, to
805
+ // decide whether a repair re-render is needed.
803
806
  const roadmapPath = resolveMilestoneFile(artifactBase, mid, "ROADMAP");
804
807
  if (roadmapPath && existsSync(roadmapPath)) {
805
808
  try {
806
- const roadmap = parseLegacyRoadmap(readFileSync(roadmapPath, "utf-8"));
807
- if (roadmap.slices.find((roadmapSlice) => roadmapSlice.id === sid)?.done) {
809
+ if (roadmapRenderMarksSliceDone(readFileSync(roadmapPath, "utf-8"), sid)) {
808
810
  return false;
809
811
  }
810
812
  }
@@ -1701,7 +1703,16 @@ export async function postUnitPreVerification(pctx, opts) {
1701
1703
  ctx.ui.notify(`Artifact missing for ${s.currentUnit.type} ${s.currentUnit.id} — DB unavailable, skipping retry.${dbSkipDiag ? ` Expected: ${dbSkipDiag}` : ""}`, "error");
1702
1704
  }
1703
1705
  else if (!triggerArtifactVerified) {
1704
- if (s.lastToolInvocationError) {
1706
+ if (s.lastToolInvocationError && isToolUnavailableError(s.lastToolInvocationError)) {
1707
+ // Tool-unavailable is the one transient invocation error: the
1708
+ // workflow MCP server registers its surface asynchronously, so a
1709
+ // Unit's first call can race the registration. Fall through to the
1710
+ // bounded verification retry instead of pausing.
1711
+ debugLog("postUnit", { phase: "tool-unavailable-retry", unitType: s.currentUnit.type, unitId: s.currentUnit.id, error: s.lastToolInvocationError });
1712
+ ctx.ui.notify(`Tool unavailable for ${s.currentUnit.type}: ${s.lastToolInvocationError}. The tool surface may still be registering — retrying.`, "warning");
1713
+ s.lastToolInvocationError = null;
1714
+ }
1715
+ else if (s.lastToolInvocationError) {
1705
1716
  const isUserSkip = /queued user message/i.test(s.lastToolInvocationError);
1706
1717
  const errMsg = isUserSkip
1707
1718
  ? `Tool skipped for ${s.currentUnit.type}: ${s.lastToolInvocationError}. Queued user message interrupted the turn — pausing auto-mode.`
@@ -1789,12 +1800,14 @@ export async function postUnitPreVerification(pctx, opts) {
1789
1800
  }
1790
1801
  }
1791
1802
  s.exhaustedVerificationUnits.add(retryKey);
1803
+ saveCustomVerifyRetryCounts(s, { logFailure: err => debugLog("postUnit", { phase: "save-verify-retries-failed", error: err instanceof Error ? err.message : String(err) }) });
1792
1804
  debugLog("postUnit", { phase: "artifact-verify-exhausted", unitType: s.currentUnit.type, unitId: s.currentUnit.id, attempt });
1793
1805
  ctx.ui.notify(`${failureDetails} Pausing auto-mode after ${MAX_ARTIFACT_VERIFICATION_RETRIES} retries.`, "error");
1794
1806
  await pauseAuto(ctx, pi);
1795
1807
  return "dispatched";
1796
1808
  }
1797
1809
  s.verificationRetryCount.set(retryKey, attempt);
1810
+ saveCustomVerifyRetryCounts(s, { logFailure: err => debugLog("postUnit", { phase: "save-verify-retries-failed", error: err instanceof Error ? err.message : String(err) }) });
1798
1811
  s.pendingVerificationRetry = {
1799
1812
  unitId: s.currentUnit.id,
1800
1813
  failureContext: `${failureDetails} (attempt ${attempt}/${MAX_ARTIFACT_VERIFICATION_RETRIES}).`,
@@ -1814,6 +1827,8 @@ export async function postUnitPreVerification(pctx, opts) {
1814
1827
  }
1815
1828
  s.verificationRetryCount.delete(retryKey);
1816
1829
  s.verificationRetryFailureHashes.delete(retryKey);
1830
+ s.exhaustedVerificationUnits.delete(retryKey);
1831
+ saveCustomVerifyRetryCounts(s, { logFailure: err => debugLog("postUnit", { phase: "save-verify-retries-failed", error: err instanceof Error ? err.message : String(err) }) });
1817
1832
  if (s.currentUnit.type === "complete-milestone") {
1818
1833
  const { milestone: mid } = parseUnitId(s.currentUnit.id);
1819
1834
  if (mid) {