@opengsd/gsd-pi 1.1.1-dev.b2556262 → 1.2.0-dev.4813ead6

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 (500) hide show
  1. package/dist/cli-web-branch.d.ts +2 -0
  2. package/dist/cli-web-branch.js +9 -2
  3. package/dist/help-text.js +5 -0
  4. package/dist/project-sessions.js +4 -2
  5. package/dist/resources/.managed-resources-content-hash +1 -1
  6. package/dist/resources/extensions/ask-user-questions.js +78 -23
  7. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +101 -237
  8. package/dist/resources/extensions/claude-code-cli/turn-assembler.js +224 -0
  9. package/dist/resources/extensions/github-sync/templates.js +3 -3
  10. package/dist/resources/extensions/gsd/artifact-projection.js +14 -0
  11. package/dist/resources/extensions/gsd/auto/contracts.js +8 -1
  12. package/dist/resources/extensions/gsd/auto/loop.js +74 -56
  13. package/dist/resources/extensions/gsd/auto/orchestrator.js +763 -63
  14. package/dist/resources/extensions/gsd/auto/phases.js +28 -3
  15. package/dist/resources/extensions/gsd/auto/run-unit.js +2 -1
  16. package/dist/resources/extensions/gsd/auto/session.js +3 -0
  17. package/dist/resources/extensions/gsd/auto-dashboard.js +16 -4
  18. package/dist/resources/extensions/gsd/auto-dispatch.js +6 -5
  19. package/dist/resources/extensions/gsd/auto-model-selection.js +8 -0
  20. package/dist/resources/extensions/gsd/auto-post-unit.js +4 -3
  21. package/dist/resources/extensions/gsd/auto-prompts.js +191 -9
  22. package/dist/resources/extensions/gsd/auto-recovery.js +48 -49
  23. package/dist/resources/extensions/gsd/auto-runtime-state.js +17 -0
  24. package/dist/resources/extensions/gsd/auto-start.js +12 -23
  25. package/dist/resources/extensions/gsd/auto-timers.js +16 -2
  26. package/dist/resources/extensions/gsd/auto-tool-tracking.js +37 -0
  27. package/dist/resources/extensions/gsd/auto-unit-tool-scope.js +33 -29
  28. package/dist/resources/extensions/gsd/auto-verification.js +7 -7
  29. package/dist/resources/extensions/gsd/auto-worktree.js +45 -36
  30. package/dist/resources/extensions/gsd/auto.js +73 -471
  31. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +28 -37
  32. package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +11 -37
  33. package/dist/resources/extensions/gsd/bootstrap/query-tools.js +2 -2
  34. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +103 -138
  35. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +63 -4
  36. package/dist/resources/extensions/gsd/closeout-consistency-gate.js +21 -4
  37. package/dist/resources/extensions/gsd/codebase-generator.js +8 -4
  38. package/dist/resources/extensions/gsd/commands/handlers/auto.js +3 -0
  39. package/dist/resources/extensions/gsd/commands-handlers.js +20 -0
  40. package/dist/resources/extensions/gsd/commands-inspect.js +4 -8
  41. package/dist/resources/extensions/gsd/commands-maintenance.js +61 -41
  42. package/dist/resources/extensions/gsd/commands-ship.js +2 -2
  43. package/dist/resources/extensions/gsd/commands-verdict.js +12 -2
  44. package/dist/resources/extensions/gsd/db-workspace.js +103 -0
  45. package/dist/resources/extensions/gsd/debug-logger.js +10 -0
  46. package/dist/resources/extensions/gsd/delegation-policy.js +2 -10
  47. package/dist/resources/extensions/gsd/discussion-handoff.js +218 -0
  48. package/dist/resources/extensions/gsd/docs/preferences-reference.md +9 -0
  49. package/dist/resources/extensions/gsd/doctor-proactive.js +7 -2
  50. package/dist/resources/extensions/gsd/doctor.js +16 -9
  51. package/dist/resources/extensions/gsd/error-classifier.js +1 -1
  52. package/dist/resources/extensions/gsd/git-conflict-state.js +16 -1
  53. package/dist/resources/extensions/gsd/gsd-db.js +12 -0
  54. package/dist/resources/extensions/gsd/guided-flow.js +36 -470
  55. package/dist/resources/extensions/gsd/guided-unit-completion.js +225 -0
  56. package/dist/resources/extensions/gsd/markdown-renderer.js +33 -33
  57. package/dist/resources/extensions/gsd/mcp-filter.js +8 -1
  58. package/dist/resources/extensions/gsd/mcp-tool-name.js +26 -0
  59. package/dist/resources/extensions/gsd/md-importer.js +4 -3
  60. package/dist/resources/extensions/gsd/migrate/safety.js +2 -2
  61. package/dist/resources/extensions/gsd/migration-auto-check.js +3 -2
  62. package/dist/resources/extensions/gsd/milestone-closeout-proof.js +72 -0
  63. package/dist/resources/extensions/gsd/milestone-closeout.js +12 -4
  64. package/dist/resources/extensions/gsd/milestone-merge-transaction.js +10 -0
  65. package/dist/resources/extensions/gsd/milestone-planning-persistence.js +156 -0
  66. package/dist/resources/extensions/gsd/milestone-readiness.js +77 -0
  67. package/dist/resources/extensions/gsd/milestone-settlement.js +50 -0
  68. package/dist/resources/extensions/gsd/milestone-validation-evidence.js +73 -0
  69. package/dist/resources/extensions/gsd/milestone-validation-verdict.js +57 -0
  70. package/dist/resources/extensions/gsd/native-git-bridge.js +45 -0
  71. package/dist/resources/extensions/gsd/parallel-eligibility.js +3 -6
  72. package/dist/resources/extensions/gsd/parallel-orchestrator.js +3 -2
  73. package/dist/resources/extensions/gsd/preferences-diagnostics.js +67 -0
  74. package/dist/resources/extensions/gsd/preferences.js +147 -29
  75. package/dist/resources/extensions/gsd/prompts/complete-slice.md +1 -0
  76. package/dist/resources/extensions/gsd/prompts/discuss.md +6 -7
  77. package/dist/resources/extensions/gsd/prompts/execute-task.md +2 -0
  78. package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +5 -7
  79. package/dist/resources/extensions/gsd/prompts/guided-discuss-project.md +6 -6
  80. package/dist/resources/extensions/gsd/prompts/guided-discuss-requirements.md +1 -2
  81. package/dist/resources/extensions/gsd/prompts/guided-discuss-slice.md +5 -6
  82. package/dist/resources/extensions/gsd/prompts/plan-milestone.md +2 -0
  83. package/dist/resources/extensions/gsd/prompts/plan-slice.md +2 -1
  84. package/dist/resources/extensions/gsd/prompts/refine-slice.md +1 -0
  85. package/dist/resources/extensions/gsd/prompts/research-milestone.md +2 -2
  86. package/dist/resources/extensions/gsd/prompts/system.md +1 -1
  87. package/dist/resources/extensions/gsd/prompts/validate-milestone.md +5 -3
  88. package/dist/resources/extensions/gsd/provider-payload-policy.js +83 -0
  89. package/dist/resources/extensions/gsd/pull-request-process.js +13 -0
  90. package/dist/resources/extensions/gsd/quality-gate-closure.js +109 -0
  91. package/dist/resources/extensions/gsd/question-transport.js +86 -0
  92. package/dist/resources/extensions/gsd/roadmap-slices.js +8 -2
  93. package/dist/resources/extensions/gsd/schemas/parsers.js +6 -1
  94. package/dist/resources/extensions/gsd/slice-parallel-orchestrator.js +3 -2
  95. package/dist/resources/extensions/gsd/state-reconciliation/drift/artifact-db.js +21 -1
  96. package/dist/resources/extensions/gsd/state.js +13 -5
  97. package/dist/resources/extensions/gsd/templates/plan.md +7 -0
  98. package/dist/resources/extensions/gsd/templates/project.md +1 -0
  99. package/dist/resources/extensions/gsd/templates/roadmap.md +1 -1
  100. package/dist/resources/extensions/gsd/templates/uat.md +5 -1
  101. package/dist/resources/extensions/gsd/tool-contract.js +52 -8
  102. package/dist/resources/extensions/gsd/tool-presentation-plan.js +15 -34
  103. package/dist/resources/extensions/gsd/tool-surface-snapshot.js +17 -0
  104. package/dist/resources/extensions/gsd/tools/plan-milestone.js +15 -143
  105. package/dist/resources/extensions/gsd/tools/reassess-roadmap.js +39 -0
  106. package/dist/resources/extensions/gsd/tools/validate-milestone.js +15 -78
  107. package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +169 -20
  108. package/dist/resources/extensions/gsd/uat-policy.js +16 -10
  109. package/dist/resources/extensions/gsd/uat-run.js +9 -14
  110. package/dist/resources/extensions/gsd/unit-context-composer.js +40 -20
  111. package/dist/resources/extensions/gsd/unit-runtime.js +3 -2
  112. package/dist/resources/extensions/gsd/unit-tool-contracts.js +2 -1
  113. package/dist/resources/extensions/gsd/user-input-boundary.js +65 -4
  114. package/dist/resources/extensions/gsd/validation-block-guard.js +2 -0
  115. package/dist/resources/extensions/gsd/web-app-uat.js +80 -0
  116. package/dist/resources/extensions/gsd/workflow-mcp.js +15 -102
  117. package/dist/resources/extensions/gsd/workflow-reconcile.js +4 -3
  118. package/dist/resources/extensions/gsd/workflow-tool-surface.js +46 -0
  119. package/dist/resources/extensions/gsd/workspace-git-guard.js +2 -0
  120. package/dist/resources/extensions/gsd/worktree-state-projection.js +33 -4
  121. package/dist/resources/extensions/gsd/worktree-telemetry.js +12 -0
  122. package/dist/resources/extensions/shared/interview-ui.js +2 -2
  123. package/dist/resources/shared/claude-runtime-floor.js +182 -0
  124. package/dist/tsconfig.extensions.tsbuildinfo +1 -0
  125. package/dist/update-cmd.js +20 -0
  126. package/dist/web/standalone/.next/BUILD_ID +1 -1
  127. package/dist/web/standalone/.next/app-path-routes-manifest.json +5 -5
  128. package/dist/web/standalone/.next/build-manifest.json +3 -3
  129. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  130. package/dist/web/standalone/.next/react-loadable-manifest.json +8 -8
  131. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  132. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  133. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  134. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  135. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  136. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  137. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  138. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  139. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  140. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  141. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  142. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  143. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  144. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  145. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  146. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  147. package/dist/web/standalone/.next/server/app/api/boot/route.js.nft.json +1 -1
  148. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js.nft.json +1 -1
  149. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js.nft.json +1 -1
  150. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js.nft.json +1 -1
  151. package/dist/web/standalone/.next/server/app/api/captures/route.js.nft.json +1 -1
  152. package/dist/web/standalone/.next/server/app/api/cleanup/route.js.nft.json +1 -1
  153. package/dist/web/standalone/.next/server/app/api/doctor/route.js.nft.json +1 -1
  154. package/dist/web/standalone/.next/server/app/api/export-data/route.js.nft.json +1 -1
  155. package/dist/web/standalone/.next/server/app/api/files/route.js.nft.json +1 -1
  156. package/dist/web/standalone/.next/server/app/api/forensics/route.js.nft.json +1 -1
  157. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  158. package/dist/web/standalone/.next/server/app/api/git/route.js.nft.json +1 -1
  159. package/dist/web/standalone/.next/server/app/api/history/route.js.nft.json +1 -1
  160. package/dist/web/standalone/.next/server/app/api/hooks/route.js.nft.json +1 -1
  161. package/dist/web/standalone/.next/server/app/api/inspect/route.js.nft.json +1 -1
  162. package/dist/web/standalone/.next/server/app/api/knowledge/route.js.nft.json +1 -1
  163. package/dist/web/standalone/.next/server/app/api/live-state/route.js.nft.json +1 -1
  164. package/dist/web/standalone/.next/server/app/api/mcp-connections/route.js.nft.json +1 -1
  165. package/dist/web/standalone/.next/server/app/api/notifications/route.js.nft.json +1 -1
  166. package/dist/web/standalone/.next/server/app/api/onboarding/route.js.nft.json +1 -1
  167. package/dist/web/standalone/.next/server/app/api/projects/route.js.nft.json +1 -1
  168. package/dist/web/standalone/.next/server/app/api/recovery/route.js.nft.json +1 -1
  169. package/dist/web/standalone/.next/server/app/api/session/browser/route.js.nft.json +1 -1
  170. package/dist/web/standalone/.next/server/app/api/session/command/route.js.nft.json +1 -1
  171. package/dist/web/standalone/.next/server/app/api/session/events/route.js.nft.json +1 -1
  172. package/dist/web/standalone/.next/server/app/api/session/manage/route.js.nft.json +1 -1
  173. package/dist/web/standalone/.next/server/app/api/settings-data/route.js.nft.json +1 -1
  174. package/dist/web/standalone/.next/server/app/api/shutdown/route.js.nft.json +1 -1
  175. package/dist/web/standalone/.next/server/app/api/skill-health/route.js.nft.json +1 -1
  176. package/dist/web/standalone/.next/server/app/api/steer/route.js.nft.json +1 -1
  177. package/dist/web/standalone/.next/server/app/api/switch-root/route.js.nft.json +1 -1
  178. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js.nft.json +1 -1
  179. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js.nft.json +1 -1
  180. package/dist/web/standalone/.next/server/app/api/undo/route.js.nft.json +1 -1
  181. package/dist/web/standalone/.next/server/app/api/visualizer/route.js.nft.json +1 -1
  182. package/dist/web/standalone/.next/server/app/index.html +1 -1
  183. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  184. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  185. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  186. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  187. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  188. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  189. package/dist/web/standalone/.next/server/app-paths-manifest.json +5 -5
  190. package/dist/web/standalone/.next/server/chunks/5047.js +2 -0
  191. package/dist/web/standalone/.next/server/chunks/5124.js +1 -0
  192. package/dist/web/standalone/.next/server/chunks/8357.js +2 -2
  193. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  194. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  195. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  196. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  197. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  198. package/dist/web/standalone/.next/static/chunks/2659.b7b129ee6a769448.js +1 -0
  199. package/dist/web/standalone/.next/static/chunks/2772.bfa657f49f955239.js +1 -0
  200. package/dist/web/standalone/.next/static/chunks/{3616.4113d484a994e411.js → 3616.3c60753b8ffcbd2e.js} +1 -1
  201. package/dist/web/standalone/.next/static/chunks/4283.e4873b058df143a1.js +2 -0
  202. package/dist/web/standalone/.next/static/chunks/5826.a46ecdd1cfe8dabc.js +1 -0
  203. package/dist/web/standalone/.next/static/chunks/796.cf859a427a2cb2ac.js +10 -0
  204. package/dist/web/standalone/.next/static/chunks/8785.2e5a118797fb2dd2.js +1 -0
  205. package/dist/web/standalone/.next/static/chunks/{webpack-dda80a1ef5587410.js → webpack-fbea77b5f9953368.js} +1 -1
  206. package/dist/web/standalone/node_modules/@gsd/native/package.json +1 -1
  207. package/dist/web/standalone/node_modules/node-pty/build/Makefile +1 -1
  208. package/dist/web/standalone/node_modules/postcss/lib/container.js +26 -18
  209. package/dist/web/standalone/node_modules/postcss/lib/css-syntax-error.js +47 -14
  210. package/dist/web/standalone/node_modules/postcss/lib/declaration.js +4 -4
  211. package/dist/web/standalone/node_modules/postcss/lib/fromJSON.js +3 -3
  212. package/dist/web/standalone/node_modules/postcss/lib/input.js +54 -29
  213. package/dist/web/standalone/node_modules/postcss/lib/lazy-result.js +47 -37
  214. package/dist/web/standalone/node_modules/postcss/lib/map-generator.js +26 -9
  215. package/dist/web/standalone/node_modules/postcss/lib/no-work-result.js +57 -55
  216. package/dist/web/standalone/node_modules/postcss/lib/node.js +99 -31
  217. package/dist/web/standalone/node_modules/postcss/lib/parse.js +1 -1
  218. package/dist/web/standalone/node_modules/postcss/lib/parser.js +10 -9
  219. package/dist/web/standalone/node_modules/postcss/lib/postcss.js +12 -12
  220. package/dist/web/standalone/node_modules/postcss/lib/previous-map.js +30 -11
  221. package/dist/web/standalone/node_modules/postcss/lib/processor.js +7 -7
  222. package/dist/web/standalone/node_modules/postcss/lib/result.js +5 -5
  223. package/dist/web/standalone/node_modules/postcss/lib/rule.js +6 -6
  224. package/dist/web/standalone/node_modules/postcss/lib/stringifier.js +69 -28
  225. package/dist/web/standalone/node_modules/postcss/lib/tokenize.js +6 -2
  226. package/dist/web/standalone/node_modules/postcss/package.json +48 -48
  227. package/dist/web-mode.d.ts +2 -0
  228. package/dist/web-mode.js +20 -8
  229. package/package.json +17 -11
  230. package/packages/cloud-mcp-gateway/package.json +2 -2
  231. package/packages/contracts/package.json +1 -1
  232. package/packages/daemon/package.json +4 -4
  233. package/packages/gsd-agent-core/dist/session/agent-session-extensions.d.ts +2 -0
  234. package/packages/gsd-agent-core/dist/session/agent-session-extensions.d.ts.map +1 -1
  235. package/packages/gsd-agent-core/dist/session/agent-session-extensions.js +14 -0
  236. package/packages/gsd-agent-core/dist/session/agent-session-extensions.js.map +1 -1
  237. package/packages/gsd-agent-core/package.json +5 -5
  238. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  239. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js +3 -0
  240. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js.map +1 -1
  241. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js +1 -1
  242. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js.map +1 -1
  243. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  244. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js +106 -40
  245. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  246. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-extension-widgets.d.ts.map +1 -1
  247. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-extension-widgets.js +6 -0
  248. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-extension-widgets.js.map +1 -1
  249. package/packages/gsd-agent-modes/package.json +7 -7
  250. package/packages/mcp-server/dist/server.d.ts +10 -0
  251. package/packages/mcp-server/dist/server.d.ts.map +1 -1
  252. package/packages/mcp-server/dist/server.js +8 -0
  253. package/packages/mcp-server/dist/server.js.map +1 -1
  254. package/packages/mcp-server/dist/workflow-tools.d.ts +41 -0
  255. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  256. package/packages/mcp-server/dist/workflow-tools.js +2 -1
  257. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  258. package/packages/mcp-server/package.json +3 -3
  259. package/packages/native/package.json +1 -1
  260. package/packages/pi-agent-core/package.json +1 -1
  261. package/packages/pi-ai/dist/image-models.generated.d.ts +30 -0
  262. package/packages/pi-ai/dist/image-models.generated.d.ts.map +1 -1
  263. package/packages/pi-ai/dist/image-models.generated.js +30 -0
  264. package/packages/pi-ai/dist/image-models.generated.js.map +1 -1
  265. package/packages/pi-ai/dist/models.generated.d.ts +8 -127
  266. package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
  267. package/packages/pi-ai/dist/models.generated.js +47 -166
  268. package/packages/pi-ai/dist/models.generated.js.map +1 -1
  269. package/packages/pi-ai/package.json +1 -1
  270. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts.map +1 -1
  271. package/packages/pi-coding-agent/dist/core/auth-storage.js +11 -3
  272. package/packages/pi-coding-agent/dist/core/auth-storage.js.map +1 -1
  273. package/packages/pi-coding-agent/package.json +7 -7
  274. package/packages/pi-tui/dist/components/input.js +1 -1
  275. package/packages/pi-tui/dist/components/input.js.map +1 -1
  276. package/packages/pi-tui/dist/keys.d.ts.map +1 -1
  277. package/packages/pi-tui/dist/keys.js +39 -30
  278. package/packages/pi-tui/dist/keys.js.map +1 -1
  279. package/packages/pi-tui/dist/stdin-buffer.d.ts.map +1 -1
  280. package/packages/pi-tui/dist/stdin-buffer.js +22 -0
  281. package/packages/pi-tui/dist/stdin-buffer.js.map +1 -1
  282. package/packages/pi-tui/package.json +2 -2
  283. package/packages/rpc-client/package.json +2 -2
  284. package/pkg/package.json +1 -1
  285. package/scripts/install/deps.js +10 -0
  286. package/scripts/link-workspace-packages.cjs +7 -40
  287. package/src/resources/extensions/ask-user-questions.ts +87 -24
  288. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +126 -289
  289. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +242 -2
  290. package/src/resources/extensions/claude-code-cli/turn-assembler.ts +287 -0
  291. package/src/resources/extensions/github-sync/templates.ts +3 -3
  292. package/src/resources/extensions/github-sync/tests/templates.test.ts +2 -2
  293. package/src/resources/extensions/gsd/artifact-projection.ts +31 -0
  294. package/src/resources/extensions/gsd/auto/contracts.ts +40 -121
  295. package/src/resources/extensions/gsd/auto/loop-deps.ts +2 -0
  296. package/src/resources/extensions/gsd/auto/loop.ts +83 -61
  297. package/src/resources/extensions/gsd/auto/orchestrator.ts +913 -64
  298. package/src/resources/extensions/gsd/auto/phases.ts +35 -3
  299. package/src/resources/extensions/gsd/auto/run-unit.ts +2 -1
  300. package/src/resources/extensions/gsd/auto/session.ts +4 -0
  301. package/src/resources/extensions/gsd/auto-dashboard.ts +18 -4
  302. package/src/resources/extensions/gsd/auto-dispatch.ts +20 -7
  303. package/src/resources/extensions/gsd/auto-model-selection.ts +8 -0
  304. package/src/resources/extensions/gsd/auto-post-unit.ts +4 -3
  305. package/src/resources/extensions/gsd/auto-prompts.ts +220 -9
  306. package/src/resources/extensions/gsd/auto-recovery.ts +50 -50
  307. package/src/resources/extensions/gsd/auto-runtime-state.ts +30 -0
  308. package/src/resources/extensions/gsd/auto-start.ts +17 -20
  309. package/src/resources/extensions/gsd/auto-timers.ts +16 -2
  310. package/src/resources/extensions/gsd/auto-tool-tracking.ts +40 -0
  311. package/src/resources/extensions/gsd/auto-unit-tool-scope.ts +42 -30
  312. package/src/resources/extensions/gsd/auto-verification.ts +7 -8
  313. package/src/resources/extensions/gsd/auto-worktree.ts +57 -42
  314. package/src/resources/extensions/gsd/auto.ts +96 -508
  315. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +29 -37
  316. package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +10 -37
  317. package/src/resources/extensions/gsd/bootstrap/query-tools.ts +2 -2
  318. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +120 -151
  319. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +107 -3
  320. package/src/resources/extensions/gsd/closeout-consistency-gate.ts +27 -5
  321. package/src/resources/extensions/gsd/codebase-generator.ts +9 -5
  322. package/src/resources/extensions/gsd/commands/handlers/auto.ts +3 -0
  323. package/src/resources/extensions/gsd/commands-handlers.ts +18 -0
  324. package/src/resources/extensions/gsd/commands-inspect.ts +7 -8
  325. package/src/resources/extensions/gsd/commands-maintenance.ts +74 -40
  326. package/src/resources/extensions/gsd/commands-ship.ts +2 -2
  327. package/src/resources/extensions/gsd/commands-verdict.ts +19 -2
  328. package/src/resources/extensions/gsd/db-workspace.ts +170 -0
  329. package/src/resources/extensions/gsd/debug-logger.ts +11 -0
  330. package/src/resources/extensions/gsd/delegation-policy.ts +3 -11
  331. package/src/resources/extensions/gsd/discussion-handoff.ts +276 -0
  332. package/src/resources/extensions/gsd/docs/preferences-reference.md +9 -0
  333. package/src/resources/extensions/gsd/doctor-proactive.ts +8 -2
  334. package/src/resources/extensions/gsd/doctor.ts +15 -5
  335. package/src/resources/extensions/gsd/error-classifier.ts +1 -1
  336. package/src/resources/extensions/gsd/git-conflict-state.ts +17 -1
  337. package/src/resources/extensions/gsd/gsd-db.ts +12 -0
  338. package/src/resources/extensions/gsd/guided-flow.ts +49 -560
  339. package/src/resources/extensions/gsd/guided-unit-completion.ts +275 -0
  340. package/src/resources/extensions/gsd/markdown-renderer.ts +40 -20
  341. package/src/resources/extensions/gsd/mcp-filter.ts +9 -1
  342. package/src/resources/extensions/gsd/mcp-tool-name.ts +35 -0
  343. package/src/resources/extensions/gsd/md-importer.ts +3 -3
  344. package/src/resources/extensions/gsd/migrate/safety.ts +2 -2
  345. package/src/resources/extensions/gsd/migration-auto-check.ts +2 -2
  346. package/src/resources/extensions/gsd/milestone-closeout-proof.ts +131 -0
  347. package/src/resources/extensions/gsd/milestone-closeout.ts +12 -4
  348. package/src/resources/extensions/gsd/milestone-merge-transaction.ts +47 -0
  349. package/src/resources/extensions/gsd/milestone-planning-persistence.ts +224 -0
  350. package/src/resources/extensions/gsd/milestone-readiness.ts +125 -0
  351. package/src/resources/extensions/gsd/milestone-settlement.ts +81 -0
  352. package/src/resources/extensions/gsd/milestone-validation-evidence.ts +95 -0
  353. package/src/resources/extensions/gsd/milestone-validation-verdict.ts +80 -0
  354. package/src/resources/extensions/gsd/native-git-bridge.ts +48 -0
  355. package/src/resources/extensions/gsd/parallel-eligibility.ts +4 -5
  356. package/src/resources/extensions/gsd/parallel-orchestrator.ts +6 -2
  357. package/src/resources/extensions/gsd/preferences-diagnostics.ts +98 -0
  358. package/src/resources/extensions/gsd/preferences-types.ts +16 -0
  359. package/src/resources/extensions/gsd/preferences.ts +173 -28
  360. package/src/resources/extensions/gsd/prompts/complete-slice.md +1 -0
  361. package/src/resources/extensions/gsd/prompts/discuss.md +6 -7
  362. package/src/resources/extensions/gsd/prompts/execute-task.md +2 -0
  363. package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +5 -7
  364. package/src/resources/extensions/gsd/prompts/guided-discuss-project.md +6 -6
  365. package/src/resources/extensions/gsd/prompts/guided-discuss-requirements.md +1 -2
  366. package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +5 -6
  367. package/src/resources/extensions/gsd/prompts/plan-milestone.md +2 -0
  368. package/src/resources/extensions/gsd/prompts/plan-slice.md +2 -1
  369. package/src/resources/extensions/gsd/prompts/refine-slice.md +1 -0
  370. package/src/resources/extensions/gsd/prompts/research-milestone.md +2 -2
  371. package/src/resources/extensions/gsd/prompts/system.md +1 -1
  372. package/src/resources/extensions/gsd/prompts/validate-milestone.md +5 -3
  373. package/src/resources/extensions/gsd/provider-payload-policy.ts +140 -0
  374. package/src/resources/extensions/gsd/pull-request-process.ts +41 -0
  375. package/src/resources/extensions/gsd/quality-gate-closure.ts +140 -0
  376. package/src/resources/extensions/gsd/question-transport.ts +138 -0
  377. package/src/resources/extensions/gsd/roadmap-slices.ts +8 -2
  378. package/src/resources/extensions/gsd/schemas/parsers.ts +6 -1
  379. package/src/resources/extensions/gsd/slice-parallel-orchestrator.ts +6 -2
  380. package/src/resources/extensions/gsd/state-reconciliation/drift/artifact-db.ts +31 -10
  381. package/src/resources/extensions/gsd/state.ts +15 -5
  382. package/src/resources/extensions/gsd/templates/plan.md +7 -0
  383. package/src/resources/extensions/gsd/templates/project.md +1 -0
  384. package/src/resources/extensions/gsd/templates/roadmap.md +1 -1
  385. package/src/resources/extensions/gsd/templates/uat.md +5 -1
  386. package/src/resources/extensions/gsd/tests/artifact-db-drift-memo.test.ts +66 -0
  387. package/src/resources/extensions/gsd/tests/ask-user-questions-render.test.ts +92 -0
  388. package/src/resources/extensions/gsd/tests/auto-dashboard.test.ts +29 -1
  389. package/src/resources/extensions/gsd/tests/auto-dispatch-baseline-harness.test.ts +53 -0
  390. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +321 -5
  391. package/src/resources/extensions/gsd/tests/auto-milestone-target.test.ts +23 -0
  392. package/src/resources/extensions/gsd/tests/auto-model-selection-tool-poisoning.test.ts +18 -0
  393. package/src/resources/extensions/gsd/tests/auto-orchestrator.test.ts +709 -845
  394. package/src/resources/extensions/gsd/tests/auto-paused-ui-cleanup.test.ts +38 -10
  395. package/src/resources/extensions/gsd/tests/auto-runtime-state.test.ts +34 -0
  396. package/src/resources/extensions/gsd/tests/canonical-milestone-root.test.ts +20 -0
  397. package/src/resources/extensions/gsd/tests/codebase-generator.test.ts +22 -0
  398. package/src/resources/extensions/gsd/tests/commands-dispatcher-workspace-git.test.ts +11 -0
  399. package/src/resources/extensions/gsd/tests/commands-verdict.test.ts +38 -1
  400. package/src/resources/extensions/gsd/tests/debug-logger.test.ts +15 -0
  401. package/src/resources/extensions/gsd/tests/dispatch-complete-milestone-guard.test.ts +34 -3
  402. package/src/resources/extensions/gsd/tests/dispatch-run-uat-browser-tools.test.ts +88 -0
  403. package/src/resources/extensions/gsd/tests/doctor-scope-db-unavailable.test.ts +18 -0
  404. package/src/resources/extensions/gsd/tests/execute-summary-save-empty-project.test.ts +64 -1
  405. package/src/resources/extensions/gsd/tests/execute-task-rendering.test.ts +1 -0
  406. package/src/resources/extensions/gsd/tests/fixtures/pr-body/swarm-lane-no-blockers.md +1 -5
  407. package/src/resources/extensions/gsd/tests/fixtures/pr-body/swarm-lane-with-blockers.md +1 -5
  408. package/src/resources/extensions/gsd/tests/gate-state-canonicalization.test.ts +48 -1
  409. package/src/resources/extensions/gsd/tests/integration/merge-strategy-regular.test.ts +157 -0
  410. package/src/resources/extensions/gsd/tests/markdown-renderer-parse-cache.test.ts +75 -0
  411. package/src/resources/extensions/gsd/tests/mcp-tool-name.test.ts +34 -0
  412. package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +58 -0
  413. package/src/resources/extensions/gsd/tests/milestone-closeout-proof.test.ts +99 -0
  414. package/src/resources/extensions/gsd/tests/milestone-closeout.test.ts +25 -0
  415. package/src/resources/extensions/gsd/tests/milestone-merge-transaction.test.ts +46 -0
  416. package/src/resources/extensions/gsd/tests/milestone-readiness.test.ts +65 -0
  417. package/src/resources/extensions/gsd/tests/milestone-validation-evidence.test.ts +41 -0
  418. package/src/resources/extensions/gsd/tests/milestone-validation-verdict.test.ts +55 -0
  419. package/src/resources/extensions/gsd/tests/native-merge-regular.test.ts +139 -0
  420. package/src/resources/extensions/gsd/tests/orchestrator-legacy-parity.test.ts +127 -0
  421. package/src/resources/extensions/gsd/tests/parse-project-milestone-bridge.test.ts +77 -0
  422. package/src/resources/extensions/gsd/tests/plan-milestone.test.ts +45 -0
  423. package/src/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +6 -2
  424. package/src/resources/extensions/gsd/tests/planning-crossval.test.ts +45 -0
  425. package/src/resources/extensions/gsd/tests/preferences-diagnostics.test.ts +67 -0
  426. package/src/resources/extensions/gsd/tests/preferences.test.ts +183 -0
  427. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +75 -2
  428. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +9 -0
  429. package/src/resources/extensions/gsd/tests/provider-payload-policy.test.ts +165 -0
  430. package/src/resources/extensions/gsd/tests/pull-request-process.test.ts +47 -0
  431. package/src/resources/extensions/gsd/tests/register-hooks-depth-verification.test.ts +94 -0
  432. package/src/resources/extensions/gsd/tests/research-milestone-composer.test.ts +65 -0
  433. package/src/resources/extensions/gsd/tests/roadmap-parse-regression.test.ts +40 -0
  434. package/src/resources/extensions/gsd/tests/runtime-invariant-modules.test.ts +25 -1
  435. package/src/resources/extensions/gsd/tests/session-start-footer.test.ts +80 -0
  436. package/src/resources/extensions/gsd/tests/single-writer-invariant.test.ts +101 -1
  437. package/src/resources/extensions/gsd/tests/stale-queued-milestone.test.ts +27 -0
  438. package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +21 -6
  439. package/src/resources/extensions/gsd/tests/token-tool-gating.test.ts +38 -0
  440. package/src/resources/extensions/gsd/tests/tool-availability-audit.test.ts +35 -0
  441. package/src/resources/extensions/gsd/tests/tool-naming.test.ts +35 -42
  442. package/src/resources/extensions/gsd/tests/uat-policy.test.ts +23 -0
  443. package/src/resources/extensions/gsd/tests/unit-context-composer.test.ts +47 -0
  444. package/src/resources/extensions/gsd/tests/user-input-boundary.test.ts +147 -0
  445. package/src/resources/extensions/gsd/tests/validate-milestone-stuck-guard.test.ts +39 -0
  446. package/src/resources/extensions/gsd/tests/web-app-uat.test.ts +150 -0
  447. package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +126 -9
  448. package/src/resources/extensions/gsd/tests/workspace-git-preflight.test.ts +15 -0
  449. package/src/resources/extensions/gsd/tests/worktree-manager.test.ts +21 -0
  450. package/src/resources/extensions/gsd/tests/worktree-projection-writers.test.ts +1 -1
  451. package/src/resources/extensions/gsd/tests/worktree-safety.test.ts +24 -0
  452. package/src/resources/extensions/gsd/tests/worktree-telemetry.test.ts +22 -0
  453. package/src/resources/extensions/gsd/tests/write-gate-planning-unit.test.ts +15 -3
  454. package/src/resources/extensions/gsd/tests/write-gate.test.ts +79 -0
  455. package/src/resources/extensions/gsd/tool-contract.ts +86 -8
  456. package/src/resources/extensions/gsd/tool-presentation-plan.ts +16 -33
  457. package/src/resources/extensions/gsd/tool-surface-snapshot.ts +47 -0
  458. package/src/resources/extensions/gsd/tools/plan-milestone.ts +19 -160
  459. package/src/resources/extensions/gsd/tools/reassess-roadmap.ts +43 -0
  460. package/src/resources/extensions/gsd/tools/validate-milestone.ts +25 -84
  461. package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +183 -21
  462. package/src/resources/extensions/gsd/uat-policy.ts +19 -10
  463. package/src/resources/extensions/gsd/uat-run.ts +10 -14
  464. package/src/resources/extensions/gsd/unit-context-composer.ts +85 -20
  465. package/src/resources/extensions/gsd/unit-runtime.ts +3 -2
  466. package/src/resources/extensions/gsd/unit-tool-contracts.ts +2 -1
  467. package/src/resources/extensions/gsd/user-input-boundary.ts +55 -5
  468. package/src/resources/extensions/gsd/validation-block-guard.ts +2 -0
  469. package/src/resources/extensions/gsd/web-app-uat.ts +101 -0
  470. package/src/resources/extensions/gsd/workflow-mcp.ts +22 -110
  471. package/src/resources/extensions/gsd/workflow-reconcile.ts +3 -3
  472. package/src/resources/extensions/gsd/workflow-tool-surface.ts +73 -0
  473. package/src/resources/extensions/gsd/workspace-git-guard.ts +1 -0
  474. package/src/resources/extensions/gsd/worktree-lifecycle.ts +7 -16
  475. package/src/resources/extensions/gsd/worktree-state-projection.ts +55 -7
  476. package/src/resources/extensions/gsd/worktree-telemetry.ts +16 -0
  477. package/src/resources/extensions/shared/interview-ui.ts +15 -2
  478. package/src/resources/shared/claude-runtime-floor.ts +248 -0
  479. package/dist/web/standalone/.next/server/chunks/678.js +0 -2
  480. package/dist/web/standalone/.next/static/chunks/2659.feb6499ca863ebfc.js +0 -1
  481. package/dist/web/standalone/.next/static/chunks/2772.151789db0edea835.js +0 -1
  482. package/dist/web/standalone/.next/static/chunks/4283.10a065467b5340d8.js +0 -2
  483. package/dist/web/standalone/.next/static/chunks/5826.960dc4634cc9b0d3.js +0 -1
  484. package/dist/web/standalone/.next/static/chunks/796.46f811c0fac23aab.js +0 -10
  485. package/dist/web/standalone/.next/static/chunks/8785.d32f7a61f55c1600.js +0 -1
  486. package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/gsd-widget-prototype.d.ts +0 -21
  487. package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/gsd-widget-prototype.d.ts.map +0 -1
  488. package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/gsd-widget-prototype.js +0 -213
  489. package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/gsd-widget-prototype.js.map +0 -1
  490. package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/transcript-density-prototype.d.ts +0 -28
  491. package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/transcript-density-prototype.d.ts.map +0 -1
  492. package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/transcript-density-prototype.js +0 -249
  493. package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/transcript-density-prototype.js.map +0 -1
  494. package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/transcript-design-prototype.d.ts +0 -19
  495. package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/transcript-design-prototype.d.ts.map +0 -1
  496. package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/transcript-design-prototype.js +0 -797
  497. package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/transcript-design-prototype.js.map +0 -1
  498. package/scripts/ensure-workspace-builds.cjs +0 -129
  499. /package/dist/web/standalone/.next/static/{tJOKQbQRO-9MiFDO8DIDS → tkLHUSzPA2kMmWz4DmGwI}/_buildManifest.js +0 -0
  500. /package/dist/web/standalone/.next/static/{tJOKQbQRO-9MiFDO8DIDS → tkLHUSzPA2kMmWz4DmGwI}/_ssgManifest.js +0 -0
