@opengsd/gsd-pi 1.5.0 → 1.6.0

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 (499) hide show
  1. package/README.md +10 -10
  2. package/dist/resources/.managed-resources-content-hash +1 -1
  3. package/dist/resources/extensions/ask-user-questions.js +1 -1
  4. package/dist/resources/extensions/browser-tools/engine/managed-gsd-browser.js +114 -36
  5. package/dist/resources/extensions/gsd/activity-log.js +1 -1
  6. package/dist/resources/extensions/gsd/auto/closeout.js +97 -5
  7. package/dist/resources/extensions/gsd/auto/orchestrator.js +20 -13
  8. package/dist/resources/extensions/gsd/auto/pre-dispatch.js +6 -3
  9. package/dist/resources/extensions/gsd/auto/unit-phase.js +5 -9
  10. package/dist/resources/extensions/gsd/auto-artifact-paths.js +14 -5
  11. package/dist/resources/extensions/gsd/auto-direct-dispatch.js +11 -7
  12. package/dist/resources/extensions/gsd/auto-dispatch.js +69 -17
  13. package/dist/resources/extensions/gsd/auto-model-selection.js +28 -1
  14. package/dist/resources/extensions/gsd/auto-post-unit.js +4 -3
  15. package/dist/resources/extensions/gsd/auto-prompts.js +29 -117
  16. package/dist/resources/extensions/gsd/auto-recovery.js +50 -6
  17. package/dist/resources/extensions/gsd/auto-start.js +57 -33
  18. package/dist/resources/extensions/gsd/auto-timeout-recovery.js +12 -2
  19. package/dist/resources/extensions/gsd/auto-timers.js +9 -2
  20. package/dist/resources/extensions/gsd/auto-worktree-branch-lifecycle.js +189 -0
  21. package/dist/resources/extensions/gsd/auto-worktree-cleanup.js +133 -0
  22. package/dist/resources/extensions/gsd/auto-worktree-conflict-auto-resolve.js +35 -0
  23. package/dist/resources/extensions/gsd/auto-worktree-creation.js +130 -0
  24. package/dist/resources/extensions/gsd/auto-worktree-entry.js +118 -0
  25. package/dist/resources/extensions/gsd/auto-worktree-merge-already-merged.js +98 -0
  26. package/dist/resources/extensions/gsd/auto-worktree-merge-branch-head.js +52 -0
  27. package/dist/resources/extensions/gsd/auto-worktree-merge-cleanup.js +87 -0
  28. package/dist/resources/extensions/gsd/auto-worktree-merge-code-changes.js +46 -0
  29. package/dist/resources/extensions/gsd/auto-worktree-merge-db-ready.js +68 -0
  30. package/dist/resources/extensions/gsd/auto-worktree-merge-integration-branch.js +51 -0
  31. package/dist/resources/extensions/gsd/auto-worktree-merge-message.js +106 -0
  32. package/dist/resources/extensions/gsd/auto-worktree-merge-pre-teardown.js +64 -0
  33. package/dist/resources/extensions/gsd/auto-worktree-merge-stash.js +168 -0
  34. package/dist/resources/extensions/gsd/auto-worktree-merge.js +302 -0
  35. package/dist/resources/extensions/gsd/auto-worktree-milestone-shelter.js +119 -0
  36. package/dist/resources/extensions/gsd/auto-worktree-path-resolution.js +5 -0
  37. package/dist/resources/extensions/gsd/auto-worktree-path-utils.js +5 -0
  38. package/dist/resources/extensions/gsd/auto-worktree-resource-version.js +41 -0
  39. package/dist/resources/extensions/gsd/auto-worktree-runtime-cleanup.js +99 -0
  40. package/dist/resources/extensions/gsd/auto-worktree-session-registry.js +57 -0
  41. package/dist/resources/extensions/gsd/auto-worktree-sync.js +249 -0
  42. package/dist/resources/extensions/gsd/auto-worktree-teardown.js +120 -0
  43. package/dist/resources/extensions/gsd/auto-worktree.js +20 -2073
  44. package/dist/resources/extensions/gsd/auto.js +106 -34
  45. package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +1 -1
  46. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +20 -2
  47. package/dist/resources/extensions/gsd/bootstrap/system-context.js +1 -1
  48. package/dist/resources/extensions/gsd/bootstrap/tool-call-loop-guard.js +185 -27
  49. package/dist/resources/extensions/gsd/codebase-generator.js +7 -1
  50. package/dist/resources/extensions/gsd/commands-handlers.js +1 -1
  51. package/dist/resources/extensions/gsd/commands-workflow-templates.js +3 -3
  52. package/dist/resources/extensions/gsd/compat/compat-marker.js +44 -0
  53. package/dist/resources/extensions/gsd/config-overlay.js +7 -2
  54. package/dist/resources/extensions/gsd/db/engine.js +33 -0
  55. package/dist/resources/extensions/gsd/db/queries.js +9 -9
  56. package/dist/resources/extensions/gsd/db/writers/cascades.js +20 -2
  57. package/dist/resources/extensions/gsd/db-adapter.js +30 -3
  58. package/dist/resources/extensions/gsd/docs/preferences-reference.md +3 -3
  59. package/dist/resources/extensions/gsd/doctor-engine-checks.js +93 -11
  60. package/dist/resources/extensions/gsd/flat-phase-migration.js +32 -3
  61. package/dist/resources/extensions/gsd/forensics.js +1 -1
  62. package/dist/resources/extensions/gsd/git-conflict-resolve.js +2 -2
  63. package/dist/resources/extensions/gsd/git-service.js +45 -0
  64. package/dist/resources/extensions/gsd/gsd-db.js +23 -11
  65. package/dist/resources/extensions/gsd/guided-flow.js +22 -17
  66. package/dist/resources/extensions/gsd/journal.js +1 -1
  67. package/dist/resources/extensions/gsd/markdown-renderer.js +34 -79
  68. package/dist/resources/extensions/gsd/md-importer.js +89 -23
  69. package/dist/resources/extensions/gsd/metrics.js +2 -2
  70. package/dist/resources/extensions/gsd/migration-auto-check.js +30 -3
  71. package/dist/resources/extensions/gsd/milestone-merge-transaction.js +11 -0
  72. package/dist/resources/extensions/gsd/milestone-settlement.js +1 -1
  73. package/dist/resources/extensions/gsd/parallel-merge.js +1 -1
  74. package/dist/resources/extensions/gsd/parallel-orchestrator.js +3 -1
  75. package/dist/resources/extensions/gsd/paths.js +87 -52
  76. package/dist/resources/extensions/gsd/post-unit-hooks.js +27 -0
  77. package/dist/resources/extensions/gsd/preferences-models.js +66 -15
  78. package/dist/resources/extensions/gsd/preferences-skills.js +10 -16
  79. package/dist/resources/extensions/gsd/preferences-types.js +1 -0
  80. package/dist/resources/extensions/gsd/preferences-validation.js +148 -10
  81. package/dist/resources/extensions/gsd/preferences.js +20 -0
  82. package/dist/resources/extensions/gsd/prompts/system.md +6 -6
  83. package/dist/resources/extensions/gsd/quick.js +12 -3
  84. package/dist/resources/extensions/gsd/rule-registry.js +106 -10
  85. package/dist/resources/extensions/gsd/skill-discovery.js +36 -23
  86. package/dist/resources/extensions/gsd/slice-parallel-orchestrator.js +2 -1
  87. package/dist/resources/extensions/gsd/state/derive/from-db.js +88 -153
  88. package/dist/resources/extensions/gsd/state-reconciliation/drift/artifact-db.js +1 -10
  89. package/dist/resources/extensions/gsd/state-reconciliation/drift/completion.js +3 -24
  90. package/dist/resources/extensions/gsd/state-reconciliation/index.js +10 -0
  91. package/dist/resources/extensions/gsd/tool-contract.js +14 -0
  92. package/dist/resources/extensions/gsd/tools/complete-milestone.js +3 -5
  93. package/dist/resources/extensions/gsd/tools/reassess-roadmap.js +16 -10
  94. package/dist/resources/extensions/gsd/tools/reopen-slice.js +5 -0
  95. package/dist/resources/extensions/gsd/tools/reopen-task.js +13 -5
  96. package/dist/resources/extensions/gsd/tools/validate-milestone.js +3 -8
  97. package/dist/resources/extensions/gsd/uat-dispatch.js +94 -0
  98. package/dist/resources/extensions/gsd/unit-registry.js +12 -2
  99. package/dist/resources/extensions/gsd/unmerged-milestone-guard.js +1 -1
  100. package/dist/resources/extensions/gsd/uok/audit-toggle.js +31 -2
  101. package/dist/resources/extensions/gsd/uok/audit.js +3 -0
  102. package/dist/resources/extensions/gsd/uok/gate-runner.js +22 -30
  103. package/dist/resources/extensions/gsd/uok/gitops.js +18 -15
  104. package/dist/resources/extensions/gsd/uok/kernel.js +120 -46
  105. package/dist/resources/extensions/gsd/uok/model-policy.js +17 -14
  106. package/dist/resources/extensions/gsd/validation-block-guard.js +2 -1
  107. package/dist/resources/extensions/gsd/workflow-event-ledger.js +1 -1
  108. package/dist/resources/extensions/gsd/workflow-logger.js +1 -1
  109. package/dist/resources/extensions/gsd/workflow-projections.js +9 -26
  110. package/dist/resources/extensions/gsd/workspace-git-preflight.js +2 -2
  111. package/dist/resources/extensions/gsd/workspace.js +5 -14
  112. package/dist/resources/extensions/gsd/worktree-command.js +1 -1
  113. package/dist/resources/extensions/gsd/worktree-lifecycle.js +14 -9
  114. package/dist/resources/extensions/gsd/worktree-reentry.js +2 -1
  115. package/dist/resources/extensions/shared/gsd-browser-cli.js +29 -0
  116. package/dist/resources/extensions/shared/interview-ui.js +62 -9
  117. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  118. package/dist/web/standalone/.next/BUILD_ID +1 -1
  119. package/dist/web/standalone/.next/app-path-routes-manifest.json +9 -9
  120. package/dist/web/standalone/.next/build-manifest.json +2 -2
  121. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  122. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  123. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  124. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  125. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  126. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  127. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  128. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  129. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  130. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  131. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  132. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  133. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  134. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  135. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  136. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  137. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  138. package/dist/web/standalone/.next/server/app/api/boot/route.js.nft.json +1 -1
  139. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js.nft.json +1 -1
  140. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js.nft.json +1 -1
  141. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js.nft.json +1 -1
  142. package/dist/web/standalone/.next/server/app/api/captures/route.js.nft.json +1 -1
  143. package/dist/web/standalone/.next/server/app/api/cleanup/route.js.nft.json +1 -1
  144. package/dist/web/standalone/.next/server/app/api/doctor/route.js.nft.json +1 -1
  145. package/dist/web/standalone/.next/server/app/api/export-data/route.js.nft.json +1 -1
  146. package/dist/web/standalone/.next/server/app/api/files/route.js.nft.json +1 -1
  147. package/dist/web/standalone/.next/server/app/api/forensics/route.js.nft.json +1 -1
  148. package/dist/web/standalone/.next/server/app/api/git/route.js.nft.json +1 -1
  149. package/dist/web/standalone/.next/server/app/api/history/route.js.nft.json +1 -1
  150. package/dist/web/standalone/.next/server/app/api/hooks/route.js.nft.json +1 -1
  151. package/dist/web/standalone/.next/server/app/api/inspect/route.js.nft.json +1 -1
  152. package/dist/web/standalone/.next/server/app/api/knowledge/route.js.nft.json +1 -1
  153. package/dist/web/standalone/.next/server/app/api/live-state/route.js.nft.json +1 -1
  154. package/dist/web/standalone/.next/server/app/api/mcp-connections/route.js.nft.json +1 -1
  155. package/dist/web/standalone/.next/server/app/api/notifications/route.js.nft.json +1 -1
  156. package/dist/web/standalone/.next/server/app/api/onboarding/route.js.nft.json +1 -1
  157. package/dist/web/standalone/.next/server/app/api/projects/route.js.nft.json +1 -1
  158. package/dist/web/standalone/.next/server/app/api/recovery/route.js.nft.json +1 -1
  159. package/dist/web/standalone/.next/server/app/api/session/browser/route.js.nft.json +1 -1
  160. package/dist/web/standalone/.next/server/app/api/session/command/route.js.nft.json +1 -1
  161. package/dist/web/standalone/.next/server/app/api/session/events/route.js.nft.json +1 -1
  162. package/dist/web/standalone/.next/server/app/api/session/manage/route.js.nft.json +1 -1
  163. package/dist/web/standalone/.next/server/app/api/settings-data/route.js.nft.json +1 -1
  164. package/dist/web/standalone/.next/server/app/api/shutdown/route.js.nft.json +1 -1
  165. package/dist/web/standalone/.next/server/app/api/skill-health/route.js.nft.json +1 -1
  166. package/dist/web/standalone/.next/server/app/api/steer/route.js.nft.json +1 -1
  167. package/dist/web/standalone/.next/server/app/api/switch-root/route.js.nft.json +1 -1
  168. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js.nft.json +1 -1
  169. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js.nft.json +1 -1
  170. package/dist/web/standalone/.next/server/app/api/undo/route.js.nft.json +1 -1
  171. package/dist/web/standalone/.next/server/app/api/visualizer/route.js.nft.json +1 -1
  172. package/dist/web/standalone/.next/server/app/index.html +1 -1
  173. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  174. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  175. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  176. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  177. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  178. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  179. package/dist/web/standalone/.next/server/app-paths-manifest.json +9 -9
  180. package/dist/web/standalone/.next/server/chunks/{1128.js → 1991.js} +1 -1
  181. package/dist/web/standalone/.next/server/chunks/8357.js +1 -1
  182. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  183. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  184. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  185. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  186. package/dist/web/standalone/node_modules/@gsd/native/package.json +1 -1
  187. package/dist/web/standalone/node_modules/node-pty/build/Makefile +1 -1
  188. package/dist/worktree-cli-create.d.ts +10 -0
  189. package/dist/worktree-cli-create.js +11 -0
  190. package/dist/worktree-cli-format.d.ts +12 -0
  191. package/dist/worktree-cli-format.js +38 -0
  192. package/dist/worktree-cli-plan.d.ts +16 -0
  193. package/dist/worktree-cli-plan.js +19 -0
  194. package/dist/worktree-cli-session.d.ts +13 -0
  195. package/dist/worktree-cli-session.js +18 -0
  196. package/dist/worktree-cli-status.d.ts +35 -0
  197. package/dist/worktree-cli-status.js +54 -0
  198. package/dist/worktree-cli.d.ts +1 -16
  199. package/dist/worktree-cli.js +28 -122
  200. package/integrations/hermes/open_gsd_hermes/gsd_client.py +25 -36
  201. package/integrations/hermes/pyproject.toml +1 -1
  202. package/integrations/hermes/tests/test_gsd_client_timeout.py +4 -4
  203. package/package.json +6 -6
  204. package/packages/cloud-mcp-gateway/package.json +2 -2
  205. package/packages/contracts/package.json +1 -1
  206. package/packages/daemon/dist/orchestrator-agent.d.ts +21 -0
  207. package/packages/daemon/dist/orchestrator-agent.d.ts.map +1 -0
  208. package/packages/daemon/dist/orchestrator-agent.js +113 -0
  209. package/packages/daemon/dist/orchestrator-agent.js.map +1 -0
  210. package/packages/daemon/dist/orchestrator-tools.d.ts +12 -0
  211. package/packages/daemon/dist/orchestrator-tools.d.ts.map +1 -0
  212. package/packages/daemon/dist/orchestrator-tools.js +137 -0
  213. package/packages/daemon/dist/orchestrator-tools.js.map +1 -0
  214. package/packages/daemon/dist/orchestrator.d.ts +4 -56
  215. package/packages/daemon/dist/orchestrator.d.ts.map +1 -1
  216. package/packages/daemon/dist/orchestrator.js +13 -303
  217. package/packages/daemon/dist/orchestrator.js.map +1 -1
  218. package/packages/daemon/dist/orchestrator.test.js +11 -1
  219. package/packages/daemon/dist/orchestrator.test.js.map +1 -1
  220. package/packages/daemon/package.json +4 -4
  221. package/packages/gsd-agent-core/dist/export-html/template.js +53 -4
  222. package/packages/gsd-agent-core/package.json +5 -5
  223. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  224. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js +3 -2
  225. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  226. package/packages/gsd-agent-modes/package.json +7 -7
  227. package/packages/mcp-server/package.json +4 -4
  228. package/packages/native/package.json +1 -1
  229. package/packages/pi-agent-core/dist/agent-loop.d.ts.map +1 -1
  230. package/packages/pi-agent-core/dist/agent-loop.js +14 -9
  231. package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
  232. package/packages/pi-agent-core/package.json +1 -1
  233. package/packages/pi-ai/dist/index.d.ts +1 -0
  234. package/packages/pi-ai/dist/index.d.ts.map +1 -1
  235. package/packages/pi-ai/dist/index.js +1 -0
  236. package/packages/pi-ai/dist/index.js.map +1 -1
  237. package/packages/pi-ai/dist/providers/transform-messages.d.ts.map +1 -1
  238. package/packages/pi-ai/dist/providers/transform-messages.js +7 -3
  239. package/packages/pi-ai/dist/providers/transform-messages.js.map +1 -1
  240. package/packages/pi-ai/dist/tool-result-content.d.ts +3 -0
  241. package/packages/pi-ai/dist/tool-result-content.d.ts.map +1 -0
  242. package/packages/pi-ai/dist/tool-result-content.js +62 -0
  243. package/packages/pi-ai/dist/tool-result-content.js.map +1 -0
  244. package/packages/pi-ai/dist/types.d.ts +8 -20
  245. package/packages/pi-ai/dist/types.d.ts.map +1 -1
  246. package/packages/pi-ai/dist/types.js.map +1 -1
  247. package/packages/pi-ai/package.json +1 -1
  248. package/packages/pi-coding-agent/dist/core/package-manager.d.ts +9 -0
  249. package/packages/pi-coding-agent/dist/core/package-manager.d.ts.map +1 -1
  250. package/packages/pi-coding-agent/dist/core/package-manager.js +74 -53
  251. package/packages/pi-coding-agent/dist/core/package-manager.js.map +1 -1
  252. package/packages/pi-coding-agent/dist/core/resource-loader.d.ts +6 -0
  253. package/packages/pi-coding-agent/dist/core/resource-loader.d.ts.map +1 -1
  254. package/packages/pi-coding-agent/dist/core/resource-loader.js +30 -2
  255. package/packages/pi-coding-agent/dist/core/resource-loader.js.map +1 -1
  256. package/packages/pi-coding-agent/dist/core/skill-directories.d.ts +66 -0
  257. package/packages/pi-coding-agent/dist/core/skill-directories.d.ts.map +1 -0
  258. package/packages/pi-coding-agent/dist/core/skill-directories.js +138 -0
  259. package/packages/pi-coding-agent/dist/core/skill-directories.js.map +1 -0
  260. package/packages/pi-coding-agent/dist/core/skills.d.ts +4 -1
  261. package/packages/pi-coding-agent/dist/core/skills.d.ts.map +1 -1
  262. package/packages/pi-coding-agent/dist/core/skills.js +4 -1
  263. package/packages/pi-coding-agent/dist/core/skills.js.map +1 -1
  264. package/packages/pi-coding-agent/dist/index.d.ts +1 -0
  265. package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
  266. package/packages/pi-coding-agent/dist/index.js +1 -0
  267. package/packages/pi-coding-agent/dist/index.js.map +1 -1
  268. package/packages/pi-coding-agent/package.json +7 -7
  269. package/packages/pi-tui/package.json +2 -2
  270. package/packages/rpc-client/package.json +2 -2
  271. package/pkg/dist/core/export-html/template.js +53 -4
  272. package/pkg/package.json +1 -1
  273. package/src/resources/extensions/ask-user-questions.ts +1 -1
  274. package/src/resources/extensions/browser-tools/engine/managed-gsd-browser.ts +146 -33
  275. package/src/resources/extensions/browser-tools/tests/gsd-browser-launch-config.test.mjs +25 -0
  276. package/src/resources/extensions/browser-tools/tests/managed-gsd-browser-tools.test.mjs +93 -0
  277. package/src/resources/extensions/gsd/activity-log.ts +1 -1
  278. package/src/resources/extensions/gsd/auto/closeout.ts +114 -7
  279. package/src/resources/extensions/gsd/auto/orchestrator.ts +21 -14
  280. package/src/resources/extensions/gsd/auto/pre-dispatch.ts +6 -3
  281. package/src/resources/extensions/gsd/auto/unit-phase.ts +5 -9
  282. package/src/resources/extensions/gsd/auto-artifact-paths.ts +14 -4
  283. package/src/resources/extensions/gsd/auto-direct-dispatch.ts +13 -7
  284. package/src/resources/extensions/gsd/auto-dispatch.ts +77 -16
  285. package/src/resources/extensions/gsd/auto-model-selection.ts +33 -1
  286. package/src/resources/extensions/gsd/auto-post-unit.ts +4 -2
  287. package/src/resources/extensions/gsd/auto-prompts.ts +53 -113
  288. package/src/resources/extensions/gsd/auto-recovery.ts +50 -6
  289. package/src/resources/extensions/gsd/auto-start.ts +62 -31
  290. package/src/resources/extensions/gsd/auto-timeout-recovery.ts +12 -2
  291. package/src/resources/extensions/gsd/auto-timers.ts +9 -2
  292. package/src/resources/extensions/gsd/auto-worktree-branch-lifecycle.ts +237 -0
  293. package/src/resources/extensions/gsd/auto-worktree-cleanup.ts +166 -0
  294. package/src/resources/extensions/gsd/auto-worktree-conflict-auto-resolve.ts +37 -0
  295. package/src/resources/extensions/gsd/auto-worktree-creation.ts +171 -0
  296. package/src/resources/extensions/gsd/auto-worktree-entry.ts +154 -0
  297. package/src/resources/extensions/gsd/auto-worktree-merge-already-merged.ts +161 -0
  298. package/src/resources/extensions/gsd/auto-worktree-merge-branch-head.ts +83 -0
  299. package/src/resources/extensions/gsd/auto-worktree-merge-cleanup.ts +132 -0
  300. package/src/resources/extensions/gsd/auto-worktree-merge-code-changes.ts +71 -0
  301. package/src/resources/extensions/gsd/auto-worktree-merge-db-ready.ts +112 -0
  302. package/src/resources/extensions/gsd/auto-worktree-merge-integration-branch.ts +99 -0
  303. package/src/resources/extensions/gsd/auto-worktree-merge-message.ts +166 -0
  304. package/src/resources/extensions/gsd/auto-worktree-merge-pre-teardown.ts +93 -0
  305. package/src/resources/extensions/gsd/auto-worktree-merge-stash.ts +213 -0
  306. package/src/resources/extensions/gsd/auto-worktree-merge.ts +391 -0
  307. package/src/resources/extensions/gsd/auto-worktree-milestone-shelter.ts +159 -0
  308. package/src/resources/extensions/gsd/auto-worktree-path-resolution.ts +6 -0
  309. package/src/resources/extensions/gsd/auto-worktree-path-utils.ts +10 -0
  310. package/src/resources/extensions/gsd/auto-worktree-resource-version.ts +44 -0
  311. package/src/resources/extensions/gsd/auto-worktree-runtime-cleanup.ts +119 -0
  312. package/src/resources/extensions/gsd/auto-worktree-session-registry.ts +71 -0
  313. package/src/resources/extensions/gsd/auto-worktree-sync.ts +370 -0
  314. package/src/resources/extensions/gsd/auto-worktree-teardown.ts +161 -0
  315. package/src/resources/extensions/gsd/auto-worktree.ts +69 -2429
  316. package/src/resources/extensions/gsd/auto.ts +114 -47
  317. package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +1 -1
  318. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +20 -2
  319. package/src/resources/extensions/gsd/bootstrap/system-context.ts +1 -1
  320. package/src/resources/extensions/gsd/bootstrap/tool-call-loop-guard.ts +218 -27
  321. package/src/resources/extensions/gsd/codebase-generator.ts +7 -1
  322. package/src/resources/extensions/gsd/commands-handlers.ts +1 -1
  323. package/src/resources/extensions/gsd/commands-workflow-templates.ts +3 -3
  324. package/src/resources/extensions/gsd/compat/compat-marker.ts +47 -0
  325. package/src/resources/extensions/gsd/config-overlay.ts +7 -1
  326. package/src/resources/extensions/gsd/db/engine.ts +36 -0
  327. package/src/resources/extensions/gsd/db/queries.ts +9 -9
  328. package/src/resources/extensions/gsd/db/writers/cascades.ts +23 -1
  329. package/src/resources/extensions/gsd/db-adapter.ts +29 -3
  330. package/src/resources/extensions/gsd/docs/preferences-reference.md +3 -3
  331. package/src/resources/extensions/gsd/doctor-engine-checks.ts +109 -10
  332. package/src/resources/extensions/gsd/flat-phase-migration.ts +34 -3
  333. package/src/resources/extensions/gsd/forensics.ts +1 -1
  334. package/src/resources/extensions/gsd/git-conflict-resolve.ts +2 -2
  335. package/src/resources/extensions/gsd/git-service.ts +53 -0
  336. package/src/resources/extensions/gsd/gsd-db.ts +33 -11
  337. package/src/resources/extensions/gsd/guided-flow.ts +26 -18
  338. package/src/resources/extensions/gsd/journal.ts +1 -1
  339. package/src/resources/extensions/gsd/markdown-renderer.ts +52 -91
  340. package/src/resources/extensions/gsd/md-importer.ts +93 -22
  341. package/src/resources/extensions/gsd/metrics.ts +2 -2
  342. package/src/resources/extensions/gsd/migration-auto-check.ts +32 -3
  343. package/src/resources/extensions/gsd/milestone-merge-transaction.ts +15 -0
  344. package/src/resources/extensions/gsd/milestone-settlement.ts +1 -1
  345. package/src/resources/extensions/gsd/parallel-merge.ts +1 -1
  346. package/src/resources/extensions/gsd/parallel-orchestrator.ts +3 -1
  347. package/src/resources/extensions/gsd/paths.ts +108 -51
  348. package/src/resources/extensions/gsd/post-unit-hooks.ts +32 -0
  349. package/src/resources/extensions/gsd/preferences-models.ts +74 -20
  350. package/src/resources/extensions/gsd/preferences-skills.ts +10 -16
  351. package/src/resources/extensions/gsd/preferences-types.ts +54 -1
  352. package/src/resources/extensions/gsd/preferences-validation.ts +146 -9
  353. package/src/resources/extensions/gsd/preferences.ts +20 -0
  354. package/src/resources/extensions/gsd/prompts/system.md +6 -6
  355. package/src/resources/extensions/gsd/quick.ts +11 -3
  356. package/src/resources/extensions/gsd/rule-registry.ts +111 -9
  357. package/src/resources/extensions/gsd/skill-discovery.ts +47 -23
  358. package/src/resources/extensions/gsd/slice-parallel-orchestrator.ts +3 -1
  359. package/src/resources/extensions/gsd/state/derive/from-db.ts +201 -157
  360. package/src/resources/extensions/gsd/state-reconciliation/drift/artifact-db.ts +1 -9
  361. package/src/resources/extensions/gsd/state-reconciliation/drift/completion.ts +2 -22
  362. package/src/resources/extensions/gsd/state-reconciliation/index.ts +14 -0
  363. package/src/resources/extensions/gsd/tests/auto-artifact-paths.test.ts +169 -1
  364. package/src/resources/extensions/gsd/tests/auto-loop-no-copy-artifacts.test.ts +2 -1
  365. package/src/resources/extensions/gsd/tests/auto-orchestrator.test.ts +41 -2
  366. package/src/resources/extensions/gsd/tests/auto-post-unit-artifact-diagnostic.test.ts +48 -1
  367. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +57 -0
  368. package/src/resources/extensions/gsd/tests/auto-start-clean-runtime-db-gated.test.ts +1 -1
  369. package/src/resources/extensions/gsd/tests/auto-start-project-milestone-reconcile.test.ts +29 -1
  370. package/src/resources/extensions/gsd/tests/auto-timeout-recovery-supervisor-inflight-guard.test.ts +159 -0
  371. package/src/resources/extensions/gsd/tests/auto-warning-noise-regression.test.ts +2 -1
  372. package/src/resources/extensions/gsd/tests/auto-worktree-auto-resolve.test.ts +1 -1
  373. package/src/resources/extensions/gsd/tests/auto-worktree-merge-already-merged.test.ts +177 -0
  374. package/src/resources/extensions/gsd/tests/auto-worktree-merge-branch-head.test.ts +118 -0
  375. package/src/resources/extensions/gsd/tests/auto-worktree-merge-cleanup.test.ts +115 -0
  376. package/src/resources/extensions/gsd/tests/auto-worktree-merge-code-changes.test.ts +76 -0
  377. package/src/resources/extensions/gsd/tests/auto-worktree-merge-db-ready.test.ts +140 -0
  378. package/src/resources/extensions/gsd/tests/auto-worktree-merge-integration-branch.test.ts +123 -0
  379. package/src/resources/extensions/gsd/tests/auto-worktree-merge-message.test.ts +114 -0
  380. package/src/resources/extensions/gsd/tests/auto-worktree-merge-pre-teardown.test.ts +84 -0
  381. package/src/resources/extensions/gsd/tests/auto-worktree-merge-stash.test.ts +70 -0
  382. package/src/resources/extensions/gsd/tests/auto-worktree-milestone-shelter.test.ts +52 -0
  383. package/src/resources/extensions/gsd/tests/auto-worktree-registry.test.ts +6 -6
  384. package/src/resources/extensions/gsd/tests/auto-worktree-resource-version.test.ts +59 -0
  385. package/src/resources/extensions/gsd/tests/auto-worktree-untracked-content.test.ts +3 -1
  386. package/src/resources/extensions/gsd/tests/codebase-generator.test.ts +22 -0
  387. package/src/resources/extensions/gsd/tests/commands-verdict.test.ts +3 -1
  388. package/src/resources/extensions/gsd/tests/compat-marker.test.ts +85 -0
  389. package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +45 -0
  390. package/src/resources/extensions/gsd/tests/completed-units-metrics-sync.test.ts +1 -1
  391. package/src/resources/extensions/gsd/tests/db-adapter.test.ts +39 -0
  392. package/src/resources/extensions/gsd/tests/db-writable-probe.test.ts +66 -0
  393. package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +4 -0
  394. package/src/resources/extensions/gsd/tests/doctor-scope-db-unavailable.test.ts +174 -0
  395. package/src/resources/extensions/gsd/tests/fast-forward-reused-milestone-branch.test.ts +1 -1
  396. package/src/resources/extensions/gsd/tests/flat-phase-migration.test.ts +71 -1
  397. package/src/resources/extensions/gsd/tests/flat-phase-renderer.test.ts +46 -1
  398. package/src/resources/extensions/gsd/tests/flat-phase-validation-path.test.ts +72 -32
  399. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +40 -0
  400. package/src/resources/extensions/gsd/tests/import-completion-lost-update.test.ts +196 -0
  401. package/src/resources/extensions/gsd/tests/integration/all-milestones-complete-merge.test.ts +4 -6
  402. package/src/resources/extensions/gsd/tests/integration/auto-stash-merge.test.ts +2 -1
  403. package/src/resources/extensions/gsd/tests/integration/auto-worktree-milestone-merge.test.ts +3 -3
  404. package/src/resources/extensions/gsd/tests/integration/auto-worktree.test.ts +7 -8
  405. package/src/resources/extensions/gsd/tests/integration/feature-branch-lifecycle-integration.test.ts +2 -5
  406. package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +76 -0
  407. package/src/resources/extensions/gsd/tests/integration/merge-cwd-restore.test.ts +1 -1
  408. package/src/resources/extensions/gsd/tests/integration/merge-preserve-worktree-on-preteardown-error.test.ts +1 -1
  409. package/src/resources/extensions/gsd/tests/integration/merge-strategy-regular.test.ts +2 -1
  410. package/src/resources/extensions/gsd/tests/integration/milestone-transition-worktree.test.ts +5 -7
  411. package/src/resources/extensions/gsd/tests/integration/paths.test.ts +88 -1
  412. package/src/resources/extensions/gsd/tests/integration/workspace-collapse-integration.test.ts +2 -4
  413. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +16 -15
  414. package/src/resources/extensions/gsd/tests/merge-closeout-consistency-gate.test.ts +1 -1
  415. package/src/resources/extensions/gsd/tests/merge-db-cycle.test.ts +1 -1
  416. package/src/resources/extensions/gsd/tests/merge-self-branch-guard.test.ts +1 -1
  417. package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +91 -0
  418. package/src/resources/extensions/gsd/tests/milestone-closeout.test.ts +2 -1
  419. package/src/resources/extensions/gsd/tests/milestone-merge-stash-restore.test.ts +439 -0
  420. package/src/resources/extensions/gsd/tests/milestone-merge-transaction.test.ts +24 -0
  421. package/src/resources/extensions/gsd/tests/none-mode-gates.test.ts +1 -1
  422. package/src/resources/extensions/gsd/tests/originalbase-path-comparison.test.ts +3 -3
  423. package/src/resources/extensions/gsd/tests/parallel-worker-lock-contention.test.ts +1 -1
  424. package/src/resources/extensions/gsd/tests/parsers-legacy-importers.test.ts +0 -1
  425. package/src/resources/extensions/gsd/tests/post-unit-hooks.test.ts +269 -0
  426. package/src/resources/extensions/gsd/tests/post-unit-retry-on-orchestrator-bridge.test.ts +8 -1
  427. package/src/resources/extensions/gsd/tests/preferences-worktree-sync.test.ts +1 -4
  428. package/src/resources/extensions/gsd/tests/preferences.test.ts +19 -0
  429. package/src/resources/extensions/gsd/tests/prefs-wizard-coverage.test.ts +5 -0
  430. package/src/resources/extensions/gsd/tests/read-uat-gate-verdict.test.ts +54 -0
  431. package/src/resources/extensions/gsd/tests/reassess-handler.test.ts +49 -1
  432. package/src/resources/extensions/gsd/tests/reopen-slice.test.ts +52 -0
  433. package/src/resources/extensions/gsd/tests/rule-registry.test.ts +95 -1
  434. package/src/resources/extensions/gsd/tests/runtime-invariant-modules.test.ts +20 -0
  435. package/src/resources/extensions/gsd/tests/sibling-model-fallbacks.test.ts +189 -0
  436. package/src/resources/extensions/gsd/tests/single-field-model-thinking.test.ts +156 -0
  437. package/src/resources/extensions/gsd/tests/skill-discovery.test.ts +46 -0
  438. package/src/resources/extensions/gsd/tests/skipped-validation-db-atomicity.test.ts +6 -1
  439. package/src/resources/extensions/gsd/tests/stale-worktree-cwd.test.ts +3 -5
  440. package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +32 -0
  441. package/src/resources/extensions/gsd/tests/stash-pop-gsd-conflict.test.ts +2 -1
  442. package/src/resources/extensions/gsd/tests/stash-queued-context-files.test.ts +2 -1
  443. package/src/resources/extensions/gsd/tests/steer-worktree-path.test.ts +1 -1
  444. package/src/resources/extensions/gsd/tests/supervisor-model-swap-inflight-guard.test.ts +181 -0
  445. package/src/resources/extensions/gsd/tests/supervisor-model-swap-thinking-clamp.test.ts +122 -0
  446. package/src/resources/extensions/gsd/tests/sync-worktree-skip-current.test.ts +1 -1
  447. package/src/resources/extensions/gsd/tests/system-flat-phase-layout.test.ts +33 -0
  448. package/src/resources/extensions/gsd/tests/teardown-chdir-failure-clears-registry.test.ts +4 -4
  449. package/src/resources/extensions/gsd/tests/teardown-cleanup-parity.test.ts +2 -1
  450. package/src/resources/extensions/gsd/tests/teardown-failure-clears-registry.test.ts +4 -4
  451. package/src/resources/extensions/gsd/tests/tool-call-loop-guard.test.ts +196 -0
  452. package/src/resources/extensions/gsd/tests/uat-dispatch.test.ts +211 -0
  453. package/src/resources/extensions/gsd/tests/uat-stuck-loop-orphaned-worktree.test.ts +1 -1
  454. package/src/resources/extensions/gsd/tests/unit-registry.test.ts +26 -1
  455. package/src/resources/extensions/gsd/tests/uok-gate-runner.test.ts +39 -0
  456. package/src/resources/extensions/gsd/tests/uok-kernel-path.test.ts +226 -2
  457. package/src/resources/extensions/gsd/tests/uok-model-policy.test.ts +6 -0
  458. package/src/resources/extensions/gsd/tests/workflow-projections.test.ts +213 -2
  459. package/src/resources/extensions/gsd/tests/worktree-db-integration.test.ts +1 -1
  460. package/src/resources/extensions/gsd/tests/worktree-db-respawn-truncation.test.ts +1 -1
  461. package/src/resources/extensions/gsd/tests/worktree-db-same-file.test.ts +1 -3
  462. package/src/resources/extensions/gsd/tests/worktree-expected-warnings.test.ts +1 -1
  463. package/src/resources/extensions/gsd/tests/worktree-git-pathspec.test.ts +1 -1
  464. package/src/resources/extensions/gsd/tests/worktree-main-branch.test.ts +1 -1
  465. package/src/resources/extensions/gsd/tests/worktree-preferences-sync.test.ts +1 -1
  466. package/src/resources/extensions/gsd/tests/worktree-reentry.test.ts +2 -1
  467. package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +5 -2
  468. package/src/resources/extensions/gsd/tests/worktree-sync-overwrite-loop.test.ts +1 -1
  469. package/src/resources/extensions/gsd/tests/worktree-sync-tasks.test.ts +1 -1
  470. package/src/resources/extensions/gsd/tool-contract.ts +29 -0
  471. package/src/resources/extensions/gsd/tools/complete-milestone.ts +9 -5
  472. package/src/resources/extensions/gsd/tools/reassess-roadmap.ts +19 -8
  473. package/src/resources/extensions/gsd/tools/reopen-slice.ts +5 -0
  474. package/src/resources/extensions/gsd/tools/reopen-task.ts +13 -5
  475. package/src/resources/extensions/gsd/tools/validate-milestone.ts +13 -9
  476. package/src/resources/extensions/gsd/types.ts +23 -4
  477. package/src/resources/extensions/gsd/uat-dispatch.ts +176 -0
  478. package/src/resources/extensions/gsd/unit-registry.ts +16 -2
  479. package/src/resources/extensions/gsd/unmerged-milestone-guard.ts +1 -1
  480. package/src/resources/extensions/gsd/uok/audit-toggle.ts +32 -2
  481. package/src/resources/extensions/gsd/uok/audit.ts +2 -0
  482. package/src/resources/extensions/gsd/uok/gate-runner.ts +26 -36
  483. package/src/resources/extensions/gsd/uok/gitops.ts +21 -18
  484. package/src/resources/extensions/gsd/uok/kernel.ts +169 -48
  485. package/src/resources/extensions/gsd/uok/model-policy.ts +20 -17
  486. package/src/resources/extensions/gsd/validation-block-guard.ts +2 -1
  487. package/src/resources/extensions/gsd/workflow-event-ledger.ts +1 -1
  488. package/src/resources/extensions/gsd/workflow-logger.ts +1 -1
  489. package/src/resources/extensions/gsd/workflow-projections.ts +9 -24
  490. package/src/resources/extensions/gsd/workspace-git-preflight.ts +2 -2
  491. package/src/resources/extensions/gsd/workspace.ts +5 -21
  492. package/src/resources/extensions/gsd/worktree-command.ts +1 -1
  493. package/src/resources/extensions/gsd/worktree-lifecycle.ts +19 -15
  494. package/src/resources/extensions/gsd/worktree-reentry.ts +2 -1
  495. package/src/resources/extensions/shared/gsd-browser-cli.ts +29 -0
  496. package/src/resources/extensions/shared/interview-ui.ts +58 -11
  497. package/src/resources/extensions/shared/tests/interview-ui-border.test.ts +32 -0
  498. /package/dist/web/standalone/.next/static/{bCg3aYrFN1DKZr2dqgE7L → 6xYMOzxVhsQgryneJ5KRc}/_buildManifest.js +0 -0
  499. /package/dist/web/standalone/.next/static/{bCg3aYrFN1DKZr2dqgE7L → 6xYMOzxVhsQgryneJ5KRc}/_ssgManifest.js +0 -0
@@ -19,10 +19,11 @@ import {
19
19
  resolveSlicePath,
20
20
  resolveTasksDir,
21
21
  dirIsMetaOnlyLegacyMilestone,
22
+ normalizeRealPath,
22
23
  } from "./paths.js";
23
24
  import { milestoneIdToPhaseNum } from "./layout-policy.js";
24
25
  import { parseUnitId } from "./unit-id.js";
25
- import { dirname, join, relative } from "node:path";
26
+ import { basename, dirname, join, relative } from "node:path";
26
27
  import { existsSync } from "node:fs";
27
28
 
28
29
  function resolveMilestoneArtifactPath(
@@ -221,8 +222,17 @@ export function resolveExpectedArtifactPath(
221
222
  ?? resolveProjectSlicePath(base, mid, sid!)
222
223
  ?? resolveSlicePath(base, mid, sid!);
223
224
  if (!slicePath || !tid) return null;
224
- const tasksDir = resolveTasksDir(base, mid, sid!) ?? slicePath;
225
- return join(tasksDir, buildTaskFileName(tid, "SUMMARY"));
225
+ // Legacy layout: slice dirs live under slices/<SID>/ and task summaries
226
+ // live in a tasks/ subdir. Flat-phase layout: slicePath IS the phase dir
227
+ // and task summaries live beside the plan files at the phase root. A
228
+ // tasks/ subdir may still exist in flat-phase for auxiliary task-scoped
229
+ // artifacts (e.g. T01-VERIFY.json gate outputs) — its mere existence must
230
+ // NOT redirect summary resolution into tasks/ (#1208).
231
+ const isLegacySlice = basename(dirname(slicePath)) === "slices";
232
+ const summaryDir = isLegacySlice
233
+ ? (resolveTasksDir(base, mid, sid!) ?? slicePath)
234
+ : slicePath;
235
+ return join(summaryDir, buildTaskFileName(tid, "SUMMARY"));
226
236
  }
227
237
  case "complete-slice": {
228
238
  return resolveSliceArtifactPath(base, mid, sid!, "SUMMARY");
@@ -345,7 +355,7 @@ export function resolveSliceResearchLocation(
345
355
  if (canonicalPath && existsSync(canonicalPath)) {
346
356
  return {
347
357
  absolutePath: canonicalPath,
348
- relativePath: relative(basePath, canonicalPath),
358
+ relativePath: relative(normalizeRealPath(basePath), canonicalPath),
349
359
  };
350
360
  }
351
361
 
@@ -27,11 +27,11 @@ import {
27
27
  buildRunUatPrompt,
28
28
  buildReplanSlicePrompt,
29
29
  } from "./auto-prompts.js";
30
- import { loadEffectiveGSDPreferences } from "./preferences.js";
30
+ import { loadEffectiveGSDPreferences, renderLanguageDirectiveForPrompt } from "./preferences.js";
31
31
  import type { MinimalModelRegistry } from "./context-budget.js";
32
32
  import { pauseAuto } from "./auto.js";
33
33
  import { resolveCanonicalMilestoneRoot } from "./worktree-manager.js";
34
- import { getUnitWorkflowDispatchReadinessError } from "./tool-contract.js";
34
+ import { getUnitWorkflowDispatchReadinessErrorForModel } from "./tool-contract.js";
35
35
 
36
36
  export function parseDirectDispatchPhase(raw: string): { phase: string; milestoneId?: string } {
37
37
  const tokens = raw.trim().split(/\s+/).filter(Boolean);
@@ -266,13 +266,12 @@ export async function dispatchDirectPhase(
266
266
  return;
267
267
  }
268
268
 
269
- const compatibilityError = getUnitWorkflowDispatchReadinessError({
270
- provider: ctx.model?.provider,
269
+ const compatibilityError = getUnitWorkflowDispatchReadinessErrorForModel({
270
+ model: ctx.model,
271
+ getProviderAuthMode: (provider) => ctx.modelRegistry.getProviderAuthMode(provider),
271
272
  projectRoot,
272
273
  surface: "direct phase dispatch",
273
274
  unitType,
274
- authMode: ctx.model?.provider ? ctx.modelRegistry.getProviderAuthMode(ctx.model.provider) : undefined,
275
- baseUrl: ctx.model?.baseUrl,
276
275
  activeTools: typeof pi.getActiveTools === "function" ? pi.getActiveTools() : [],
277
276
  });
278
277
  if (compatibilityError) {
@@ -287,8 +286,15 @@ export async function dispatchDirectPhase(
287
286
  ctx.ui.notify("Session creation cancelled.", "warning");
288
287
  return;
289
288
  }
289
+ // Inject the configured response language into the dispatched prompt content
290
+ // — the new session's system prompt does not carry the preferences block, so
291
+ // the language setting would otherwise never reach the unit (#1210).
292
+ const languageDirective = renderLanguageDirectiveForPrompt(
293
+ loadEffectiveGSDPreferences(dispatchBase)?.preferences,
294
+ );
295
+ const dispatchContent = languageDirective ? `${languageDirective}\n\n${prompt}` : prompt;
290
296
  pi.sendMessage(
291
- { customType: "gsd-dispatch", content: prompt, display: false },
297
+ { customType: "gsd-dispatch", content: dispatchContent, display: false },
292
298
  { triggerTurn: true },
293
299
  );
294
300
  }
@@ -14,6 +14,7 @@
14
14
 
15
15
  import type { GSDState, TaskIO } from "./types.js";
16
16
  import type { GSDPreferences } from "./preferences.js";
17
+ import { renderLanguageDirectiveForPrompt } from "./preferences.js";
17
18
  import type { MinimalModelRegistry } from "./context-budget.js";
18
19
  import { loadFile, extractUatType, loadActiveOverrides } from "./files.js";
19
20
  import { getUatBrowserToolSupportError, type UatType } from "./uat-policy.js";
@@ -78,9 +79,10 @@ import {
78
79
  buildGateEvaluatePrompt,
79
80
  buildParallelResearchSlicesPrompt,
80
81
  checkNeedsReassessment,
81
- checkNeedsRunUat,
82
+ loadRoadmapCompletedSliceCandidates,
82
83
  } from "./auto-prompts.js";
83
- import { resolveModelWithFallbacksForUnit, resolveThinkingLevelForUnit } from "./preferences-models.js";
84
+ import { checkNeedsRunUat } from "./uat-dispatch.js";
85
+ import { normalizeModelFieldConfig, resolveModelWithFallbacksForUnit, resolveThinkingLevelForUnit } from "./preferences-models.js";
84
86
  import { resolveUokFlags } from "./uok/flags.js";
85
87
  import { selectReactiveDispatchBatch } from "./uok/execution-graph.js";
86
88
  import { getMilestonePipelineVariant } from "./milestone-scope-classifier.js";
@@ -307,12 +309,21 @@ export async function readUatGateVerdict(
307
309
  return null;
308
310
  }
309
311
 
310
- const assessmentVerdict = extractVerdict(assessmentContent);
311
- if (assessmentVerdict) {
312
- return {
313
- verdict: assessmentVerdict,
314
- uatType: uatType ?? extractUatType(assessmentContent),
315
- };
312
+ // Backfilled assessments (#1258) are placeholders created during milestone
313
+ // validation for completed slices that never produced a real UAT ASSESSMENT
314
+ // (e.g. artifact-driven UAT that was never dispatched). Their fabricated
315
+ // verdict must not be treated as a genuine UAT sign-off — otherwise "never
316
+ // checked" is silently read as "passed". Skip the placeholder and fall
317
+ // through to the authoritative run-uat DB row (which stays null unless a
318
+ // real UAT was actually recorded).
319
+ if (assessmentScope !== BACKFILL_ASSESSMENT_SCOPE) {
320
+ const assessmentVerdict = extractVerdict(assessmentContent);
321
+ if (assessmentVerdict) {
322
+ return {
323
+ verdict: assessmentVerdict,
324
+ uatType: uatType ?? extractUatType(assessmentContent),
325
+ };
326
+ }
316
327
  }
317
328
  }
318
329
 
@@ -488,17 +499,28 @@ function stripGsdPrefix(path: string): string {
488
499
  return path.startsWith(".gsd/") ? path.slice(".gsd/".length) : path;
489
500
  }
490
501
 
502
+ // Scope marker for assessments fabricated by the milestone-validation backfill
503
+ // (#1258). It keeps these placeholders distinguishable from genuine `run-uat`
504
+ // sign-offs so `readUatGateVerdict` never mistakes "never checked" for "passed".
505
+ const BACKFILL_ASSESSMENT_SCOPE = "backfill";
506
+
491
507
  function persistSliceAssessmentBackfill(
492
508
  assessmentRelPath: string,
493
509
  mid: string,
494
510
  sliceId: string,
495
511
  content: string,
512
+ fabricated: boolean,
496
513
  ): void {
497
514
  const artifactPath = stripGsdPrefix(assessmentRelPath);
498
515
  const existingAssessment =
499
516
  getAssessment(assessmentRelPath) ??
500
517
  getAssessment(artifactPath);
501
- const scope = stringField(existingAssessment, "scope") ?? "run-uat";
518
+ // A newly fabricated placeholder is filed under a distinct scope; a pre-existing
519
+ // on-disk ASSESSMENT keeps its real scope (default `run-uat`) so genuine
520
+ // sign-offs are still honored.
521
+ const scope = fabricated
522
+ ? BACKFILL_ASSESSMENT_SCOPE
523
+ : stringField(existingAssessment, "scope") ?? "run-uat";
502
524
  const status = stringField(existingAssessment, "status") ??
503
525
  extractVerdict(content)?.toLowerCase() ??
504
526
  "unknown";
@@ -547,19 +569,25 @@ function backfillMissingAssessmentsFromSummaries(basePath: string, mid: string):
547
569
  "---",
548
570
  `sliceId: ${sliceId}`,
549
571
  "verdict: PASS",
572
+ // Distinguishing marker (#1258): this ASSESSMENT was fabricated to satisfy
573
+ // the per-slice artifact requirement, NOT produced by a real UAT run. It
574
+ // must not be read as a genuine UAT sign-off.
575
+ "backfilled: true",
576
+ "verified: false",
550
577
  `date: ${now}`,
551
578
  "---",
552
579
  "",
553
580
  `# Assessment — ${sliceId}`,
554
581
  "",
555
582
  "Auto-created during milestone validation because this completed slice had a SUMMARY but no ASSESSMENT artifact.",
583
+ "This is a placeholder: no UAT was executed for this slice, so its verdict is not an independent sign-off.",
556
584
  "No additional reassessment changes were detected in this backfill step.",
557
585
  "",
558
586
  ].join("\n") : readFileSync(assessmentPath, "utf-8");
559
587
 
560
588
  if (isDbAvailable()) {
561
589
  try {
562
- persistSliceAssessmentBackfill(assessmentRelPath, mid, sliceId, content);
590
+ persistSliceAssessmentBackfill(assessmentRelPath, mid, sliceId, content, didCreateAssessment);
563
591
  } catch (err) {
564
592
  logWarning("dispatch", `failed to backfill assessment DB rows for ${mid}/${sliceId}: ${(err as Error).message}`);
565
593
  }
@@ -768,7 +796,12 @@ export const DISPATCH_RULES: DispatchRule[] = [
768
796
  registeredTools,
769
797
  sessionBaseUrl,
770
798
  }) => {
771
- const needsRunUat = await checkNeedsRunUat(basePath, mid, state, prefs);
799
+ const needsRunUat = await checkNeedsRunUat(
800
+ basePath,
801
+ mid,
802
+ prefs,
803
+ await loadRoadmapCompletedSliceCandidates(basePath, mid),
804
+ );
772
805
  if (!needsRunUat) return null;
773
806
  const { sliceId, uatType } = needsRunUat;
774
807
 
@@ -1427,8 +1460,15 @@ export const DISPATCH_RULES: DispatchRule[] = [
1427
1460
  const sTitle = state.activeSlice.title;
1428
1461
  if (resolveSliceFile(basePath, mid, sid, "REACTIVE-BLOCKER")) return null;
1429
1462
  const maxParallel = reactiveConfig?.max_parallel ?? 2;
1430
- const subagentModel = reactiveConfig?.subagent_model ?? resolveModelWithFallbacksForUnit("subagent")?.primary;
1431
- const subagentThinking = resolveThinkingLevelForUnit("subagent");
1463
+ // `subagent_model` accepts the phase-bucket object form (#1229); honor the
1464
+ // full primary→fallbacks chain in the dispatch prompt (the reactive
1465
+ // subagent model is embedded there rather than set as the session model).
1466
+ const subagentModelConfig = normalizeModelFieldConfig(reactiveConfig?.subagent_model)
1467
+ ?? resolveModelWithFallbacksForUnit("subagent");
1468
+ const subagentModel = subagentModelConfig?.primary;
1469
+ // Prefer the per-field `thinking` carried on `reactive_execution.subagent_model`'s
1470
+ // object form over the phase-bucket `subagent` level (#1269).
1471
+ const subagentThinking = subagentModelConfig?.thinking ?? resolveThinkingLevelForUnit("subagent");
1432
1472
  // Default-on safety threshold: only activate reactive dispatch when at
1433
1473
  // least N tasks are ready. Users who explicitly enabled reactive_execution
1434
1474
  // keep the legacy threshold of 2 (matches the prior "any parallelism is
@@ -1517,7 +1557,13 @@ export const DISPATCH_RULES: DispatchRule[] = [
1517
1557
  selected,
1518
1558
  basePath,
1519
1559
  subagentModel,
1520
- { sessionContextWindow, modelRegistry, sessionProvider, subagentThinking },
1560
+ {
1561
+ sessionContextWindow,
1562
+ modelRegistry,
1563
+ sessionProvider,
1564
+ subagentThinking,
1565
+ subagentModelFallbacks: subagentModelConfig?.fallbacks,
1566
+ },
1521
1567
  ),
1522
1568
  };
1523
1569
  } catch (err) {
@@ -1834,6 +1880,21 @@ export const DISPATCH_RULES: DispatchRule[] = [
1834
1880
 
1835
1881
  import { getRegistry } from "./rule-registry.js";
1836
1882
 
1883
+ /**
1884
+ * Prepend the configured response-language directive to a dispatched unit
1885
+ * prompt so auto-execution units respond in the language set in PREFERENCES.md
1886
+ * (#1210). No-op for non-dispatch actions or when no language is configured.
1887
+ */
1888
+ function applyLanguageDirectiveToDispatch(
1889
+ action: DispatchAction,
1890
+ prefs: GSDPreferences | undefined,
1891
+ ): DispatchAction {
1892
+ if (action.action !== "dispatch" || !action.prompt) return action;
1893
+ const directive = renderLanguageDirectiveForPrompt(prefs);
1894
+ if (!directive) return action;
1895
+ return { ...action, prompt: `${directive}\n\n${action.prompt}` };
1896
+ }
1897
+
1837
1898
  // ─── Resolver ─────────────────────────────────────────────────────────────
1838
1899
 
1839
1900
  /**
@@ -1896,7 +1957,7 @@ export async function resolveDispatch(
1896
1957
  level: "error",
1897
1958
  };
1898
1959
  }
1899
- return action;
1960
+ return applyLanguageDirectiveToDispatch(action, ctx.prefs);
1900
1961
  } catch (err) {
1901
1962
  // Registry not initialized — fall back to inline loop
1902
1963
  logWarning("dispatch", `registry dispatch failed, falling back to inline rules: ${err instanceof Error ? err.message : String(err)}`);
@@ -1918,7 +1979,7 @@ export async function resolveDispatch(
1918
1979
  matchedRule: rule.name,
1919
1980
  };
1920
1981
  }
1921
- return action;
1982
+ return applyLanguageDirectiveToDispatch(action, ctx.prefs);
1922
1983
  }
1923
1984
  }
1924
1985
 
@@ -273,7 +273,7 @@ function buildModelPolicyBlockReasons(
273
273
  }];
274
274
  }
275
275
 
276
- function isModelUnavailable(
276
+ export function isModelUnavailable(
277
277
  basePath: string,
278
278
  provider: string | undefined,
279
279
  id: string | undefined,
@@ -282,6 +282,38 @@ function isModelUnavailable(
282
282
  isModelTemporarilyUnavailable(basePath, provider, id);
283
283
  }
284
284
 
285
+ /** Apply `auto_supervisor.model`, walking configured fallbacks on unavailability (#1229). */
286
+ export async function applySupervisorModelIfConfigured(
287
+ ctx: ExtensionContext,
288
+ pi: ExtensionAPI,
289
+ basePath: string,
290
+ ): Promise<void> {
291
+ const modelConfig = resolveModelWithFallbacksForUnit("supervisor", basePath);
292
+ if (!modelConfig) return;
293
+
294
+ const availableModels = ctx.modelRegistry.getAvailable();
295
+ for (const candidate of [modelConfig.primary, ...modelConfig.fallbacks]) {
296
+ const match = resolveModelId(candidate, availableModels, ctx.model?.provider);
297
+ if (!match) continue;
298
+ if (isModelUnavailable(basePath, match.provider, match.id)) continue;
299
+ try {
300
+ if (await pi.setModel(match, { persist: false })) {
301
+ // Prefer the per-field `thinking` from `auto_supervisor.model`'s object
302
+ // form over the session level so a configured supervisor level actually
303
+ // applies (#1269).
304
+ applyThinkingLevelForModel(pi, modelConfig.thinking ?? pi.getThinkingLevel(), match, ctx);
305
+ return;
306
+ }
307
+ } catch (err) {
308
+ // Non-fatal — try the next fallback.
309
+ logWarning(
310
+ "dispatch",
311
+ `supervisor model set failed for ${candidate}: ${err instanceof Error ? err.message : String(err)}`,
312
+ );
313
+ }
314
+ }
315
+ }
316
+
285
317
  function restoreToolBaseline(pi: ExtensionAPI): void {
286
318
  const key = pi as unknown as object;
287
319
  const baseline = TOOL_BASELINE.get(key);
@@ -30,6 +30,7 @@ import {
30
30
  relMilestoneFile,
31
31
  relSliceFile,
32
32
  relTaskFile,
33
+ normalizeRealPath,
33
34
  } from "./paths.js";
34
35
  import { invalidateAllCaches } from "./cache.js";
35
36
  import { rebuildState } from "./doctor.js";
@@ -960,7 +961,7 @@ function describeArtifactVerificationFailure(
960
961
  if (!artifactPath) {
961
962
  return `Artifact verification failed: ${unitType} "${unitId}" has no resolvable artifact path.`;
962
963
  }
963
- const relPath = relative(basePath, artifactPath);
964
+ const relPath = relative(normalizeRealPath(basePath), artifactPath);
964
965
  if (!existsSync(artifactPath)) {
965
966
  const completionToolHint = unitType === "execute-task" && !hasTaskCompletionToolCall(agentEndMessages)
966
967
  ? " No completion tool call detected (`gsd_task_complete`/alias)."
@@ -2398,7 +2399,8 @@ export async function postUnitPostVerification(pctx: PostUnitContext): Promise<"
2398
2399
  const verdict = gateBlock.verdict ? ` verdict=${gateBlock.verdict};` : "";
2399
2400
  const artifact = gateBlock.artifact ? ` artifact=${gateBlock.artifact};` : "";
2400
2401
  const message =
2401
- `Post-unit gate "${gateBlock.hookName}" blocked ${gateBlock.triggerUnitType} ${gateBlock.triggerUnitId}:` +
2402
+ `Post-unit gate "${gateBlock.hookName}" blocked ${gateBlock.triggerUnitType} ${gateBlock.triggerUnitId} ` +
2403
+ `(detected on completion of ${s.currentUnit.type} ${s.currentUnit.id}):` +
2402
2404
  `${verdict}${artifact} ${gateBlock.reason}. Run /gsd status to inspect, then /gsd auto after recovery.`;
2403
2405
  ctx.ui.notify(message, "warning");
2404
2406
  await pauseAuto(ctx, pi);
@@ -11,7 +11,7 @@
11
11
 
12
12
  import { loadFile, parseContinue, parseSummary, loadActiveOverrides, formatOverridesSection, parseTaskPlanFile } from "./files.js";
13
13
  import type { Override } from "./files.js";
14
- import { hasVerdict, extractVerdict } from "./verdict-parser.js";
14
+ import { extractVerdict } from "./verdict-parser.js";
15
15
  import { loadPrompt, inlineTemplate } from "./prompt-loader.js";
16
16
  import {
17
17
  resolveMilestoneFile, resolveSliceFile, resolveSlicePath,
@@ -60,11 +60,8 @@ import { debugLog } from "./debug-logger.js";
60
60
  import { buildSkillActivationBlock, buildSkillDiscoveryVars } from "./skill-activation.js";
61
61
  import { findMilestoneIds } from "./milestone-ids.js";
62
62
  import { buildRunUatPresentationForType, RUN_UAT_TOOL_PRESENTATION_PLAN_ID } from "./tool-presentation-plan.js";
63
- import {
64
- classifyUatContentForRun,
65
- shouldDispatchUatForContent,
66
- type UatType,
67
- } from "./uat-policy.js";
63
+ import { classifyUatContentForRun } from "./uat-policy.js";
64
+ import { checkNeedsRunUat as resolveNeedsRunUat, type UatDispatchCandidate } from "./uat-dispatch.js";
68
65
  import { buildWebAppUatGuidanceBlock } from "./web-app-uat.js";
69
66
 
70
67
  export { buildSkillActivationBlock, buildSkillDiscoveryVars };
@@ -1514,116 +1511,43 @@ export async function checkNeedsReassessment(
1514
1511
  return { sliceId: lastDone };
1515
1512
  }
1516
1513
 
1517
- /**
1518
- * Check if the most recently completed slice needs a UAT run.
1519
- * Returns { sliceId, uatType } if UAT should be dispatched, null otherwise.
1520
- *
1521
- * Skips when:
1522
- * - No roadmap or no completed slices
1523
- * - uat_dispatch is not enabled and the UAT spec does not require runtime/browser evidence
1524
- * - No UAT file exists for the slice
1525
- * - UAT result file already exists (idempotent — already ran)
1526
- */
1527
- /**
1528
- * Resolve the effective UAT mode for the dispatch gate the same way
1529
- * `buildRunUatPrompt` does: classify the UAT body with the slice's SUMMARY as
1530
- * supplemental context, so a `browser-executable` UAT whose slice references a
1531
- * self-contained harness (`npm run test:uat`, `search-uat.mjs`, `npx
1532
- * playwright test`) is promoted to `runtime-executable` for the gate too.
1533
- *
1534
- * Without this, `checkNeedsRunUat` returns `browser-executable` while the
1535
- * prompt instructs runtime-only execution, causing the dispatch gate to
1536
- * require browser tools / warm up the browser daemon (or stop dispatch when
1537
- * browser MCP is unavailable) for UAT runs that never touch the browser. See
1538
- * cursor[bot] review on PR #696 for the M007/S01 regression.
1539
- */
1540
- async function resolveRunUatEffectiveType(
1514
+
1515
+ export function getRoadmapCompletedSliceCandidates(
1516
+ roadmapContent: string,
1517
+ ): UatDispatchCandidate[] {
1518
+ return parseRoadmap(roadmapContent)
1519
+ .slices.filter((slice) => slice.done)
1520
+ .map((slice) => ({ sliceId: slice.id }))
1521
+ .reverse();
1522
+ }
1523
+
1524
+
1525
+ export async function loadRoadmapCompletedSliceCandidates(
1541
1526
  base: string,
1542
1527
  mid: string,
1543
- sliceId: string,
1544
- uatContent: string,
1545
- ): Promise<UatType> {
1546
- let summaryContent = "";
1547
- try {
1548
- const summaryPath = resolveSliceFile(base, mid, sliceId, "SUMMARY");
1549
- if (summaryPath) {
1550
- summaryContent = (await loadFile(summaryPath)) ?? "";
1551
- }
1552
- } catch (err) {
1553
- logWarning(
1554
- "prompt",
1555
- `resolveRunUatEffectiveType SUMMARY load failed: ${err instanceof Error ? err.message : String(err)}`,
1556
- );
1557
- }
1558
- return classifyUatContentForRun(uatContent, summaryContent).effectiveType;
1528
+ ): Promise<UatDispatchCandidate[]> {
1529
+ const roadmapPath = resolveMilestoneFile(base, mid, "ROADMAP");
1530
+ const roadmapContent = roadmapPath ? await loadFile(roadmapPath) : null;
1531
+ return roadmapContent ? getRoadmapCompletedSliceCandidates(roadmapContent) : [];
1559
1532
  }
1560
1533
 
1534
+ /**
1535
+ * Back-compat wrapper for callers that import UAT dispatch checks from the
1536
+ * prompt module. UAT dispatch architecture lives in `uat-dispatch.ts`; the
1537
+ * `state` parameter is intentionally ignored because DB/roadmap artifacts are
1538
+ * the source of truth for completed-slice discovery.
1539
+ */
1561
1540
  export async function checkNeedsRunUat(
1562
1541
  base: string, mid: string, state: GSDState, prefs: GSDPreferences | undefined,
1563
- ): Promise<{ sliceId: string; uatType: UatType } | null> {
1564
- // DB primary path — fall through to file-based when DB has no data for this milestone
1565
- try {
1566
- const { isDbAvailable, getMilestoneSlices } = await import("./gsd-db.js");
1567
- if (isDbAvailable()) {
1568
- const slices = getMilestoneSlices(mid);
1569
- if (slices.length > 0) {
1570
- const completedSlices = slices.filter(s => s.status === "complete");
1571
- if (completedSlices.length === 0) return null;
1572
- for (const completed of [...completedSlices].reverse()) {
1573
- const sid = completed.id;
1574
- const uatFile = resolveSliceFile(base, mid, sid, "UAT");
1575
- if (!uatFile) continue;
1576
- const uatContent = await loadFile(uatFile);
1577
- if (!uatContent) continue;
1578
- // If the UAT file already contains a verdict, UAT has been run — skip
1579
- if (hasVerdict(uatContent)) continue;
1580
- // Also check the ASSESSMENT file — the run-uat prompt writes the verdict
1581
- // there (via gsd_uat_result_save), not into the
1582
- // UAT spec file. Without this check the unit re-dispatches indefinitely.
1583
- const assessmentFile = resolveSliceFile(base, mid, sid, "ASSESSMENT");
1584
- if (assessmentFile) {
1585
- const assessmentContent = await loadFile(assessmentFile);
1586
- if (assessmentContent && hasVerdict(assessmentContent)) continue;
1587
- }
1588
- if (!shouldDispatchUatForContent(uatContent, prefs)) continue;
1589
- const uatType = await resolveRunUatEffectiveType(base, mid, sid, uatContent);
1590
- return { sliceId: sid, uatType };
1591
- }
1592
- return null;
1593
- }
1594
- }
1595
- } catch (err) {
1596
- logWarning("prompt", `checkNeedsRunUat DB lookup failed: ${err instanceof Error ? err.message : String(err)}`);
1597
- }
1542
+ ): Promise<Awaited<ReturnType<typeof resolveNeedsRunUat>>> {
1543
+ void state;
1598
1544
 
1599
- // File-based fallback using roadmap checkboxes
1600
- const roadmapPath = resolveMilestoneFile(base, mid, "ROADMAP");
1601
- if (!roadmapPath) return null;
1602
- const roadmapContent = await loadFile(roadmapPath);
1603
- if (!roadmapContent) return null;
1604
- const parsed = parseRoadmap(roadmapContent);
1605
- const completedFileSlices = parsed.slices.filter(s => s.done);
1606
- if (completedFileSlices.length === 0) return null;
1607
- for (const completed of [...completedFileSlices].reverse()) {
1608
- const uatSid = completed.id;
1609
- const uatFileFb = resolveSliceFile(base, mid, uatSid, "UAT");
1610
- if (!uatFileFb) continue;
1611
- const uatContentFb = await loadFile(uatFileFb);
1612
- if (!uatContentFb) continue;
1613
- // If the UAT file already contains a verdict, UAT has been run — skip
1614
- if (hasVerdict(uatContentFb)) continue;
1615
- // Also check the ASSESSMENT file for the file-based fallback path (same
1616
- // reason as the DB path above — verdict lives in ASSESSMENT, not UAT).
1617
- const assessmentFileFb = resolveSliceFile(base, mid, uatSid, "ASSESSMENT");
1618
- if (assessmentFileFb) {
1619
- const assessmentContentFb = await loadFile(assessmentFileFb);
1620
- if (assessmentContentFb && hasVerdict(assessmentContentFb)) continue;
1621
- }
1622
- if (!shouldDispatchUatForContent(uatContentFb, prefs)) continue;
1623
- const uatType = await resolveRunUatEffectiveType(base, mid, uatSid, uatContentFb);
1624
- return { sliceId: uatSid, uatType };
1625
- }
1626
- return null;
1545
+ return resolveNeedsRunUat(
1546
+ base,
1547
+ mid,
1548
+ prefs,
1549
+ await loadRoadmapCompletedSliceCandidates(base, mid),
1550
+ );
1627
1551
  }
1628
1552
 
1629
1553
  // ─── Prompt Builders ──────────────────────────────────────────────────────
@@ -3909,9 +3833,15 @@ export async function buildReassessRoadmapPrompt(
3909
3833
  * that instructs the coordinator how to dispatch a `subagent` call. Either or
3910
3834
  * both may be absent (ADR-026 / #508).
3911
3835
  */
3912
- function subagentCallSuffix(model?: string, thinking?: string): string {
3836
+ function subagentCallSuffix(model?: string, thinking?: string, fallbacks?: string[]): string {
3913
3837
  const parts: string[] = [];
3914
- if (model) parts.push(`model: "${model}"`);
3838
+ if (model) {
3839
+ let modelHint = `model: "${model}"`;
3840
+ if (fallbacks && fallbacks.length > 0) {
3841
+ modelHint += ` (fallbacks, in order: ${fallbacks.map((f) => `"${f}"`).join(", ")})`;
3842
+ }
3843
+ parts.push(modelHint);
3844
+ }
3915
3845
  if (thinking) parts.push(`thinking: "${thinking}"`);
3916
3846
  return parts.length > 0 ? ` with ${parts.join(" and ")}` : "";
3917
3847
  }
@@ -3922,7 +3852,13 @@ export async function buildReactiveExecutePrompt(
3922
3852
  subagentModel?: string,
3923
3853
  // Reasoning effort travels inside opts here (not as a positional param) so
3924
3854
  // existing positional `opts` callers don't shift (#508).
3925
- opts?: { sessionContextWindow?: number; modelRegistry?: MinimalModelRegistry; sessionProvider?: string; subagentThinking?: string },
3855
+ opts?: {
3856
+ sessionContextWindow?: number;
3857
+ modelRegistry?: MinimalModelRegistry;
3858
+ sessionProvider?: string;
3859
+ subagentThinking?: string;
3860
+ subagentModelFallbacks?: string[];
3861
+ },
3926
3862
  ): Promise<string> {
3927
3863
  const { loadSliceTaskIO, deriveTaskGraph, graphMetrics } = await import("./reactive-graph.js");
3928
3864
 
@@ -4019,7 +3955,11 @@ export async function buildReactiveExecutePrompt(
4019
3955
  `When done, say: "Task ${tid} complete."`,
4020
3956
  ].join("\n");
4021
3957
 
4022
- const modelSuffix = subagentCallSuffix(subagentModel, opts?.subagentThinking);
3958
+ const modelSuffix = subagentCallSuffix(
3959
+ subagentModel,
3960
+ opts?.subagentThinking,
3961
+ opts?.subagentModelFallbacks,
3962
+ );
4023
3963
  subagentSections.push([
4024
3964
  `### ${tid}: ${tTitle}`,
4025
3965
  "",
@@ -234,12 +234,56 @@ export function refreshRecoveryDbForArtifact(
234
234
  }
235
235
 
236
236
  if (!isClosedStatus(task.status)) {
237
- return {
238
- ok: false,
239
- fatal: true,
240
- reason: "execute-task-artifact-db-mismatch",
241
- message: `Stuck recovery found execute-task ${unitId} artifacts, but the DB task status is still '${task.status}' after refresh.`,
242
- };
237
+ // The artifact was already confirmed on disk by verifyExpectedArtifact
238
+ // upstream (Gate 1 accepts a checked disk checkbox as proof of completion),
239
+ // but the DB task row is still open. A bare refreshWorkflowDatabaseFromDisk
240
+ // never promotes the disk checkbox into task status (the checkbox is
241
+ // rendered *from* the DB, never *into* it), so this divergence can never
242
+ // self-heal. Promote the task to align the DB with disk instead of failing
243
+ // shut — otherwise the lowest-pending derivation re-picks the same task
244
+ // forever and execute-task hard-stops with "state did not advance" (#1204).
245
+ // Mirrors the promotion pattern used by the context-exhaustion and
246
+ // reactive-execute-blocker recovery paths.
247
+ const ts = new Date().toISOString();
248
+ // Only the DB promotion is load-bearing: if it throws, nothing advanced
249
+ // and failing shut is correct. The plan checkbox rewrite and cache clears
250
+ // are best-effort follow-ups — a throw there must NOT abort recovery, or
251
+ // the DB is left complete without ever emitting the stuck-artifact-recovery
252
+ // event (#1221), stranding worktree reconciliation.
253
+ try {
254
+ updateTaskStatus(mid, sid, tid, "complete", ts);
255
+ } catch (e) {
256
+ return {
257
+ ok: false,
258
+ fatal: true,
259
+ reason: "execute-task-artifact-db-promote-failed",
260
+ message: `Stuck recovery found execute-task ${unitId} artifacts, but promoting the DB task status from '${task.status}' failed: ${getErrorMessage(e)}`,
261
+ };
262
+ }
263
+ try {
264
+ const artifactBase = resolveArtifactVerificationBase(unitId, basePath);
265
+ const planPath = resolveExpectedArtifactPath("plan-slice", `${mid}/${sid}`, artifactBase);
266
+ if (planPath && existsSync(planPath)) {
267
+ const planContent = readFileSync(planPath, "utf-8");
268
+ const updatedPlan = planContent.replace(
269
+ new RegExp(`^(\\s*-\\s+)\\[ \\]\\s+\\*\\*${tid}:`, "m"),
270
+ `$1[x] **${tid}:`,
271
+ );
272
+ if (updatedPlan !== planContent) {
273
+ atomicWriteSync(planPath, updatedPlan);
274
+ }
275
+ }
276
+ clearPathCache();
277
+ clearParseCache();
278
+ } catch (e) {
279
+ logWarning("recovery", `stuck-artifact plan checkbox sync failed after DB promotion of ${unitId}: ${getErrorMessage(e)}`);
280
+ }
281
+ // Append event so worktree reconciliation can replay this recovery completion.
282
+ try {
283
+ appendEvent(basePath, { cmd: "complete-task", params: { milestoneId: mid, sliceId: sid, taskId: tid }, ts, actor: "system", trigger_reason: "stuck-artifact-recovery" });
284
+ } catch (e) {
285
+ logWarning("recovery", `appendEvent failed for stuck-artifact task promotion: ${getErrorMessage(e)}`);
286
+ }
243
287
  }
244
288
 
245
289
  return { ok: true };