@@ -1,5 +1,49 @@
1
1
  // Project/App: gsd-pi
2
2
  // File Purpose: Auto Orchestration module implementation and ADR-015 invariant pipeline owner.
3
+ //
4
+ // Phase 2 of #442 collapsed the nine single-implementation adapter seams
5
+ // (DispatchAdapter, RecoveryAdapter, StateReconciliationAdapter,
6
+ // ToolContractAdapter, WorktreeAdapter, HealthAdapter, UokGateAdapter,
7
+ // RuntimePersistenceAdapter, NotificationAdapter) into this class. The
8
+ // orchestrator now constructs from the concrete extension context and calls
9
+ // the real collaborators (state-reconciliation, doctor-proactive,
10
+ // auto-dispatch, recovery-classification, tool-contract, worktree-safety,
11
+ // uok/gate-runner, journal, session-lock, ctx.ui.notify) directly.
12
+ import { debugCount, debugTime } from "../debug-logger.js";
13
+ import { reconcileBeforeDispatch } from "../state-reconciliation.js";
14
+ import { resolveDispatch } from "../auto-dispatch.js";
15
+ import { classifyFailure } from "../recovery-classification.js";
16
+ import { verifyExpectedArtifact, refreshRecoveryDbForArtifact } from "../auto-recovery.js";
17
+ import { invalidateAllCaches } from "../cache.js";
18
+ import { compileUnitToolContract } from "../tool-contract.js";
19
+ import { createWorktreeSafetyModule } from "../worktree-safety.js";
20
+ import { repairAutoWorktreeSafetyFailure } from "../auto-worktree-repair.js";
21
+ import { resolveManifest } from "../unit-context-manifest.js";
22
+ import { preDispatchHealthGate, recordHealthSnapshot, } from "../doctor-proactive.js";
23
+ import { checkResourcesStale, autoWorktreeBranch, mergeMilestoneToMain } from "../auto-worktree.js";
24
+ import { getSessionLockStatus } from "../session-lock.js";
25
+ import { resolveUokFlags } from "../uok/flags.js";
26
+ import { emitJournalEvent as _emitJournalEvent } from "../journal.js";
27
+ import { loadEffectiveGSDPreferences, getIsolationMode } from "../preferences.js";
28
+ import { detectWorktreeName, getMainBranch, resolveProjectRoot, resolveWorktreeProjectRoot, } from "../worktree.js";
29
+ import { getPriorSliceCompletionBlocker } from "../dispatch-guard.js";
30
+ import { GitServiceImpl } from "../git-service.js";
31
+ import { WorktreeStateProjection } from "../worktree-state-projection.js";
32
+ import { WorktreeLifecycle } from "../worktree-lifecycle.js";
33
+ import { createMilestoneMergeTransaction } from "../milestone-merge-transaction.js";
34
+ import { createWorkspace, scopeMilestone } from "../workspace.js";
35
+ import { supportsStructuredQuestions } from "../workflow-mcp.js";
36
+ import { getRegisteredToolSnapshot, getToolBaselineSnapshot } from "../auto-model-selection.js";
37
+ import { deriveState } from "../state.js";
38
+ import { parseUnitId } from "../unit-id.js";
39
+ import { isClosedStatus } from "../status-guards.js";
40
+ import { isDbAvailable, getSlice, getTask, } from "../gsd-db.js";
41
+ import { refreshWorkflowDatabaseFromDisk } from "../db-workspace.js";
42
+ import { getErrorMessage } from "../error-utils.js";
43
+ import { logWarning } from "../workflow-logger.js";
44
+ import { existsSync, readFileSync } from "node:fs";
45
+ import { join } from "node:path";
46
+ import { evaluateAllCompleteSettlement } from "../milestone-settlement.js";
3
47
  function now() {
4
48
  return Date.now();
5
49
  }
@@ -12,40 +56,600 @@ function now() {
12
56
  * preserved across the eventual cutover (issue #5791).
13
57
  */
14
58
  export const STUCK_WINDOW_SIZE = 6;
15
- function noRemainingUnitsReason(stateSnapshot) {
59
+ function noRemainingUnitsOutcome(stateSnapshot) {
16
60
  if (stateSnapshot.phase === "complete") {
17
- return "all milestones complete";
61
+ return {
62
+ code: "all-complete",
63
+ displayReason: "All milestones complete",
64
+ allMilestonesComplete: true,
65
+ };
18
66
  }
19
- return "no remaining units";
67
+ return {
68
+ code: "no-remaining-units",
69
+ displayReason: "No remaining units",
70
+ allMilestonesComplete: false,
71
+ };
72
+ }
73
+ function getAlreadyClosedDispatchReason(unitType, unitId) {
74
+ if (!isDbAvailable())
75
+ return null;
76
+ refreshWorkflowDatabaseFromDisk();
77
+ const { milestone, slice, task } = parseUnitId(unitId);
78
+ if (unitType === "execute-task" && milestone && slice && task) {
79
+ const row = getTask(milestone, slice, task);
80
+ return row && isClosedStatus(row.status)
81
+ ? `execute-task ${unitId} is already ${row.status}`
82
+ : null;
83
+ }
84
+ if (unitType === "complete-slice" && milestone && slice) {
85
+ const row = getSlice(milestone, slice);
86
+ return row && isClosedStatus(row.status)
87
+ ? `complete-slice ${unitId} is already ${row.status}`
88
+ : null;
89
+ }
90
+ return null;
91
+ }
92
+ function shouldAdoptActiveMilestone(state, activeSession, activeDispatchBasePath) {
93
+ const activeMilestoneId = state.activeMilestone?.id;
94
+ const currentMilestoneId = activeSession?.currentMilestoneId;
95
+ if (!activeSession || !activeMilestoneId || !currentMilestoneId || activeMilestoneId === currentMilestoneId) {
96
+ return false;
97
+ }
98
+ const scopedWorktreeMilestone = (activeSession.basePath ? detectWorktreeName(activeSession.basePath) : null) ??
99
+ detectWorktreeName(activeDispatchBasePath);
100
+ if (scopedWorktreeMilestone && scopedWorktreeMilestone !== activeMilestoneId) {
101
+ return false;
102
+ }
103
+ const currentMilestone = state.registry.find((milestone) => milestone.id === currentMilestoneId);
104
+ return !!currentMilestone && isClosedStatus(currentMilestone.status);
105
+ }
106
+ /**
107
+ * Pure dispatch-decision function — formerly `createWiredDispatchAdapter`'s
108
+ * `decideNextUnit`. Folded out of the closure so the orchestrator can call it
109
+ * directly and tests can drive the exact dispatch decision logic against real
110
+ * fixtures without re-introducing an adapter seam.
111
+ *
112
+ * Derives session-derived dispatch inputs the same way phases.ts:runDispatch
113
+ * does (#5789): prefers caller-supplied values when present so test harnesses
114
+ * and alternative wirings can inject deterministic snapshots; otherwise pulls
115
+ * from the captured pi/ctx references.
116
+ */
117
+ export async function decideOrchestratorDispatch(ctx, pi, dispatchBasePath, session, input) {
118
+ const state = input.stateSnapshot;
119
+ const active = state.activeMilestone;
120
+ if (!active)
121
+ return null;
122
+ const activeSession = input.session ?? session;
123
+ const activeDispatchBasePath = activeSession?.basePath || dispatchBasePath;
124
+ if (activeSession && shouldAdoptActiveMilestone(state, activeSession, activeDispatchBasePath)) {
125
+ activeSession.currentMilestoneId = active.id;
126
+ }
127
+ const prefs = loadEffectiveGSDPreferences(activeDispatchBasePath)?.preferences;
128
+ // Derive session-derived dispatch inputs the same way phases.ts:runDispatch does
129
+ // (#5789). Prefer caller-supplied values when present so test harnesses and
130
+ // alternative wirings can inject deterministic snapshots; otherwise pull from
131
+ // the captured pi/ctx references.
132
+ const sessionProvider = input.sessionProvider ?? ctx.model?.provider;
133
+ const sessionContextWindow = input.sessionContextWindow ?? ctx.model?.contextWindow;
134
+ const modelRegistry = input.modelRegistry ?? ctx.modelRegistry;
135
+ const authMode = sessionProvider && typeof ctx.modelRegistry?.getProviderAuthMode === "function"
136
+ ? ctx.modelRegistry.getProviderAuthMode(sessionProvider)
137
+ : undefined;
138
+ // Use baseline snapshot — same reason as phases.ts:runDispatch: the live
139
+ // active set may be narrowed by the prior unit before selectAndApplyModel
140
+ // restores it, causing false transport-preflight failures (#477 follow-up).
141
+ const activeTools = getToolBaselineSnapshot(pi);
142
+ const registeredTools = getRegisteredToolSnapshot(pi);
143
+ // Mirrors runDispatch: deep-planning keeps approval gates in plain chat
144
+ // because structured questions can be cancelled outside the chat turn on
145
+ // some transports.
146
+ const structuredQuestionsAvailable = input.structuredQuestionsAvailable ??
147
+ (prefs?.planning_depth === "deep"
148
+ ? "false"
149
+ : supportsStructuredQuestions(activeTools, {
150
+ authMode,
151
+ baseUrl: ctx.model?.baseUrl,
152
+ })
153
+ ? "true"
154
+ : "false");
155
+ const pendingRetry = session?.pendingVerificationRetryDispatch;
156
+ if (session && pendingRetry) {
157
+ session.pendingVerificationRetryDispatch = null;
158
+ const alreadyClosedReason = getAlreadyClosedDispatchReason(pendingRetry.unitType, pendingRetry.unitId);
159
+ if (alreadyClosedReason) {
160
+ session.pendingOrchestrationDispatch = null;
161
+ session.pendingVerificationRetry = null;
162
+ return { kind: "skipped", reason: alreadyClosedReason };
163
+ }
164
+ session.pendingOrchestrationDispatch = pendingRetry;
165
+ return {
166
+ unitType: pendingRetry.unitType,
167
+ unitId: pendingRetry.unitId,
168
+ reason: "verification-retry",
169
+ preconditions: [],
170
+ };
171
+ }
172
+ const action = await resolveDispatch({
173
+ basePath: activeDispatchBasePath,
174
+ mid: active.id,
175
+ midTitle: active.title,
176
+ state,
177
+ prefs,
178
+ session: activeSession,
179
+ structuredQuestionsAvailable,
180
+ sessionContextWindow,
181
+ sessionProvider,
182
+ modelRegistry,
183
+ activeTools,
184
+ registeredTools,
185
+ sessionAuthMode: authMode,
186
+ sessionBaseUrl: ctx.model?.baseUrl,
187
+ });
188
+ if (action.action === "stop") {
189
+ if (session)
190
+ session.pendingOrchestrationDispatch = null;
191
+ return {
192
+ kind: "blocked",
193
+ reason: action.reason,
194
+ action: action.level === "warning" ? "pause" : "stop",
195
+ };
196
+ }
197
+ if (action.action !== "dispatch") {
198
+ if (session)
199
+ session.pendingOrchestrationDispatch = null;
200
+ return {
201
+ kind: "skipped",
202
+ reason: action.matchedRule ?? "dispatch-skip",
203
+ };
204
+ }
205
+ const alreadyClosedReason = getAlreadyClosedDispatchReason(action.unitType, action.unitId);
206
+ if (alreadyClosedReason) {
207
+ if (session) {
208
+ session.pendingOrchestrationDispatch = null;
209
+ session.pendingVerificationRetry = null;
210
+ }
211
+ return { kind: "skipped", reason: alreadyClosedReason };
212
+ }
213
+ if (session) {
214
+ const pending = {
215
+ unitType: action.unitType,
216
+ unitId: action.unitId,
217
+ prompt: action.prompt,
218
+ pauseAfterUatDispatch: action.pauseAfterDispatch ?? false,
219
+ state,
220
+ mid: active.id,
221
+ midTitle: active.title,
222
+ };
223
+ session.pendingOrchestrationDispatch = pending;
224
+ }
225
+ return {
226
+ unitType: action.unitType,
227
+ unitId: action.unitId,
228
+ reason: action.matchedRule ?? "dispatch",
229
+ preconditions: [],
230
+ };
20
231
  }
21
232
  export class AutoOrchestrator {
22
233
  status = {
23
234
  phase: "idle",
24
235
  transitionCount: 0,
25
236
  };
26
- deps;
237
+ ctx;
238
+ pi;
239
+ dispatchBasePath;
240
+ runtimeBasePath;
241
+ s;
242
+ flowId;
243
+ seq = 0;
27
244
  lastAdvanceKey = null;
28
245
  lastFinalizedUnitKey = null;
29
246
  dispatchKeyWindow = [];
30
- constructor(deps) {
31
- this.deps = deps;
247
+ // #442: the unit key we last attempted graduated stuck-recovery for. Bounds
248
+ // recovery to one attempt per stuck episode per run (reset on start/resume/
249
+ // stop), mirroring the legacy Level-1-then-Level-2 escalation in phases.ts.
250
+ lastStuckRecoveryKey = null;
251
+ constructor(context) {
252
+ this.ctx = context.ctx;
253
+ this.pi = context.pi;
254
+ this.dispatchBasePath = context.dispatchBasePath;
255
+ this.runtimeBasePath = context.runtimeBasePath;
256
+ this.s = context.session;
257
+ this.flowId = `auto-orchestrator-${Date.now()}`;
258
+ }
259
+ // ── Live base-path resolution (was the wiring factory's getLiveDispatchBasePath) ──
260
+ getLiveDispatchBasePath() {
261
+ return resolveLiveOrchestratorBasePath({
262
+ capturedBasePath: this.dispatchBasePath,
263
+ runtimeBasePath: this.runtimeBasePath,
264
+ sessionBasePath: this.s.basePath,
265
+ originalBasePath: this.s.originalBasePath,
266
+ });
267
+ }
268
+ // ── RuntimePersistenceAdapter (folded) ───────────────────────────────────
269
+ ensureLockOwnership() {
270
+ const status = getSessionLockStatus(this.runtimeBasePath);
271
+ if (!status.valid || status.failureReason === "pid-mismatch") {
272
+ throw new Error("session lock held by another process");
273
+ }
274
+ }
275
+ /**
276
+ * Map an orchestrator lifecycle event name to its journal eventType and emit
277
+ * it. The name→eventType ternary is preserved byte-for-byte from the legacy
278
+ * wired RuntimePersistenceAdapter.journalTransition.
279
+ */
280
+ journalTransition(event) {
281
+ const eventType = event.name === "start"
282
+ ? "orchestrator-iteration-start"
283
+ : event.name === "resume"
284
+ ? "orchestrator-iteration-start"
285
+ : event.name === "advance"
286
+ ? "orchestrator-dispatch-match"
287
+ : event.name === "advance-blocked"
288
+ ? "orchestrator-guard-block"
289
+ : event.name === "advance-stopped"
290
+ ? "orchestrator-dispatch-stop"
291
+ : event.name === "advance-error"
292
+ ? "orchestrator-iteration-end"
293
+ : event.name === "advance-paused" || event.name === "advance-retry"
294
+ ? "orchestrator-guard-block"
295
+ : event.name === "stop"
296
+ ? "orchestrator-terminal"
297
+ : "orchestrator-iteration-end";
298
+ _emitJournalEvent(this.runtimeBasePath, {
299
+ ts: new Date().toISOString(),
300
+ flowId: this.flowId,
301
+ seq: ++this.seq,
302
+ eventType,
303
+ data: {
304
+ source: "auto-orchestrator",
305
+ name: event.name,
306
+ reason: event.reason,
307
+ unitType: event.unitType,
308
+ unitId: event.unitId,
309
+ },
310
+ });
311
+ }
312
+ // ── NotificationAdapter (folded) ─────────────────────────────────────────
313
+ notifyLifecycle(event) {
314
+ if (event.name === "error") {
315
+ this.ctx.ui.notify(event.detail ?? "auto orchestration error", "error");
316
+ }
317
+ }
318
+ // ── HealthAdapter (folded) ───────────────────────────────────────────────
319
+ checkResourcesStale() {
320
+ return checkResourcesStale(this.s.resourceVersionOnStart);
321
+ }
322
+ async preAdvanceGate() {
323
+ try {
324
+ const gate = await preDispatchHealthGate(this.getLiveDispatchBasePath());
325
+ if (gate.proceed) {
326
+ return {
327
+ kind: "pass",
328
+ fixesApplied: gate.fixesApplied,
329
+ };
330
+ }
331
+ return {
332
+ kind: "fail",
333
+ reason: gate.reason ?? "Pre-dispatch health check failed — run /gsd doctor for details.",
334
+ action: gate.severity ?? "pause",
335
+ };
336
+ }
337
+ catch (error) {
338
+ return { kind: "threw", error };
339
+ }
340
+ }
341
+ postAdvanceRecord(result) {
342
+ if (result.kind === "error") {
343
+ recordHealthSnapshot(1, 0, 0, [{
344
+ code: "orchestration-error",
345
+ message: result.reason ?? "orchestration error",
346
+ severity: "error",
347
+ unitId: "orchestration",
348
+ }], [], "orchestration");
349
+ }
350
+ else if (result.kind === "blocked") {
351
+ recordHealthSnapshot(0, 1, 0, [{
352
+ code: "orchestration-blocked",
353
+ message: result.reason ?? "orchestration blocked",
354
+ severity: "warning",
355
+ unitId: "orchestration",
356
+ }], [], "orchestration");
357
+ }
358
+ }
359
+ // ── UokGateAdapter (folded) ──────────────────────────────────────────────
360
+ async emitUokGate(input) {
361
+ const activeBasePath = this.getLiveDispatchBasePath();
362
+ const prefs = loadEffectiveGSDPreferences(activeBasePath)?.preferences;
363
+ const uokFlags = resolveUokFlags(prefs);
364
+ if (!uokFlags.gates)
365
+ return;
366
+ const milestoneId = input.milestoneId ?? this.s.currentMilestoneId ?? undefined;
367
+ try {
368
+ const { UokGateRunner } = await import("../uok/gate-runner.js");
369
+ const runner = new UokGateRunner();
370
+ runner.register({
371
+ id: input.gateId,
372
+ type: input.gateType,
373
+ execute: async () => ({
374
+ outcome: input.outcome,
375
+ failureClass: input.failureClass,
376
+ rationale: input.rationale,
377
+ findings: input.findings ?? "",
378
+ }),
379
+ });
380
+ await runner.run(input.gateId, {
381
+ basePath: activeBasePath,
382
+ traceId: `pre-dispatch:${this.flowId}`,
383
+ turnId: `orch-${this.seq}`,
384
+ milestoneId,
385
+ unitType: "pre-dispatch",
386
+ unitId: `orch-${this.seq}`,
387
+ });
388
+ }
389
+ catch (err) {
390
+ logWarning("engine", `uok gate emit failed: ${getErrorMessage(err)}`, {
391
+ file: "orchestrator.ts",
392
+ gateId: input.gateId,
393
+ gateType: input.gateType,
394
+ ...(milestoneId ? { milestoneId } : {}),
395
+ });
396
+ }
397
+ }
398
+ // ── StateReconciliationAdapter (folded) ──────────────────────────────────
399
+ async reconcileBeforeDispatch() {
400
+ const activeBasePath = this.getLiveDispatchBasePath();
401
+ const result = await reconcileBeforeDispatch(activeBasePath);
402
+ // Failure-path summaries written by gsd_summary_save create
403
+ // artifact-db-status-divergence blockers for tasks that are still
404
+ // pending (gsd_task_complete never ran). These tasks can still be
405
+ // dispatched and the drift self-heals once they complete successfully.
406
+ const hardBlockers = result.blockers.filter((b) => !b.includes("has SUMMARY artifact while DB status is") &&
407
+ !b.includes("has SUMMARY on disk while DB status is") &&
408
+ !b.includes("has task SUMMARY artifacts but no DB tasks"));
409
+ if (hardBlockers.length > 0) {
410
+ return {
411
+ ok: false,
412
+ reason: hardBlockers[0],
413
+ stateSnapshot: result.stateSnapshot,
414
+ };
415
+ }
416
+ const repairedKinds = result.repaired.map((d) => d.kind);
417
+ return {
418
+ ok: true,
419
+ reason: repairedKinds.length > 0
420
+ ? `repaired: ${repairedKinds.join(", ")}`
421
+ : "clean",
422
+ stateSnapshot: result.stateSnapshot,
423
+ };
424
+ }
425
+ // ── DispatchAdapter (folded) ─────────────────────────────────────────────
426
+ decideNextUnit(input) {
427
+ return decideOrchestratorDispatch(this.ctx, this.pi, this.dispatchBasePath, this.s, input);
428
+ }
429
+ evaluateNoRemainingUnitsSettlement(stateSnapshot) {
430
+ const settlement = evaluateAllCompleteSettlement({
431
+ milestoneId: this.s.currentMilestoneId ?? stateSnapshot.activeMilestone?.id,
432
+ statePhase: stateSnapshot.phase,
433
+ basePath: this.s.basePath || this.getLiveDispatchBasePath(),
434
+ originalBasePath: this.s.originalBasePath || this.runtimeBasePath,
435
+ milestoneMerged: this.s.milestoneMergedInPhases,
436
+ });
437
+ this.s.milestoneSettlement = settlement;
438
+ if (settlement.ok)
439
+ return null;
440
+ return {
441
+ kind: "blocked",
442
+ reason: settlement.message,
443
+ action: settlement.action,
444
+ stateSnapshot,
445
+ terminalOutcome: {
446
+ code: "settlement-blocked",
447
+ displayReason: settlement.message,
448
+ nextAction: settlement.nextAction,
449
+ milestoneId: settlement.milestoneId,
450
+ allMilestonesComplete: false,
451
+ },
452
+ };
453
+ }
454
+ clearPendingDispatch() {
455
+ this.s.pendingOrchestrationDispatch = null;
456
+ }
457
+ findPriorSliceCompletionBlocker(unitType, unitId) {
458
+ const guardBasePath = resolveWorktreeProjectRoot(this.getLiveDispatchBasePath(), this.s.originalBasePath);
459
+ let mainBranch = "main";
460
+ try {
461
+ mainBranch = getMainBranch(guardBasePath);
462
+ }
463
+ catch (err) {
464
+ // Preserve legacy dispatch behavior: fall back to main when branch
465
+ // discovery fails, then let the guard make the progression decision.
466
+ logWarning("engine", `branch discovery failed, falling back to main: ${getErrorMessage(err)}`, { file: "orchestrator.ts" });
467
+ }
468
+ return getPriorSliceCompletionBlocker(guardBasePath, mainBranch, unitType, unitId);
469
+ }
470
+ // ── ToolContractAdapter (folded) ─────────────────────────────────────────
471
+ compileUnitToolContract(unitType) {
472
+ const result = compileUnitToolContract(unitType);
473
+ if (!result.ok)
474
+ return { ok: false, reason: result.detail };
475
+ return { ok: true, reason: result.contract.validationRules.join(", ") };
476
+ }
477
+ // ── WorktreeAdapter (folded) ─────────────────────────────────────────────
478
+ getEffectiveUnitIsolationMode(basePath) {
479
+ const configuredMode = getIsolationMode(basePath);
480
+ return configuredMode === "worktree" && this.s.isolationDegraded ? "branch" : configuredMode;
481
+ }
482
+ buildLifecycle() {
483
+ return new WorktreeLifecycle(this.s, {
484
+ gitServiceFactory: (basePath) => {
485
+ const gitConfig = loadEffectiveGSDPreferences()?.preferences?.git ?? {};
486
+ return new GitServiceImpl(basePath, gitConfig);
487
+ },
488
+ worktreeProjection: new WorktreeStateProjection(),
489
+ mergeMilestoneToMain: createMilestoneMergeTransaction(mergeMilestoneToMain),
490
+ });
491
+ }
492
+ rebuildScope(rawPath, milestoneId) {
493
+ if (!milestoneId) {
494
+ this.s.scope = null;
495
+ return;
496
+ }
497
+ try {
498
+ const workspace = createWorkspace(rawPath);
499
+ this.s.scope = scopeMilestone(workspace, milestoneId);
500
+ }
501
+ catch {
502
+ // Non-fatal — scope is additive. Existing readers still use basePath.
503
+ this.s.scope = null;
504
+ }
505
+ }
506
+ async prepareWorktreeForUnit(unitType, unitId) {
507
+ const isolationMode = this.getEffectiveUnitIsolationMode(this.runtimeBasePath);
508
+ const manifest = resolveManifest(unitType);
509
+ if (!manifest) {
510
+ return {
511
+ ok: false,
512
+ reason: `No Unit manifest is registered for ${unitType}`,
513
+ };
514
+ }
515
+ if (isolationMode !== "worktree") {
516
+ return { ok: true, reason: "not-required" };
517
+ }
518
+ const writeScope = manifest.tools.mode === "all" || manifest.tools.mode === "docs"
519
+ ? "source-writing"
520
+ : "planning-only";
521
+ const safety = createWorktreeSafetyModule();
522
+ const activeBasePath = this.getLiveDispatchBasePath();
523
+ const snapshot = await deriveState(activeBasePath);
524
+ const milestoneId = snapshot.activeMilestone?.id ?? null;
525
+ const expectedBranch = milestoneId ? autoWorktreeBranch(milestoneId) : null;
526
+ let result = safety.validateUnitRoot({
527
+ unitType,
528
+ unitId,
529
+ writeScope,
530
+ projectRoot: this.runtimeBasePath,
531
+ unitRoot: activeBasePath,
532
+ milestoneId,
533
+ isolationMode,
534
+ expectedBranch,
535
+ });
536
+ if (!result.ok) {
537
+ const repaired = await repairAutoWorktreeSafetyFailure({
538
+ safetyResult: result,
539
+ projectRoot: this.runtimeBasePath,
540
+ activeRoot: activeBasePath,
541
+ milestoneId,
542
+ enterMilestone: async (id) => {
543
+ this.buildLifecycle().adoptSessionRoot(this.runtimeBasePath, this.s.originalBasePath || this.runtimeBasePath);
544
+ const enterResult = this.buildLifecycle().enterMilestone(id, {
545
+ notify: this.ctx.ui.notify.bind(this.ctx.ui),
546
+ });
547
+ if (!enterResult.ok)
548
+ return { ok: false, reason: enterResult.reason };
549
+ this.rebuildScope(this.s.basePath, this.s.currentMilestoneId);
550
+ return { ok: true };
551
+ },
552
+ revalidate: () => safety.validateUnitRoot({
553
+ unitType,
554
+ unitId,
555
+ writeScope,
556
+ projectRoot: this.runtimeBasePath,
557
+ unitRoot: this.getLiveDispatchBasePath(),
558
+ milestoneId,
559
+ isolationMode: this.getEffectiveUnitIsolationMode(this.runtimeBasePath),
560
+ expectedBranch,
561
+ }),
562
+ });
563
+ result = repaired.result;
564
+ if (result.ok) {
565
+ return { ok: true, reason: repaired.repaired ? `repaired-${result.kind}` : result.kind };
566
+ }
567
+ const repairDetail = repaired.repairReason
568
+ ? ` (repair skipped: ${repaired.repairReason})`
569
+ : "";
570
+ return { ok: false, reason: `${result.kind}: ${result.reason}${repairDetail}` };
571
+ }
572
+ return { ok: true, reason: result.kind };
573
+ }
574
+ // ── RecoveryAdapter (folded) ─────────────────────────────────────────────
575
+ classifyAndRecover(input) {
576
+ const recovery = classifyFailure(input);
577
+ return { action: recovery.action, reason: recovery.reason };
578
+ }
579
+ // ── Lifecycle verbs ──────────────────────────────────────────────────────
580
+ /**
581
+ * #442: graduated stuck recovery, ported from the legacy
582
+ * auto/phases.ts:runDispatch path that Phase 3 retires. The ring-buffer
583
+ * hard-stops (stuck-loop saturation and finalized-repeat) would otherwise
584
+ * KILL a unit that actually completed on disk but whose DB row is still
585
+ * stale. Before hard-stopping, verify the expected artifact exists; if so,
586
+ * refresh the DB from it, invalidate caches and reset the dispatch ring so
587
+ * the next advance picks the correct next unit. Bounded to one attempt per
588
+ * stuck key per episode (reset on lifecycle + genuine finalize) to avoid an
589
+ * unbounded recover→re-saturate→recover loop — mirrors the legacy
590
+ * Level-1-recover-then-Level-2-hard-stop escalation.
591
+ *
592
+ * Returns true when recovery succeeded; the caller should re-loop (return a
593
+ * skipped result) instead of stopping.
594
+ */
595
+ tryStuckArtifactRecovery(unitType, unitId) {
596
+ const key = `${unitType}:${unitId}`;
597
+ if (this.lastStuckRecoveryKey === key)
598
+ return false; // already tried this episode
599
+ const basePath = this.getLiveDispatchBasePath();
600
+ if (!verifyExpectedArtifact(unitType, unitId, basePath))
601
+ return false;
602
+ const refreshed = refreshRecoveryDbForArtifact(unitType, unitId, basePath);
603
+ // Fatal failures cannot be recovered — hard-stop. Non-fatal (e.g. plan-slice
604
+ // DB refresh hiccup) still fall through: invalidating caches and resetting
605
+ // the ring gives the next advance a clean slate to pick up the correct state,
606
+ // mirroring the legacy Level-1 "continue" escalation path.
607
+ if (!refreshed.ok && refreshed.fatal)
608
+ return false;
609
+ this.lastStuckRecoveryKey = key;
610
+ invalidateAllCaches();
611
+ this.dispatchKeyWindow = [];
612
+ this.lastAdvanceKey = null;
613
+ this.lastFinalizedUnitKey = null;
614
+ return true;
615
+ }
616
+ stuckRecovered(decision, stateSnapshot) {
617
+ const recovered = {
618
+ kind: "skipped",
619
+ reason: `stuck-recovery: ${decision.unitType} ${decision.unitId} artifact found on disk; DB refreshed`,
620
+ stateSnapshot,
621
+ };
622
+ this.status.phase = "running";
623
+ this.status.activeUnit = undefined;
624
+ this.bumpTransition();
625
+ this.journalTransition({
626
+ name: "advance-skipped",
627
+ reason: recovered.reason,
628
+ unitType: decision.unitType,
629
+ unitId: decision.unitId,
630
+ });
631
+ this.postAdvanceRecord(recovered);
632
+ return recovered;
32
633
  }
33
634
  async start(_sessionContext) {
34
635
  this.lastAdvanceKey = null;
35
636
  this.lastFinalizedUnitKey = null;
36
637
  this.dispatchKeyWindow = [];
638
+ this.lastStuckRecoveryKey = null;
37
639
  this.status.phase = "running";
38
640
  this.bumpTransition();
39
- await this.deps.runtime.journalTransition({ name: "start" });
40
- await this.deps.notifications.notifyLifecycle({ name: "start" });
641
+ this.journalTransition({ name: "start" });
642
+ this.notifyLifecycle({ name: "start" });
41
643
  return { kind: "started" };
42
644
  }
43
645
  async advance() {
646
+ debugCount("dispatches");
647
+ const stopAdvanceTimer = debugTime("orchestrator-advance");
44
648
  try {
45
- await this.deps.runtime.ensureLockOwnership();
46
- const staleMsg = this.deps.health.checkResourcesStale();
649
+ this.ensureLockOwnership();
650
+ const staleMsg = this.checkResourcesStale();
47
651
  if (staleMsg) {
48
- await this.deps.uokGate.emit({
652
+ await this.emitUokGate({
49
653
  gateId: "resource-version-guard",
50
654
  gateType: "policy",
51
655
  outcome: "fail",
@@ -54,20 +658,20 @@ export class AutoOrchestrator {
54
658
  findings: staleMsg,
55
659
  });
56
660
  const blocked = { kind: "blocked", reason: staleMsg, action: "pause" };
57
- await this.deps.runtime.journalTransition({ name: "advance-blocked", reason: blocked.reason });
58
- await this.deps.health.postAdvanceRecord(blocked);
661
+ this.journalTransition({ name: "advance-blocked", reason: blocked.reason });
662
+ this.postAdvanceRecord(blocked);
59
663
  return blocked;
60
664
  }
61
- await this.deps.uokGate.emit({
665
+ await this.emitUokGate({
62
666
  gateId: "resource-version-guard",
63
667
  gateType: "policy",
64
668
  outcome: "pass",
65
669
  failureClass: "none",
66
670
  rationale: "resource version guard passed",
67
671
  });
68
- const gate = await this.deps.health.preAdvanceGate();
672
+ const gate = await this.preAdvanceGate();
69
673
  if (gate.kind === "fail") {
70
- await this.deps.uokGate.emit({
674
+ await this.emitUokGate({
71
675
  gateId: "pre-dispatch-health-gate",
72
676
  gateType: "execution",
73
677
  outcome: "manual-attention",
@@ -80,12 +684,12 @@ export class AutoOrchestrator {
80
684
  reason: gate.reason,
81
685
  action: gate.action ?? "pause",
82
686
  };
83
- await this.deps.runtime.journalTransition({ name: "advance-blocked", reason: blocked.reason });
84
- await this.deps.health.postAdvanceRecord(blocked);
687
+ this.journalTransition({ name: "advance-blocked", reason: blocked.reason });
688
+ this.postAdvanceRecord(blocked);
85
689
  return blocked;
86
690
  }
87
691
  if (gate.kind === "threw") {
88
- await this.deps.uokGate.emit({
692
+ await this.emitUokGate({
89
693
  gateId: "pre-dispatch-health-gate",
90
694
  gateType: "execution",
91
695
  outcome: "manual-attention",
@@ -96,7 +700,7 @@ export class AutoOrchestrator {
96
700
  // intentional fall-through: matches runPreDispatch behaviour
97
701
  }
98
702
  else {
99
- await this.deps.uokGate.emit({
703
+ await this.emitUokGate({
100
704
  gateId: "pre-dispatch-health-gate",
101
705
  gateType: "execution",
102
706
  outcome: "pass",
@@ -105,7 +709,7 @@ export class AutoOrchestrator {
105
709
  findings: gate.fixesApplied?.join(", ") ?? "",
106
710
  });
107
711
  }
108
- const reconciliation = await this.deps.stateReconciliation.reconcileBeforeDispatch();
712
+ const reconciliation = await this.reconcileBeforeDispatch();
109
713
  if (!reconciliation.ok || !reconciliation.stateSnapshot) {
110
714
  const blocked = {
111
715
  kind: "blocked",
@@ -113,24 +717,37 @@ export class AutoOrchestrator {
113
717
  action: "pause",
114
718
  stateSnapshot: reconciliation.stateSnapshot,
115
719
  };
116
- await this.deps.runtime.journalTransition({ name: "advance-blocked", reason: blocked.reason });
117
- await this.deps.health.postAdvanceRecord(blocked);
720
+ this.journalTransition({ name: "advance-blocked", reason: blocked.reason });
721
+ this.postAdvanceRecord(blocked);
118
722
  return blocked;
119
723
  }
120
- const decision = await this.deps.dispatch.decideNextUnit({ stateSnapshot: reconciliation.stateSnapshot });
724
+ const decision = await this.decideNextUnit({ stateSnapshot: reconciliation.stateSnapshot });
121
725
  if (!decision) {
726
+ const settlementBlock = this.evaluateNoRemainingUnitsSettlement(reconciliation.stateSnapshot);
727
+ if (settlementBlock) {
728
+ this.status.phase = "paused";
729
+ this.status.activeUnit = undefined;
730
+ this.lastAdvanceKey = null;
731
+ this.dispatchKeyWindow = [];
732
+ this.bumpTransition();
733
+ this.journalTransition({ name: "advance-blocked", reason: settlementBlock.reason });
734
+ this.postAdvanceRecord(settlementBlock);
735
+ return settlementBlock;
736
+ }
737
+ const terminalOutcome = noRemainingUnitsOutcome(reconciliation.stateSnapshot);
122
738
  const stopped = {
123
739
  kind: "stopped",
124
- reason: noRemainingUnitsReason(reconciliation.stateSnapshot),
740
+ reason: terminalOutcome.displayReason,
125
741
  stateSnapshot: reconciliation.stateSnapshot,
742
+ terminalOutcome,
126
743
  };
127
744
  this.status.phase = "stopped";
128
745
  this.status.activeUnit = undefined;
129
746
  this.lastAdvanceKey = null;
130
747
  this.dispatchKeyWindow = [];
131
748
  this.bumpTransition();
132
- await this.deps.runtime.journalTransition({ name: "advance-stopped", reason: stopped.reason });
133
- await this.deps.health.postAdvanceRecord(stopped);
749
+ this.journalTransition({ name: "advance-stopped", reason: stopped.reason });
750
+ this.postAdvanceRecord(stopped);
134
751
  return stopped;
135
752
  }
136
753
  if ("kind" in decision && decision.kind === "skipped") {
@@ -142,8 +759,8 @@ export class AutoOrchestrator {
142
759
  this.status.phase = "running";
143
760
  this.status.activeUnit = undefined;
144
761
  this.bumpTransition();
145
- await this.deps.runtime.journalTransition({ name: "advance-skipped", reason: skipped.reason });
146
- await this.deps.health.postAdvanceRecord(skipped);
762
+ this.journalTransition({ name: "advance-skipped", reason: skipped.reason });
763
+ this.postAdvanceRecord(skipped);
147
764
  return skipped;
148
765
  }
149
766
  if (!("unitType" in decision)) {
@@ -153,8 +770,26 @@ export class AutoOrchestrator {
153
770
  action: decision.action,
154
771
  stateSnapshot: reconciliation.stateSnapshot,
155
772
  };
156
- await this.deps.runtime.journalTransition({ name: "advance-blocked", reason: blocked.reason });
157
- await this.deps.health.postAdvanceRecord(blocked);
773
+ this.journalTransition({ name: "advance-blocked", reason: blocked.reason });
774
+ this.postAdvanceRecord(blocked);
775
+ return blocked;
776
+ }
777
+ const priorSliceBlocker = this.findPriorSliceCompletionBlocker(decision.unitType, decision.unitId);
778
+ if (priorSliceBlocker) {
779
+ this.clearPendingDispatch();
780
+ const blocked = {
781
+ kind: "blocked",
782
+ reason: priorSliceBlocker,
783
+ action: "stop",
784
+ stateSnapshot: reconciliation.stateSnapshot,
785
+ };
786
+ this.journalTransition({
787
+ name: "advance-blocked",
788
+ reason: blocked.reason,
789
+ unitType: decision.unitType,
790
+ unitId: decision.unitId,
791
+ });
792
+ this.postAdvanceRecord(blocked);
158
793
  return blocked;
159
794
  }
160
795
  const nextKey = `${decision.unitType}:${decision.unitId}`;
@@ -168,19 +803,27 @@ export class AutoOrchestrator {
168
803
  }
169
804
  const matchingCount = this.dispatchKeyWindow.filter((k) => k === nextKey).length;
170
805
  if (this.lastFinalizedUnitKey === nextKey) {
806
+ // #442: the unit re-dispatched immediately after finalizing may have
807
+ // actually completed on disk with a stale DB. Verify + recover before
808
+ // hard-stopping (legacy graduated stuck-recovery parity).
809
+ if (this.tryStuckArtifactRecovery(decision.unitType, decision.unitId)) {
810
+ this.clearPendingDispatch();
811
+ return this.stuckRecovered(decision, reconciliation.stateSnapshot);
812
+ }
813
+ this.clearPendingDispatch();
171
814
  const blocked = {
172
815
  kind: "blocked",
173
816
  reason: `state did not advance after finalized ${decision.unitType} ${decision.unitId}`,
174
817
  action: "stop",
175
818
  stateSnapshot: reconciliation.stateSnapshot,
176
819
  };
177
- await this.deps.runtime.journalTransition({
820
+ this.journalTransition({
178
821
  name: "advance-blocked",
179
822
  reason: blocked.reason,
180
823
  unitType: decision.unitType,
181
824
  unitId: decision.unitId,
182
825
  });
183
- await this.deps.health.postAdvanceRecord(blocked);
826
+ this.postAdvanceRecord(blocked);
184
827
  return blocked;
185
828
  }
186
829
  // Idempotency: same key as immediately previous successful advance.
@@ -190,14 +833,15 @@ export class AutoOrchestrator {
190
833
  // checks coexist: idempotency for the common immediate-repeat case,
191
834
  // stuck-loop for the saturated-window case.
192
835
  if (this.lastAdvanceKey === nextKey && matchingCount < STUCK_WINDOW_SIZE) {
836
+ this.clearPendingDispatch();
193
837
  const blocked = { kind: "blocked", reason: "idempotent advance: unit already active", action: "pause" };
194
- await this.deps.runtime.journalTransition({
838
+ this.journalTransition({
195
839
  name: "advance-blocked",
196
840
  reason: blocked.reason,
197
841
  unitType: decision.unitType,
198
842
  unitId: decision.unitId,
199
843
  });
200
- await this.deps.health.postAdvanceRecord(blocked);
844
+ this.postAdvanceRecord(blocked);
201
845
  return blocked;
202
846
  }
203
847
  // Stuck-loop detection: when the ring is saturated with copies of
@@ -205,75 +849,85 @@ export class AutoOrchestrator {
205
849
  // picking the same unit across the whole window and must hard-stop with
206
850
  // a diagnosable reason.
207
851
  if (matchingCount >= STUCK_WINDOW_SIZE) {
852
+ // #442: before declaring a stuck loop, verify the unit didn't actually
853
+ // complete on disk (stale DB) and recover if so — legacy graduated
854
+ // stuck-recovery parity. Otherwise hard-stop with a diagnosable reason.
855
+ if (this.tryStuckArtifactRecovery(decision.unitType, decision.unitId)) {
856
+ this.clearPendingDispatch();
857
+ return this.stuckRecovered(decision, reconciliation.stateSnapshot);
858
+ }
859
+ this.clearPendingDispatch();
208
860
  const blocked = {
209
861
  kind: "blocked",
210
862
  reason: `stuck-loop: ${nextKey} picked ${matchingCount} times`,
211
863
  action: "stop",
212
864
  };
213
- await this.deps.runtime.journalTransition({
865
+ this.journalTransition({
214
866
  name: "advance-blocked",
215
867
  reason: blocked.reason,
216
868
  unitType: decision.unitType,
217
869
  unitId: decision.unitId,
218
870
  });
219
- await this.deps.health.postAdvanceRecord(blocked);
871
+ this.postAdvanceRecord(blocked);
220
872
  return blocked;
221
873
  }
222
- const contract = await this.deps.toolContract.compileUnitToolContract(decision.unitType, decision.unitId);
874
+ const contract = this.compileUnitToolContract(decision.unitType);
223
875
  if (!contract.ok) {
876
+ this.clearPendingDispatch();
224
877
  const blocked = {
225
878
  kind: "blocked",
226
879
  reason: contract.reason,
227
880
  action: "pause",
228
881
  stateSnapshot: reconciliation.stateSnapshot,
229
882
  };
230
- await this.deps.runtime.journalTransition({
883
+ this.journalTransition({
231
884
  name: "advance-blocked",
232
885
  reason: blocked.reason,
233
886
  unitType: decision.unitType,
234
887
  unitId: decision.unitId,
235
888
  });
236
- await this.deps.health.postAdvanceRecord(blocked);
889
+ this.postAdvanceRecord(blocked);
237
890
  return blocked;
238
891
  }
239
- const worktree = await this.deps.worktree.prepareForUnit(decision.unitType, decision.unitId);
892
+ const worktree = await this.prepareWorktreeForUnit(decision.unitType, decision.unitId);
240
893
  if (!worktree.ok) {
894
+ this.clearPendingDispatch();
241
895
  const blocked = {
242
896
  kind: "blocked",
243
897
  reason: worktree.reason,
244
898
  action: "pause",
245
899
  stateSnapshot: reconciliation.stateSnapshot,
246
900
  };
247
- await this.deps.runtime.journalTransition({
901
+ this.journalTransition({
248
902
  name: "advance-blocked",
249
903
  reason: blocked.reason,
250
904
  unitType: decision.unitType,
251
905
  unitId: decision.unitId,
252
906
  });
253
- await this.deps.health.postAdvanceRecord(blocked);
907
+ this.postAdvanceRecord(blocked);
254
908
  return blocked;
255
909
  }
256
910
  this.status.activeUnit = { unitType: decision.unitType, unitId: decision.unitId };
257
911
  this.status.phase = "running";
258
912
  this.lastAdvanceKey = nextKey;
259
913
  this.bumpTransition();
260
- await this.deps.runtime.journalTransition({
914
+ this.journalTransition({
261
915
  name: "advance",
262
916
  reason: decision.reason,
263
917
  unitType: decision.unitType,
264
918
  unitId: decision.unitId,
265
919
  });
266
- await this.deps.worktree.syncAfterUnit(decision.unitType, decision.unitId);
920
+ // syncAfterUnit was a no-op in the wired WorktreeAdapter.
267
921
  const advanced = {
268
922
  kind: "advanced",
269
923
  unit: { unitType: decision.unitType, unitId: decision.unitId },
270
924
  stateSnapshot: reconciliation.stateSnapshot,
271
925
  };
272
- await this.deps.health.postAdvanceRecord(advanced);
926
+ this.postAdvanceRecord(advanced);
273
927
  return advanced;
274
928
  }
275
929
  catch (error) {
276
- const recovery = await this.deps.recovery.classifyAndRecover({
930
+ const recovery = this.classifyAndRecover({
277
931
  error,
278
932
  unitType: this.status.activeUnit?.unitType,
279
933
  unitId: this.status.activeUnit?.unitId,
@@ -304,43 +958,53 @@ export class AutoOrchestrator {
304
958
  : result.kind === "stopped"
305
959
  ? "advance-stopped"
306
960
  : "advance-error";
307
- await this.deps.runtime.journalTransition({ name: journalName, reason: recovery.reason });
961
+ this.journalTransition({ name: journalName, reason: recovery.reason });
308
962
  if (result.kind === "paused") {
309
- await this.deps.notifications.notifyLifecycle({ name: "pause", detail: recovery.reason });
963
+ this.notifyLifecycle({ name: "pause", detail: recovery.reason });
310
964
  }
311
965
  else if (result.kind === "stopped") {
312
- await this.deps.notifications.notifyLifecycle({ name: "stopped", detail: recovery.reason });
966
+ this.notifyLifecycle({ name: "stopped", detail: recovery.reason });
313
967
  }
314
968
  else if (result.kind === "error") {
315
- await this.deps.notifications.notifyLifecycle({ name: "error", detail: recovery.reason });
969
+ this.notifyLifecycle({ name: "error", detail: recovery.reason });
316
970
  }
317
- await this.deps.health.postAdvanceRecord(result);
971
+ this.postAdvanceRecord(result);
318
972
  return result;
319
973
  }
974
+ finally {
975
+ stopAdvanceTimer();
976
+ }
320
977
  }
321
978
  async resume() {
322
979
  this.lastAdvanceKey = null;
323
980
  this.lastFinalizedUnitKey = null;
324
- this.dispatchKeyWindow = [];
981
+ // Preserve dispatchKeyWindow across resume so stuck-loop detection
982
+ // accumulates across pause/resume cycles rather than resetting each time.
983
+ this.lastStuckRecoveryKey = null;
325
984
  this.status.phase = "running";
326
985
  this.bumpTransition();
327
- await this.deps.runtime.journalTransition({ name: "resume" });
328
- await this.deps.notifications.notifyLifecycle({ name: "resume" });
986
+ this.journalTransition({ name: "resume" });
987
+ this.notifyLifecycle({ name: "resume" });
329
988
  return { kind: "resumed" };
330
989
  }
331
990
  async stop(reason) {
332
991
  if (this.status.phase === "stopped") {
333
992
  return { kind: "stopped", reason };
334
993
  }
335
- await this.deps.worktree.cleanupOnStop(reason);
994
+ // cleanupOnStop was a no-op in the wired WorktreeAdapter.
336
995
  this.status.phase = "stopped";
337
996
  this.status.activeUnit = undefined;
338
997
  this.lastAdvanceKey = null;
339
998
  this.lastFinalizedUnitKey = null;
340
- this.dispatchKeyWindow = [];
999
+ // Preserve dispatchKeyWindow on pause so stuck-loop detection accumulates
1000
+ // across pause/resume cycles. Only clear on a hard stop.
1001
+ if (reason !== "pause") {
1002
+ this.dispatchKeyWindow = [];
1003
+ }
1004
+ this.lastStuckRecoveryKey = null;
341
1005
  this.bumpTransition();
342
- await this.deps.runtime.journalTransition({ name: "stop", reason });
343
- await this.deps.notifications.notifyLifecycle({ name: "stop", detail: reason });
1006
+ this.journalTransition({ name: "stop", reason });
1007
+ this.notifyLifecycle({ name: "stop", detail: reason });
344
1008
  return { kind: "stopped", reason };
345
1009
  }
346
1010
  getStatus() {
@@ -356,8 +1020,10 @@ export class AutoOrchestrator {
356
1020
  this.status.activeUnit = undefined;
357
1021
  this.lastAdvanceKey = null;
358
1022
  this.lastFinalizedUnitKey = unitKey;
1023
+ // Genuine progress — re-enable graduated stuck recovery for future episodes.
1024
+ this.lastStuckRecoveryKey = null;
359
1025
  this.bumpTransition();
360
- await this.deps.runtime.journalTransition({
1026
+ this.journalTransition({
361
1027
  name: "unit-finalized",
362
1028
  unitType: unit.unitType,
363
1029
  unitId: unit.unitId,
@@ -376,7 +1042,7 @@ export class AutoOrchestrator {
376
1042
  this.lastAdvanceKey = null;
377
1043
  this.lastFinalizedUnitKey = null;
378
1044
  this.bumpTransition();
379
- await this.deps.runtime.journalTransition({
1045
+ this.journalTransition({
380
1046
  name: "unit-retry",
381
1047
  reason: "finalize-retry",
382
1048
  unitType: unit.unitType,
@@ -388,6 +1054,40 @@ export class AutoOrchestrator {
388
1054
  this.status.lastTransitionAt = now();
389
1055
  }
390
1056
  }
391
- export function createAutoOrchestrator(deps) {
392
- return new AutoOrchestrator(deps);
1057
+ function isUsableLiveOrchestratorBasePath(basePath) {
1058
+ if (!basePath || !existsSync(basePath))
1059
+ return false;
1060
+ if (!detectWorktreeName(basePath))
1061
+ return true;
1062
+ try {
1063
+ return readFileSync(join(basePath, ".git"), "utf8").trim().startsWith("gitdir: ");
1064
+ }
1065
+ catch {
1066
+ return false;
1067
+ }
1068
+ }
1069
+ /**
1070
+ * Resolve the base path the live orchestrator should dispatch from, falling
1071
+ * back to the project root when the captured worktree path has been removed
1072
+ * (e.g. after milestone-merge cleanup). Exported for the closeout-regression
1073
+ * tests and reused by the orchestrator's getLiveDispatchBasePath.
1074
+ */
1075
+ export function resolveLiveOrchestratorBasePath(input) {
1076
+ const primary = input.sessionBasePath || input.capturedBasePath;
1077
+ if (isUsableLiveOrchestratorBasePath(primary))
1078
+ return primary;
1079
+ const fallbacks = [
1080
+ input.originalBasePath,
1081
+ input.runtimeBasePath,
1082
+ resolveProjectRoot(input.capturedBasePath),
1083
+ ];
1084
+ for (const candidate of fallbacks) {
1085
+ if (candidate && isUsableLiveOrchestratorBasePath(candidate)) {
1086
+ return candidate;
1087
+ }
1088
+ }
1089
+ return input.runtimeBasePath || input.capturedBasePath;
1090
+ }
1091
+ export function createAutoOrchestrator(context) {
1092
+ return new AutoOrchestrator(context);
393
1093
  }