@opengsd/gsd-pi 1.1.1-dev.a5a2de8 → 1.1.1-dev.b2556262

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 (325) hide show
  1. package/dist/headless-recover.js +56 -1
  2. package/dist/resources/.managed-resources-content-hash +1 -1
  3. package/dist/resources/extensions/browser-tools/engine/managed-gsd-browser.js +18 -2
  4. package/dist/resources/extensions/browser-tools/engine/selection.js +1 -1
  5. package/dist/resources/extensions/browser-tools/extension-manifest.json +1 -1
  6. package/dist/resources/extensions/browser-tools/index.js +68 -24
  7. package/dist/resources/extensions/browser-tools/state.js +12 -0
  8. package/dist/resources/extensions/browser-tools/tools/session.js +3 -2
  9. package/dist/resources/extensions/browser-tools/utils.js +3 -3
  10. package/dist/resources/extensions/browser-tools/web-app-detect.js +52 -0
  11. package/dist/resources/extensions/gsd/auto/loop.js +4 -2
  12. package/dist/resources/extensions/gsd/auto/phases.js +87 -12
  13. package/dist/resources/extensions/gsd/auto/session.js +22 -1
  14. package/dist/resources/extensions/gsd/auto/workflow-kernel.js +1 -0
  15. package/dist/resources/extensions/gsd/auto-dispatch.js +81 -13
  16. package/dist/resources/extensions/gsd/auto-model-selection.js +154 -9
  17. package/dist/resources/extensions/gsd/auto-post-unit.js +19 -2
  18. package/dist/resources/extensions/gsd/auto-prompts.js +26 -21
  19. package/dist/resources/extensions/gsd/auto-recovery.js +4 -2
  20. package/dist/resources/extensions/gsd/auto-runtime-state.js +3 -0
  21. package/dist/resources/extensions/gsd/auto-start.js +1 -1
  22. package/dist/resources/extensions/gsd/auto-timers.js +24 -10
  23. package/dist/resources/extensions/gsd/auto.js +40 -15
  24. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +3 -3
  25. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +192 -77
  26. package/dist/resources/extensions/gsd/bootstrap/system-context.js +1 -1
  27. package/dist/resources/extensions/gsd/closeout-wizard.js +32 -9
  28. package/dist/resources/extensions/gsd/commands/handlers/auto.js +10 -0
  29. package/dist/resources/extensions/gsd/commands/handlers/ops.js +2 -9
  30. package/dist/resources/extensions/gsd/commands-maintenance.js +93 -15
  31. package/dist/resources/extensions/gsd/commands-mcp-status.js +1 -1
  32. package/dist/resources/extensions/gsd/commands-prefs-wizard.js +2 -2
  33. package/dist/resources/extensions/gsd/config-overlay.js +1 -0
  34. package/dist/resources/extensions/gsd/context-masker.js +129 -5
  35. package/dist/resources/extensions/gsd/db-writer.js +35 -0
  36. package/dist/resources/extensions/gsd/docs/preferences-reference.md +50 -1
  37. package/dist/resources/extensions/gsd/gsd-db.js +480 -172
  38. package/dist/resources/extensions/gsd/guided-flow.js +4 -1
  39. package/dist/resources/extensions/gsd/markdown-renderer.js +37 -53
  40. package/dist/resources/extensions/gsd/md-importer.js +38 -3
  41. package/dist/resources/extensions/gsd/migration-auto-check.js +126 -31
  42. package/dist/resources/extensions/gsd/parsers-legacy.js +23 -0
  43. package/dist/resources/extensions/gsd/planner-handoff.js +98 -0
  44. package/dist/resources/extensions/gsd/planning-path-scope.js +22 -4
  45. package/dist/resources/extensions/gsd/pre-execution-checks.js +10 -2
  46. package/dist/resources/extensions/gsd/preferences-models.js +111 -43
  47. package/dist/resources/extensions/gsd/preferences-types.js +13 -0
  48. package/dist/resources/extensions/gsd/preferences-validation.js +68 -3
  49. package/dist/resources/extensions/gsd/preferences.js +4 -1
  50. package/dist/resources/extensions/gsd/prompts/gate-evaluate.md +1 -1
  51. package/dist/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
  52. package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  53. package/dist/resources/extensions/gsd/prompts/refine-slice.md +1 -1
  54. package/dist/resources/extensions/gsd/prompts/run-uat.md +2 -2
  55. package/dist/resources/extensions/gsd/prompts/system.md +1 -1
  56. package/dist/resources/extensions/gsd/roadmap-slices.js +5 -1
  57. package/dist/resources/extensions/gsd/safety/content-validator.js +6 -4
  58. package/dist/resources/extensions/gsd/skill-manifest.js +12 -0
  59. package/dist/resources/extensions/gsd/source-observations.js +306 -0
  60. package/dist/resources/extensions/gsd/state-reconciliation/drift/completion.js +15 -8
  61. package/dist/resources/extensions/gsd/state-reconciliation/drift/stale-render.js +33 -5
  62. package/dist/resources/extensions/gsd/state-reconciliation/drift/stale-worker.js +34 -13
  63. package/dist/resources/extensions/gsd/state-reconciliation/index.js +39 -14
  64. package/dist/resources/extensions/gsd/state-reconciliation/spawn-gate.js +4 -4
  65. package/dist/resources/extensions/gsd/state.js +7 -3
  66. package/dist/resources/extensions/gsd/tool-contract.js +15 -1
  67. package/dist/resources/extensions/gsd/tool-presentation-plan.js +24 -2
  68. package/dist/resources/extensions/gsd/tools/complete-slice.js +28 -0
  69. package/dist/resources/extensions/gsd/tools/plan-slice.js +42 -11
  70. package/dist/resources/extensions/gsd/tools/plan-task.js +7 -1
  71. package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +62 -406
  72. package/dist/resources/extensions/gsd/uat-policy.js +130 -0
  73. package/dist/resources/extensions/gsd/uat-run.js +414 -0
  74. package/dist/resources/extensions/gsd/unit-context-manifest.js +3 -4
  75. package/dist/resources/extensions/gsd/unit-tool-contracts.js +38 -14
  76. package/dist/resources/extensions/gsd/verdict-parser.js +3 -8
  77. package/dist/resources/extensions/gsd/workflow-manifest.js +132 -5
  78. package/dist/resources/extensions/gsd/workflow-mcp.js +2 -3
  79. package/dist/resources/extensions/gsd/workflow-projections.js +8 -0
  80. package/dist/resources/extensions/gsd/worktree-manager.js +26 -0
  81. package/dist/resources/extensions/gsd/worktree-reentry.js +96 -0
  82. package/dist/resources/extensions/gsd/worktree-state-projection.js +18 -17
  83. package/dist/resources/extensions/shared/gsd-browser-cli.js +6 -0
  84. package/dist/resources/extensions/subagent/agents.js +1 -0
  85. package/dist/resources/extensions/subagent/index.js +27 -12
  86. package/dist/resources/extensions/subagent/launch.js +7 -2
  87. package/dist/web/standalone/.next/BUILD_ID +1 -1
  88. package/dist/web/standalone/.next/app-path-routes-manifest.json +6 -6
  89. package/dist/web/standalone/.next/build-manifest.json +2 -2
  90. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  91. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  92. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  93. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  94. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  95. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  96. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  97. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  98. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  99. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  100. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  101. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  102. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  103. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  104. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  105. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  106. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  107. package/dist/web/standalone/.next/server/app/index.html +1 -1
  108. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  109. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  110. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  111. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  112. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  113. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  114. package/dist/web/standalone/.next/server/app-paths-manifest.json +6 -6
  115. package/dist/web/standalone/.next/server/chunks/8357.js +1 -1
  116. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  117. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  118. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  119. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  120. package/dist/web/standalone/node_modules/@gsd/native/dist/native.js +22 -0
  121. package/dist/web/standalone/node_modules/node-pty/build/Makefile +1 -1
  122. package/package.json +4 -4
  123. package/packages/cloud-mcp-gateway/package.json +2 -2
  124. package/packages/contracts/package.json +1 -1
  125. package/packages/daemon/package.json +4 -4
  126. package/packages/gsd-agent-core/package.json +5 -5
  127. package/packages/gsd-agent-modes/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  128. package/packages/gsd-agent-modes/dist/modes/interactive/components/assistant-message.js +21 -23
  129. package/packages/gsd-agent-modes/dist/modes/interactive/components/assistant-message.js.map +1 -1
  130. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts +3 -0
  131. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  132. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js +25 -0
  133. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js.map +1 -1
  134. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.d.ts +1 -0
  135. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.d.ts.map +1 -1
  136. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js +66 -12
  137. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js.map +1 -1
  138. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  139. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js +18 -11
  140. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  141. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-chat-render.d.ts.map +1 -1
  142. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-chat-render.js +16 -0
  143. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-chat-render.js.map +1 -1
  144. package/packages/gsd-agent-modes/package.json +7 -7
  145. package/packages/mcp-server/dist/workflow-tools.js +1 -1
  146. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  147. package/packages/mcp-server/package.json +3 -3
  148. package/packages/native/dist/native.js +22 -0
  149. package/packages/native/package.json +1 -1
  150. package/packages/pi-agent-core/package.json +1 -1
  151. package/packages/pi-ai/dist/image-models.generated.d.ts +30 -0
  152. package/packages/pi-ai/dist/image-models.generated.d.ts.map +1 -1
  153. package/packages/pi-ai/dist/image-models.generated.js +30 -0
  154. package/packages/pi-ai/dist/image-models.generated.js.map +1 -1
  155. package/packages/pi-ai/dist/models.generated.d.ts +174 -29
  156. package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
  157. package/packages/pi-ai/dist/models.generated.js +178 -54
  158. package/packages/pi-ai/dist/models.generated.js.map +1 -1
  159. package/packages/pi-ai/dist/providers/transform-messages.d.ts.map +1 -1
  160. package/packages/pi-ai/dist/providers/transform-messages.js +8 -1
  161. package/packages/pi-ai/dist/providers/transform-messages.js.map +1 -1
  162. package/packages/pi-ai/package.json +1 -1
  163. package/packages/pi-coding-agent/dist/core/settings-manager.js +1 -1
  164. package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
  165. package/packages/pi-coding-agent/dist/theme/themes.js +1 -1
  166. package/packages/pi-coding-agent/dist/theme/themes.js.map +1 -1
  167. package/packages/pi-coding-agent/package.json +7 -7
  168. package/packages/pi-tui/dist/utils.d.ts +11 -0
  169. package/packages/pi-tui/dist/utils.d.ts.map +1 -1
  170. package/packages/pi-tui/dist/utils.js +119 -6
  171. package/packages/pi-tui/dist/utils.js.map +1 -1
  172. package/packages/pi-tui/package.json +2 -1
  173. package/packages/rpc-client/package.json +2 -2
  174. package/pkg/dist/theme/themes.js +1 -1
  175. package/pkg/dist/theme/themes.js.map +1 -1
  176. package/pkg/package.json +1 -1
  177. package/scripts/install/handoff.js +16 -3
  178. package/src/resources/extensions/browser-tools/engine/managed-gsd-browser.ts +21 -2
  179. package/src/resources/extensions/browser-tools/engine/selection.ts +1 -1
  180. package/src/resources/extensions/browser-tools/extension-manifest.json +1 -1
  181. package/src/resources/extensions/browser-tools/index.ts +75 -27
  182. package/src/resources/extensions/browser-tools/state.ts +13 -0
  183. package/src/resources/extensions/browser-tools/tests/browser-engine-selection.test.mjs +2 -2
  184. package/src/resources/extensions/browser-tools/tests/browser-tools-unit.test.cjs +57 -0
  185. package/src/resources/extensions/browser-tools/tests/gsd-browser-launch-config.test.mjs +37 -0
  186. package/src/resources/extensions/browser-tools/tests/web-app-detect.test.mjs +68 -0
  187. package/src/resources/extensions/browser-tools/tools/session.ts +4 -2
  188. package/src/resources/extensions/browser-tools/utils.ts +3 -3
  189. package/src/resources/extensions/browser-tools/web-app-detect.ts +63 -0
  190. package/src/resources/extensions/gsd/auto/loop-deps.ts +1 -0
  191. package/src/resources/extensions/gsd/auto/loop.ts +4 -2
  192. package/src/resources/extensions/gsd/auto/phases.ts +89 -15
  193. package/src/resources/extensions/gsd/auto/session.ts +24 -1
  194. package/src/resources/extensions/gsd/auto/workflow-kernel.ts +1 -0
  195. package/src/resources/extensions/gsd/auto-dispatch.ts +117 -12
  196. package/src/resources/extensions/gsd/auto-model-selection.ts +190 -12
  197. package/src/resources/extensions/gsd/auto-post-unit.ts +20 -2
  198. package/src/resources/extensions/gsd/auto-prompts.ts +25 -22
  199. package/src/resources/extensions/gsd/auto-recovery.ts +22 -3
  200. package/src/resources/extensions/gsd/auto-runtime-state.ts +5 -0
  201. package/src/resources/extensions/gsd/auto-start.ts +1 -1
  202. package/src/resources/extensions/gsd/auto-timers.ts +25 -9
  203. package/src/resources/extensions/gsd/auto.ts +41 -14
  204. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +3 -3
  205. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +250 -78
  206. package/src/resources/extensions/gsd/bootstrap/system-context.ts +1 -1
  207. package/src/resources/extensions/gsd/closeout-wizard.ts +47 -13
  208. package/src/resources/extensions/gsd/commands/handlers/auto.ts +9 -0
  209. package/src/resources/extensions/gsd/commands/handlers/ops.ts +2 -17
  210. package/src/resources/extensions/gsd/commands-maintenance.ts +124 -13
  211. package/src/resources/extensions/gsd/commands-mcp-status.ts +1 -1
  212. package/src/resources/extensions/gsd/commands-prefs-wizard.ts +2 -2
  213. package/src/resources/extensions/gsd/config-overlay.ts +1 -0
  214. package/src/resources/extensions/gsd/context-masker.ts +152 -5
  215. package/src/resources/extensions/gsd/db-writer.ts +38 -0
  216. package/src/resources/extensions/gsd/docs/preferences-reference.md +50 -1
  217. package/src/resources/extensions/gsd/gsd-db.ts +564 -186
  218. package/src/resources/extensions/gsd/guided-flow.ts +4 -1
  219. package/src/resources/extensions/gsd/markdown-renderer.ts +44 -66
  220. package/src/resources/extensions/gsd/md-importer.ts +49 -2
  221. package/src/resources/extensions/gsd/migration-auto-check.ts +154 -34
  222. package/src/resources/extensions/gsd/parsers-legacy.ts +20 -0
  223. package/src/resources/extensions/gsd/planner-handoff.ts +149 -0
  224. package/src/resources/extensions/gsd/planning-path-scope.ts +22 -4
  225. package/src/resources/extensions/gsd/pre-execution-checks.ts +9 -2
  226. package/src/resources/extensions/gsd/preferences-models.ts +113 -43
  227. package/src/resources/extensions/gsd/preferences-types.ts +47 -0
  228. package/src/resources/extensions/gsd/preferences-validation.ts +76 -2
  229. package/src/resources/extensions/gsd/preferences.ts +5 -0
  230. package/src/resources/extensions/gsd/prompts/gate-evaluate.md +1 -1
  231. package/src/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
  232. package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  233. package/src/resources/extensions/gsd/prompts/refine-slice.md +1 -1
  234. package/src/resources/extensions/gsd/prompts/run-uat.md +2 -2
  235. package/src/resources/extensions/gsd/prompts/system.md +1 -1
  236. package/src/resources/extensions/gsd/roadmap-slices.ts +6 -1
  237. package/src/resources/extensions/gsd/safety/content-validator.ts +8 -5
  238. package/src/resources/extensions/gsd/skill-manifest.ts +12 -0
  239. package/src/resources/extensions/gsd/source-observations.ts +402 -0
  240. package/src/resources/extensions/gsd/state-reconciliation/drift/completion.ts +20 -8
  241. package/src/resources/extensions/gsd/state-reconciliation/drift/stale-render.ts +44 -5
  242. package/src/resources/extensions/gsd/state-reconciliation/drift/stale-worker.ts +39 -11
  243. package/src/resources/extensions/gsd/state-reconciliation/index.ts +45 -15
  244. package/src/resources/extensions/gsd/state-reconciliation/spawn-gate.ts +4 -4
  245. package/src/resources/extensions/gsd/state.ts +7 -4
  246. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +114 -0
  247. package/src/resources/extensions/gsd/tests/auto-model-selection-tool-poisoning.test.ts +66 -4
  248. package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +299 -1
  249. package/src/resources/extensions/gsd/tests/auto-paused-ui-cleanup.test.ts +32 -0
  250. package/src/resources/extensions/gsd/tests/auto-phases-lifecycle.test.ts +75 -3
  251. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +22 -1
  252. package/src/resources/extensions/gsd/tests/auto-supervisor.test.mjs +4 -0
  253. package/src/resources/extensions/gsd/tests/before-provider-context-management.test.ts +145 -0
  254. package/src/resources/extensions/gsd/tests/bundled-skill-triggers.test.ts +9 -0
  255. package/src/resources/extensions/gsd/tests/closeout-wizard.test.ts +44 -0
  256. package/src/resources/extensions/gsd/tests/commands-dispatcher-unmerged-milestone.test.ts +26 -1
  257. package/src/resources/extensions/gsd/tests/complete-slice-verification-gate.test.ts +118 -0
  258. package/src/resources/extensions/gsd/tests/content-validator.test.ts +74 -0
  259. package/src/resources/extensions/gsd/tests/context-masker.test.ts +56 -1
  260. package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +17 -2
  261. package/src/resources/extensions/gsd/tests/dispatch-rule-coverage.test.ts +24 -0
  262. package/src/resources/extensions/gsd/tests/doctor-scope-db-unavailable.test.ts +1 -11
  263. package/src/resources/extensions/gsd/tests/gate-dispatch.test.ts +64 -0
  264. package/src/resources/extensions/gsd/tests/gate-storage.test.ts +15 -0
  265. package/src/resources/extensions/gsd/tests/gsd-recover.test.ts +62 -1
  266. package/src/resources/extensions/gsd/tests/integration/run-uat.test.ts +4 -1
  267. package/src/resources/extensions/gsd/tests/interrupted-session-auto.test.ts +27 -0
  268. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +16 -0
  269. package/src/resources/extensions/gsd/tests/markdown-renderer.test.ts +42 -0
  270. package/src/resources/extensions/gsd/tests/mcp-project-config.test.ts +7 -1
  271. package/src/resources/extensions/gsd/tests/mcp-status.test.ts +1 -1
  272. package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +99 -0
  273. package/src/resources/extensions/gsd/tests/plan-slice.test.ts +99 -2
  274. package/src/resources/extensions/gsd/tests/plan-task.test.ts +19 -0
  275. package/src/resources/extensions/gsd/tests/planner-handoff.test.ts +100 -0
  276. package/src/resources/extensions/gsd/tests/preferences.test.ts +14 -0
  277. package/src/resources/extensions/gsd/tests/prefs-wizard-coverage.test.ts +1 -0
  278. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +133 -0
  279. package/src/resources/extensions/gsd/tests/provider-switch-observer.test.ts +55 -0
  280. package/src/resources/extensions/gsd/tests/register-hooks-depth-verification.test.ts +101 -1
  281. package/src/resources/extensions/gsd/tests/repository-registry.test.ts +2 -2
  282. package/src/resources/extensions/gsd/tests/runtime-invariant-modules.test.ts +28 -0
  283. package/src/resources/extensions/gsd/tests/schema-v21-sequence.test.ts +5 -3
  284. package/src/resources/extensions/gsd/tests/schema-v27-v28-sequence.test.ts +162 -18
  285. package/src/resources/extensions/gsd/tests/skill-manifest.test.ts +4 -3
  286. package/src/resources/extensions/gsd/tests/skipped-validation-db-atomicity.test.ts +8 -0
  287. package/src/resources/extensions/gsd/tests/source-observations.test.ts +275 -0
  288. package/src/resources/extensions/gsd/tests/stale-queued-milestone.test.ts +43 -0
  289. package/src/resources/extensions/gsd/tests/state-reconciliation-drift.test.ts +76 -21
  290. package/src/resources/extensions/gsd/tests/thinking-level-resolution.test.ts +203 -0
  291. package/src/resources/extensions/gsd/tests/uat-policy.test.ts +170 -0
  292. package/src/resources/extensions/gsd/tests/unit-context-manifest.test.ts +7 -1
  293. package/src/resources/extensions/gsd/tests/workflow-kernel.test.ts +7 -0
  294. package/src/resources/extensions/gsd/tests/workflow-manifest.test.ts +306 -1
  295. package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +77 -10
  296. package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +260 -5
  297. package/src/resources/extensions/gsd/tests/worktree-db.test.ts +511 -1
  298. package/src/resources/extensions/gsd/tests/worktree-reentry.test.ts +102 -0
  299. package/src/resources/extensions/gsd/tests/worktree-state-projection.test.ts +44 -0
  300. package/src/resources/extensions/gsd/tool-contract.ts +29 -1
  301. package/src/resources/extensions/gsd/tool-presentation-plan.ts +41 -6
  302. package/src/resources/extensions/gsd/tools/complete-slice.ts +29 -0
  303. package/src/resources/extensions/gsd/tools/plan-slice.ts +54 -12
  304. package/src/resources/extensions/gsd/tools/plan-task.ts +8 -1
  305. package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +71 -489
  306. package/src/resources/extensions/gsd/types.ts +1 -0
  307. package/src/resources/extensions/gsd/uat-policy.ts +191 -0
  308. package/src/resources/extensions/gsd/uat-run.ts +550 -0
  309. package/src/resources/extensions/gsd/unit-context-manifest.ts +3 -4
  310. package/src/resources/extensions/gsd/unit-tool-contracts.ts +38 -14
  311. package/src/resources/extensions/gsd/verdict-parser.ts +3 -10
  312. package/src/resources/extensions/gsd/workflow-manifest.ts +193 -7
  313. package/src/resources/extensions/gsd/workflow-mcp.ts +2 -3
  314. package/src/resources/extensions/gsd/workflow-projections.ts +9 -0
  315. package/src/resources/extensions/gsd/worktree-manager.ts +32 -0
  316. package/src/resources/extensions/gsd/worktree-reentry.ts +103 -0
  317. package/src/resources/extensions/gsd/worktree-state-projection.ts +22 -22
  318. package/src/resources/extensions/shared/gsd-browser-cli.ts +6 -0
  319. package/src/resources/extensions/shared/tests/format-utils.test.ts +8 -3
  320. package/src/resources/extensions/subagent/agents.ts +4 -0
  321. package/src/resources/extensions/subagent/index.ts +28 -3
  322. package/src/resources/extensions/subagent/launch.ts +8 -0
  323. package/src/resources/extensions/subagent/tests/model-override.test.ts +31 -0
  324. /package/dist/web/standalone/.next/static/{9y3LeeR2uGr2yRj9RjY3D → tJOKQbQRO-9MiFDO8DIDS}/_buildManifest.js +0 -0
  325. /package/dist/web/standalone/.next/static/{9y3LeeR2uGr2yRj9RjY3D → tJOKQbQRO-9MiFDO8DIDS}/_ssgManifest.js +0 -0
@@ -17,6 +17,7 @@
17
17
  * auto-session-encapsulation.test.ts enforce that auto.ts has no module-level
18
18
  * `let` or `var` declarations.
19
19
  */
20
+ import { SourceObservationStore, supportsSourceObservationsForUnit } from "../source-observations.js";
20
21
  import { resolveWorktreeProjectRoot } from "../worktree-root.js";
21
22
  import { normalizeRealPath } from "../paths.js";
22
23
  // ─── Constants ───────────────────────────────────────────────────────────────
@@ -76,6 +77,7 @@ export class AutoSession {
76
77
  currentTurnId = null;
77
78
  currentUnitRouting = null;
78
79
  currentMilestoneId = null;
80
+ sourceObservations = new SourceObservationStore();
79
81
  // ── Model state ──────────────────────────────────────────────────────────
80
82
  autoModeStartModel = null;
81
83
  /** Explicit /gsd model pin captured at bootstrap (session-scoped policy override). */
@@ -94,6 +96,7 @@ export class AutoSession {
94
96
  verificationRetryCount = new Map();
95
97
  verificationRetryFailureHashes = new Map();
96
98
  exhaustedVerificationUnits = new Set();
99
+ zeroToolRetryCount = new Map();
97
100
  pausedSessionFile = null;
98
101
  pausedUnitType = null;
99
102
  pausedUnitId = null;
@@ -195,6 +198,23 @@ export class AutoSession {
195
198
  this.unitDispatchCount.clear();
196
199
  this.unitLifetimeDispatches.clear();
197
200
  }
201
+ setCurrentUnit(unit) {
202
+ this.currentUnit = unit;
203
+ if (!supportsSourceObservationsForUnit(unit.type)) {
204
+ this.sourceObservations.clear();
205
+ return;
206
+ }
207
+ this.sourceObservations.beginUnit({
208
+ unitType: unit.type,
209
+ unitId: unit.id,
210
+ startedAt: unit.startedAt,
211
+ basePath: unit.workspaceRoot ?? this.basePath,
212
+ });
213
+ }
214
+ clearCurrentUnit() {
215
+ this.currentUnit = null;
216
+ this.sourceObservations.clear();
217
+ }
198
218
  get lockBasePath() {
199
219
  return resolveWorktreeProjectRoot(this.basePath, this.originalBasePath);
200
220
  }
@@ -245,7 +265,7 @@ export class AutoSession {
245
265
  this.unitLifetimeDispatches.clear();
246
266
  this.unitRecoveryCount.clear();
247
267
  // Unit
248
- this.currentUnit = null;
268
+ this.clearCurrentUnit();
249
269
  this.currentTraceId = null;
250
270
  this.currentTurnId = null;
251
271
  this.currentUnitRouting = null;
@@ -266,6 +286,7 @@ export class AutoSession {
266
286
  this.verificationRetryCount.clear();
267
287
  this.verificationRetryFailureHashes.clear();
268
288
  this.exhaustedVerificationUnits.clear();
289
+ this.zeroToolRetryCount.clear();
269
290
  this.pausedSessionFile = null;
270
291
  this.pausedUnitType = null;
271
292
  this.pausedUnitId = null;
@@ -71,6 +71,7 @@ const COMPLETE_AND_BREAK_REASONS = [
71
71
  "verification-pause",
72
72
  "finalize-pre-timeout",
73
73
  "finalize-post-timeout",
74
+ "milestone-complete",
74
75
  ];
75
76
  function isCompleteAndBreakReason(reason) {
76
77
  return COMPLETE_AND_BREAK_REASONS.includes(reason);
@@ -1,7 +1,8 @@
1
1
  // Project/App: gsd-pi
2
2
  // File Purpose: Declarative auto-mode dispatch rules and dispatch resolver.
3
3
  import { loadFile, extractUatType, loadActiveOverrides } from "./files.js";
4
- import { isDbAvailable, getMilestoneSlices, getPendingGates, markAllGatesOmitted, getMilestone, insertAssessment, setSliceSketchFlag, transaction, getAssessment } from "./gsd-db.js";
4
+ import { getUatBrowserToolSupportError } from "./uat-policy.js";
5
+ import { isDbAvailable, getMilestoneSlices, getPendingGatesForTurn, markPendingGatesOmittedForTurn, getMilestone, insertArtifact, insertAssessment, setSliceSketchFlag, transaction, getAssessment, } from "./gsd-db.js";
5
6
  import { isClosedStatus } from "./status-guards.js";
6
7
  import { extractVerdict, isAcceptableUatVerdict } from "./verdict-parser.js";
7
8
  import { gsdRoot, resolveGsdPathContract, resolveMilestoneFile, resolveMilestonePath, resolveSliceFile, resolveSlicePath, resolveTaskFile, relTaskFile, relSliceFile, buildMilestoneFileName, buildSliceFileName, buildTaskFileName, gsdProjectionRoot, } from "./paths.js";
@@ -12,7 +13,7 @@ import { logWarning, logError } from "./workflow-logger.js";
12
13
  import { dirname, join } from "node:path";
13
14
  import { hasImplementationArtifacts } from "./milestone-implementation-evidence.js";
14
15
  import { buildDiscussMilestonePrompt, buildDiscussProjectPrompt, buildDiscussRequirementsPrompt, buildResearchDecisionPrompt, buildResearchProjectPrompt, buildResearchMilestonePrompt, buildPlanMilestonePrompt, buildResearchSlicePrompt, buildPlanSlicePrompt, buildRefineSlicePrompt, buildExecuteTaskPrompt, buildCompleteSlicePrompt, buildCompleteMilestonePrompt, buildValidateMilestonePrompt, buildReplanSlicePrompt, buildRunUatPrompt, buildReassessRoadmapPrompt, buildRewriteDocsPrompt, buildReactiveExecutePrompt, buildGateEvaluatePrompt, buildParallelResearchSlicesPrompt, checkNeedsReassessment, checkNeedsRunUat, } from "./auto-prompts.js";
15
- import { resolveModelWithFallbacksForUnit } from "./preferences-models.js";
16
+ import { resolveModelWithFallbacksForUnit, resolveThinkingLevelForUnit } from "./preferences-models.js";
16
17
  import { resolveUokFlags } from "./uok/flags.js";
17
18
  import { selectReactiveDispatchBatch } from "./uok/execution-graph.js";
18
19
  import { getMilestonePipelineVariant } from "./milestone-scope-classifier.js";
@@ -21,6 +22,7 @@ import { isAutoActive } from "./auto.js";
21
22
  import { markDepthVerified } from "./bootstrap/write-gate.js";
22
23
  import { ensureWorkflowPreferencesCaptured } from "./planning-depth.js";
23
24
  import { MILESTONE_ID_RE } from "./milestone-ids.js";
25
+ import { getWorkflowTransportSupportError, getRequiredWorkflowToolsForAutoUnit, } from "./workflow-mcp.js";
24
26
  import { PROJECT_RESEARCH_INFLIGHT_MARKER, } from "./project-research-policy.js";
25
27
  import { isWorkflowPrefsCaptured, resolveDeepProjectSetupState, } from "./deep-project-setup-policy.js";
26
28
  import { annotateBackgroundable } from "./delegation-policy.js";
@@ -249,6 +251,43 @@ export function findMissingSummaries(basePath, mid) {
249
251
  })
250
252
  .map(s => s.id);
251
253
  }
254
+ function stringField(row, key) {
255
+ const value = row?.[key];
256
+ return typeof value === "string" ? value : null;
257
+ }
258
+ function stripGsdPrefix(path) {
259
+ return path.startsWith(".gsd/") ? path.slice(".gsd/".length) : path;
260
+ }
261
+ function persistSliceAssessmentBackfill(assessmentRelPath, mid, sliceId, content) {
262
+ const artifactPath = stripGsdPrefix(assessmentRelPath);
263
+ const existingAssessment = getAssessment(assessmentRelPath) ??
264
+ getAssessment(artifactPath);
265
+ const scope = stringField(existingAssessment, "scope") ?? "run-uat";
266
+ const status = stringField(existingAssessment, "status") ??
267
+ extractVerdict(content)?.toLowerCase() ??
268
+ "unknown";
269
+ transaction(() => {
270
+ insertArtifact({
271
+ path: artifactPath,
272
+ artifact_type: "ASSESSMENT",
273
+ milestone_id: mid,
274
+ slice_id: sliceId,
275
+ task_id: null,
276
+ full_content: content,
277
+ });
278
+ if (!getAssessment(assessmentRelPath)) {
279
+ insertAssessment({
280
+ path: assessmentRelPath,
281
+ milestoneId: mid,
282
+ sliceId,
283
+ taskId: null,
284
+ status,
285
+ scope,
286
+ fullContent: content,
287
+ });
288
+ }
289
+ });
290
+ }
252
291
  function backfillMissingAssessmentsFromSummaries(basePath, mid) {
253
292
  const completedSliceIds = new Set();
254
293
  if (isDbAvailable()) {
@@ -280,11 +319,12 @@ function backfillMissingAssessmentsFromSummaries(basePath, mid) {
280
319
  const slicePath = resolveSlicePath(basePath, mid, sliceId);
281
320
  const assessmentPath = resolveSliceFile(basePath, mid, sliceId, "ASSESSMENT")
282
321
  ?? (slicePath ? join(slicePath, buildSliceFileName(sliceId, "ASSESSMENT")) : null);
283
- if (!assessmentPath || existsSync(assessmentPath))
322
+ if (!assessmentPath)
284
323
  continue;
285
- mkdirSync(dirname(assessmentPath), { recursive: true });
324
+ const assessmentRelPath = relSliceFile(basePath, mid, sliceId, "ASSESSMENT");
286
325
  const now = new Date().toISOString();
287
- const content = [
326
+ const didCreateAssessment = !existsSync(assessmentPath);
327
+ const content = didCreateAssessment ? [
288
328
  "---",
289
329
  `sliceId: ${sliceId}`,
290
330
  "verdict: PASS",
@@ -296,8 +336,19 @@ function backfillMissingAssessmentsFromSummaries(basePath, mid) {
296
336
  "Auto-created during milestone validation because this completed slice had a SUMMARY but no ASSESSMENT artifact.",
297
337
  "No additional reassessment changes were detected in this backfill step.",
298
338
  "",
299
- ].join("\n");
300
- writeFileSync(assessmentPath, content, "utf-8");
339
+ ].join("\n") : readFileSync(assessmentPath, "utf-8");
340
+ if (isDbAvailable()) {
341
+ try {
342
+ persistSliceAssessmentBackfill(assessmentRelPath, mid, sliceId, content);
343
+ }
344
+ catch (err) {
345
+ logWarning("dispatch", `failed to backfill assessment DB rows for ${mid}/${sliceId}: ${err.message}`);
346
+ }
347
+ }
348
+ if (didCreateAssessment) {
349
+ mkdirSync(dirname(assessmentPath), { recursive: true });
350
+ writeFileSync(assessmentPath, content, "utf-8");
351
+ }
301
352
  }
302
353
  }
303
354
  // ─── Rewrite Circuit Breaker ──────────────────────────────────────────────
@@ -467,11 +518,27 @@ export const DISPATCH_RULES = [
467
518
  },
468
519
  {
469
520
  name: "run-uat (post-completion)",
470
- match: async ({ state, mid, basePath, prefs }) => {
521
+ match: async ({ state, mid, basePath, prefs, sessionProvider, sessionAuthMode, activeTools, sessionBaseUrl }) => {
471
522
  const needsRunUat = await checkNeedsRunUat(basePath, mid, state, prefs);
472
523
  if (!needsRunUat)
473
524
  return null;
474
525
  const { sliceId, uatType } = needsRunUat;
526
+ // Transport preflight: verify required MCP tools are actually connected
527
+ // before consuming a retry attempt. Fixes tool-starved sessions burning
528
+ // all MAX_UAT_ATTEMPTS before stopping (#477).
529
+ const transportError = getWorkflowTransportSupportError(sessionProvider, getRequiredWorkflowToolsForAutoUnit("run-uat"), { projectRoot: basePath, surface: "auto-mode", unitType: "run-uat", authMode: sessionAuthMode, baseUrl: sessionBaseUrl, activeTools });
530
+ if (transportError) {
531
+ return { action: "stop", reason: transportError, level: "warning" };
532
+ }
533
+ const browserToolError = getUatBrowserToolSupportError({
534
+ uatType,
535
+ activeTools,
536
+ milestoneId: mid,
537
+ sliceId,
538
+ });
539
+ if (browserToolError) {
540
+ return { action: "stop", reason: browserToolError, level: "warning" };
541
+ }
475
542
  // Cap run-uat dispatch attempts to prevent infinite replay (#3624).
476
543
  // Check before incrementing so an exhausted counter cannot create a
477
544
  // no-progress skip loop that starves later dispatch rules.
@@ -877,7 +944,7 @@ export const DISPATCH_RULES = [
877
944
  action: "dispatch",
878
945
  unitType: "research-slice",
879
946
  unitId: `${mid}/parallel-research`,
880
- prompt: await buildParallelResearchSlicesPrompt(mid, midTitle, researchReadySlices, basePath, resolveModelWithFallbacksForUnit("subagent")?.primary),
947
+ prompt: await buildParallelResearchSlicesPrompt(mid, midTitle, researchReadySlices, basePath, resolveModelWithFallbacksForUnit("subagent")?.primary, resolveThinkingLevelForUnit("subagent")),
881
948
  };
882
949
  },
883
950
  },
@@ -1029,17 +1096,17 @@ export const DISPATCH_RULES = [
1029
1096
  // Gate evaluation is opt-in via preferences
1030
1097
  const gateConfig = prefs?.gate_evaluation;
1031
1098
  if (!gateConfig?.enabled) {
1032
- markAllGatesOmitted(mid, sid);
1099
+ markPendingGatesOmittedForTurn(mid, sid, "gate-evaluate");
1033
1100
  return { action: "skip" };
1034
1101
  }
1035
- const pending = getPendingGates(mid, sid, "slice");
1102
+ const pending = getPendingGatesForTurn(mid, sid, "gate-evaluate");
1036
1103
  if (pending.length === 0)
1037
1104
  return { action: "skip" };
1038
1105
  return {
1039
1106
  action: "dispatch",
1040
1107
  unitType: "gate-evaluate",
1041
1108
  unitId: `${mid}/${sid}/gates+${pending.map(g => g.gate_id).join(",")}`,
1042
- prompt: await buildGateEvaluatePrompt(mid, midTitle, sid, sTitle, basePath, resolveModelWithFallbacksForUnit("subagent")?.primary),
1109
+ prompt: await buildGateEvaluatePrompt(mid, midTitle, sid, sTitle, basePath, resolveModelWithFallbacksForUnit("subagent")?.primary, resolveThinkingLevelForUnit("subagent")),
1043
1110
  };
1044
1111
  },
1045
1112
  },
@@ -1082,6 +1149,7 @@ export const DISPATCH_RULES = [
1082
1149
  return null;
1083
1150
  const maxParallel = reactiveConfig?.max_parallel ?? 2;
1084
1151
  const subagentModel = reactiveConfig?.subagent_model ?? resolveModelWithFallbacksForUnit("subagent")?.primary;
1152
+ const subagentThinking = resolveThinkingLevelForUnit("subagent");
1085
1153
  // Default-on safety threshold: only activate reactive dispatch when at
1086
1154
  // least N tasks are ready. Users who explicitly enabled reactive_execution
1087
1155
  // keep the legacy threshold of 2 (matches the prior "any parallelism is
@@ -1139,7 +1207,7 @@ export const DISPATCH_RULES = [
1139
1207
  action: "dispatch",
1140
1208
  unitType: "reactive-execute",
1141
1209
  unitId: `${mid}/${sid}/reactive+${batchSuffix}`,
1142
- prompt: await buildReactiveExecutePrompt(mid, midTitle, sid, sTitle, selected, basePath, subagentModel, { sessionContextWindow, modelRegistry, sessionProvider }),
1210
+ prompt: await buildReactiveExecutePrompt(mid, midTitle, sid, sTitle, selected, basePath, subagentModel, { sessionContextWindow, modelRegistry, sessionProvider, subagentThinking }),
1143
1211
  };
1144
1212
  }
1145
1213
  catch (err) {
@@ -3,7 +3,8 @@
3
3
  * Handles complexity-based routing, model resolution across providers,
4
4
  * and fallback chains.
5
5
  */
6
- import { resolveModelWithFallbacksForUnit, resolveDynamicRoutingConfig } from "./preferences.js";
6
+ import { clampThinkingLevel } from "@gsd/pi-ai";
7
+ import { resolveModelWithFallbacksForUnit, resolveThinkingLevelForUnit, resolveDynamicRoutingConfig } from "./preferences.js";
7
8
  import { classifyUnitComplexity, extractTaskMetadata, tierLabel } from "./complexity-classifier.js";
8
9
  import { resolveModelForComplexity, escalateTier, getEligibleModels, loadCapabilityOverrides, adjustToolSet } from "./model-router.js";
9
10
  import { getLedger, getProjectTotals } from "./metrics.js";
@@ -63,6 +64,32 @@ const TOOL_BASELINE = new WeakMap();
63
64
  export function clearToolBaseline(pi) {
64
65
  TOOL_BASELINE.delete(pi);
65
66
  }
67
+ /**
68
+ * Return the union of the pre-dispatch baseline tool set and the current live
69
+ * active tools, or just the live tools when no baseline has been recorded yet.
70
+ *
71
+ * Use this instead of `pi.getActiveTools()` anywhere you need the full tool
72
+ * surface for a preflight/routing check that runs BEFORE `selectAndApplyModel`
73
+ * restores the baseline — e.g. in `runDispatch` and `decideNextUnit`.
74
+ *
75
+ * The union is intentional:
76
+ * - Baseline covers tools that a prior unit's per-provider narrowing (hook
77
+ * overrides, Groq 128-tool cap, etc.) has removed from the live set.
78
+ * Those tools will be restored by `selectAndApplyModel` before dispatch, so
79
+ * dropping them from the preflight check would be a false negative.
80
+ * - Live set covers tools connected after the baseline was first captured
81
+ * (e.g. MCP servers attached mid-session or after a paused resume).
82
+ * Without the live merge, a stale baseline permanently hides newly
83
+ * connected MCP tools and prevents transport-preflight from clearing on
84
+ * resume (#477 follow-up).
85
+ */
86
+ export function getToolBaselineSnapshot(pi) {
87
+ const live = typeof pi.getActiveTools === "function" ? pi.getActiveTools() : [];
88
+ const baseline = TOOL_BASELINE.get(pi);
89
+ if (baseline === undefined)
90
+ return live;
91
+ return [...new Set([...baseline, ...live])];
92
+ }
66
93
  /**
67
94
  * Models eligible for the pre-dispatch policy gate. Prefer registry-available
68
95
  * models; when that list is empty (common after worktree resume before registry
@@ -202,10 +229,88 @@ function restoreToolBaseline(pi) {
202
229
  pi.setActiveTools([...baseline]);
203
230
  }
204
231
  }
205
- function reapplyThinkingLevel(pi, level) {
232
+ /**
233
+ * Apply the desired reasoning effort for the just-selected model, clamping to
234
+ * what the model actually supports (ADR-026). An unsupported level is never
235
+ * sent to the provider — it is clamped via `clampThinkingLevel` and the
236
+ * mismatch is surfaced once per (model, requested-level). Returns the level
237
+ * actually applied so callers can record it.
238
+ */
239
+ export function applyThinkingLevelForModel(pi, desired, model, ctx) {
240
+ if (!desired)
241
+ return desired;
242
+ // Capability-clamp only when we have a bare string level AND the model
243
+ // advertises reasoning capability (`reasoning` is always present on real
244
+ // registry models). Richer host snapshot shapes (e.g. `{ effort: "high" }`)
245
+ // and partial model objects are applied verbatim — we never coerce an unknown
246
+ // shape into a string or guess capability we can't see.
247
+ if (typeof desired === "string" && model != null && typeof model === "object" && "reasoning" in model) {
248
+ const clamped = clampThinkingLevel(model, desired);
249
+ pi.setThinkingLevel(clamped);
250
+ if (clamped !== desired) {
251
+ const key = `${model.provider}/${model.id}:${desired}`;
252
+ if (!_warnedThinkingClamp.has(key)) {
253
+ _warnedThinkingClamp.add(key);
254
+ ctx.ui.notify(`Thinking level '${desired}' not supported by ${model.provider}/${model.id}; using '${clamped}'.`, "warning");
255
+ }
256
+ }
257
+ return clamped;
258
+ }
259
+ pi.setThinkingLevel(desired);
260
+ return desired;
261
+ }
262
+ /** Warn-once guard for capability clamps, keyed by `provider/id:requested`. */
263
+ const _warnedThinkingClamp = new Set();
264
+ /** Warn-once guard for the execute-task floor punch-through advisory. */
265
+ let _warnedExecuteTaskFloorBypass = false;
266
+ /**
267
+ * Ascending severity order for reasoning levels (matches @gsd/pi-agent-core
268
+ * `ThinkingLevel`). Used only for floor comparisons below.
269
+ */
270
+ const THINKING_LEVEL_ORDER = [
271
+ "off",
272
+ "minimal",
273
+ "low",
274
+ "medium",
275
+ "high",
276
+ "xhigh",
277
+ ];
278
+ /**
279
+ * Minimum reasoning level for code-writing units.
280
+ *
281
+ * `execute-task` is the only unit that edits source. With a low/minimal
282
+ * thinking level a model does not plan its edits and compensates by re-reading
283
+ * the same files dozens of times per task (measured: index.html read ~49× in a
284
+ * single task on a minimal-thinking model) and shelling out to `nl`/`sed` to
285
+ * re-locate code after every edit invalidates its line numbers. Flooring the
286
+ * level for this unit type removes that read/bash thrash. Planning, research,
287
+ * and lifecycle units are unaffected.
288
+ */
289
+ const EXECUTE_TASK_MIN_THINKING_LEVEL = "medium";
290
+ function thinkingLevelRank(level) {
291
+ const idx = THINKING_LEVEL_ORDER.indexOf(level);
292
+ return idx === -1 ? 0 : idx;
293
+ }
294
+ /**
295
+ * Raise (never lower) the thinking level for code-writing units to a sane
296
+ * floor. Returns the input unchanged for non-`execute-task` units, when no
297
+ * level was captured, or when the captured level already meets the floor.
298
+ */
299
+ export function floorThinkingLevelForUnit(unitType, level) {
300
+ if (unitType !== "execute-task")
301
+ return level;
206
302
  if (!level)
207
- return;
208
- pi.setThinkingLevel(level);
303
+ return level;
304
+ // Only act on the recognized string levels. Any other shape (e.g. a richer
305
+ // host snapshot object) is passed through untouched so we never coerce an
306
+ // unknown representation into a bare string the host can't apply.
307
+ if (!THINKING_LEVEL_ORDER.includes(level)) {
308
+ return level;
309
+ }
310
+ if (thinkingLevelRank(level) >= thinkingLevelRank(EXECUTE_TASK_MIN_THINKING_LEVEL)) {
311
+ return level;
312
+ }
313
+ return EXECUTE_TASK_MIN_THINKING_LEVEL;
209
314
  }
210
315
  export function resolvePreferredModelConfig(unitType, autoModeStartModel, isAutoMode = true) {
211
316
  const explicitConfig = resolveModelWithFallbacksForUnit(unitType);
@@ -256,6 +361,32 @@ sessionModelOverride,
256
361
  /** Thinking level captured at auto-mode start and re-applied after model swaps. */
257
362
  autoModeStartThinkingLevel) {
258
363
  const uokFlags = resolveUokFlags(prefs);
364
+ // Resolve reasoning effort for this dispatch (ADR-026). An explicit per-phase
365
+ // thinking config (inline `models.<phase>.thinking` or the separate `thinking`
366
+ // block) expresses hard user intent: it bypasses the execute-task floor and is
367
+ // honored verbatim, then capability-clamped per model at apply time below.
368
+ // With no explicit level, fall back to the auto-start session level and raise
369
+ // the code-writing floor — preserving prior behavior exactly. Recomputed per
370
+ // dispatch so neither the floor nor a phase override leaks to other units.
371
+ const explicitThinkingLevel = resolveThinkingLevelForUnit(unitType);
372
+ const desiredThinkingLevel = explicitThinkingLevel
373
+ ?? floorThinkingLevelForUnit(unitType, autoModeStartThinkingLevel);
374
+ if (explicitThinkingLevel) {
375
+ if (unitType === "execute-task" &&
376
+ thinkingLevelRank(explicitThinkingLevel) < thinkingLevelRank(EXECUTE_TASK_MIN_THINKING_LEVEL) &&
377
+ !_warnedExecuteTaskFloorBypass) {
378
+ _warnedExecuteTaskFloorBypass = true;
379
+ ctx.ui.notify(`Explicit execution thinking '${explicitThinkingLevel}' is below the measured execute-task floor ` +
380
+ `(${EXECUTE_TASK_MIN_THINKING_LEVEL}); honoring it as configured. Low reasoning on code edits can ` +
381
+ `cause repeated file re-reads.`, "warning");
382
+ }
383
+ }
384
+ else if (verbose &&
385
+ desiredThinkingLevel &&
386
+ desiredThinkingLevel !== autoModeStartThinkingLevel) {
387
+ ctx.ui.notify(`Thinking level raised to ${desiredThinkingLevel} for ${unitType} (was ${autoModeStartThinkingLevel ?? "unset"})`, "info");
388
+ }
389
+ let appliedThinkingLevel = null;
259
390
  const effectiveSessionModelOverride = sessionModelOverride === undefined
260
391
  ? getSessionModelOverride(ctx.sessionManager.getSessionId())
261
392
  : (sessionModelOverride ?? undefined);
@@ -533,7 +664,7 @@ autoModeStartThinkingLevel) {
533
664
  const ok = await pi.setModel(model, { persist: false });
534
665
  if (ok) {
535
666
  appliedModel = model;
536
- reapplyThinkingLevel(pi, autoModeStartThinkingLevel);
667
+ appliedThinkingLevel = applyThinkingLevelForModel(pi, desiredThinkingLevel, model, ctx);
537
668
  // ADR-005: Adjust active tool set for the selected model's provider capabilities.
538
669
  // Hard-filter incompatible tools, then let extensions override via adjust_tool_set hook.
539
670
  const activeToolNames = pi.getActiveTools();
@@ -591,7 +722,7 @@ autoModeStartThinkingLevel) {
591
722
  if (!ok)
592
723
  continue;
593
724
  appliedModel = model;
594
- reapplyThinkingLevel(pi, autoModeStartThinkingLevel);
725
+ appliedThinkingLevel = applyThinkingLevelForModel(pi, desiredThinkingLevel, model, ctx);
595
726
  attemptedPolicyEligible = true;
596
727
  if (verbose) {
597
728
  ctx.ui.notify(`Model policy: configured model unavailable; using ${model.provider}/${model.id}`, "info");
@@ -621,18 +752,32 @@ autoModeStartThinkingLevel) {
621
752
  const fallbackOk = await pi.setModel(byId, { persist: false });
622
753
  if (fallbackOk) {
623
754
  appliedModel = byId;
624
- reapplyThinkingLevel(pi, autoModeStartThinkingLevel);
755
+ appliedThinkingLevel = applyThinkingLevelForModel(pi, desiredThinkingLevel, byId, ctx);
625
756
  }
626
757
  }
627
758
  }
628
759
  else {
629
760
  appliedModel = startModel;
630
- reapplyThinkingLevel(pi, autoModeStartThinkingLevel);
761
+ appliedThinkingLevel = applyThinkingLevelForModel(pi, desiredThinkingLevel, startModel, ctx);
631
762
  }
632
763
  }
633
764
  }
634
765
  }
635
- return { routing, appliedModel };
766
+ // If no model branch applied a thinking level (e.g. interactive guided-flow
767
+ // with a `thinking:` block but no per-phase model and no start model), still
768
+ // honor an explicitly configured phase thinking level against the current
769
+ // session model. Only the explicit path runs here — the floored session
770
+ // default is intentionally left untouched so no-config interactive runs keep
771
+ // the user's /model thinking level. (ADR-026)
772
+ if (appliedThinkingLevel == null && explicitThinkingLevel && ctx.model) {
773
+ // Prefer the full registry model (carries reasoning capability so the level
774
+ // can be clamped); fall back to ctx.model. Always route through
775
+ // applyThinkingLevelForModel so the clamp runs whenever capability metadata
776
+ // exists — never a raw verbatim setThinkingLevel that bypasses it (ADR-026).
777
+ const current = resolveModelId(`${ctx.model.provider}/${ctx.model.id}`, ctx.modelRegistry?.getAvailable?.() ?? [], ctx.model.provider) ?? ctx.model;
778
+ appliedThinkingLevel = applyThinkingLevelForModel(pi, explicitThinkingLevel, current, ctx);
779
+ }
780
+ return { routing, appliedModel, appliedThinkingLevel };
636
781
  }
637
782
  /**
638
783
  * Resolve a model ID string to a model object from the available models list.
@@ -1280,6 +1280,7 @@ export async function postUnitPreVerification(pctx, opts) {
1280
1280
  logError("engine", "triage resolution failed", { error: err.message });
1281
1281
  }
1282
1282
  }
1283
+ let blockingContentViolation = null;
1283
1284
  // ── Safety harness: post-unit validation ──
1284
1285
  try {
1285
1286
  const { loadEffectiveGSDPreferences } = await import("./preferences.js");
@@ -1399,8 +1400,15 @@ export async function postUnitPreVerification(pctx, opts) {
1399
1400
  const artifactPath = resolveArtifactForContent(s.currentUnit.type, s.currentUnit.id, s.basePath);
1400
1401
  const contentViolations = validateContent(s.currentUnit.type, artifactPath);
1401
1402
  for (const v of contentViolations) {
1402
- logWarning("safety", `content: ${v.reason}`);
1403
- ctx.ui.notify(`Content validation: ${v.reason}`, "warning");
1403
+ if (v.severity === "error") {
1404
+ blockingContentViolation ??= v.reason;
1405
+ logError("safety", `content: ${v.reason}`);
1406
+ ctx.ui.notify(`Content validation: ${v.reason}`, "error");
1407
+ }
1408
+ else {
1409
+ logWarning("safety", `content: ${v.reason}`);
1410
+ ctx.ui.notify(`Content validation: ${v.reason}`, "warning");
1411
+ }
1404
1412
  }
1405
1413
  }
1406
1414
  catch (e) {
@@ -1573,6 +1581,15 @@ export async function postUnitPreVerification(pctx, opts) {
1573
1581
  return "continue";
1574
1582
  }
1575
1583
  }
1584
+ if (blockingContentViolation && triggerArtifactVerified) {
1585
+ triggerArtifactVerified = false;
1586
+ debugLog("postUnit", {
1587
+ phase: "content-validation-blocked-artifact",
1588
+ unitType: s.currentUnit.type,
1589
+ unitId: s.currentUnit.id,
1590
+ reason: blockingContentViolation,
1591
+ });
1592
+ }
1576
1593
  // When artifact verification fails for a unit type that has a known expected
1577
1594
  // artifact, ask the caller to retry so it re-dispatches with failure context
1578
1595
  // instead of blindly re-dispatching the same unit (#1571).
@@ -8,7 +8,7 @@
8
8
  * utility.
9
9
  */
10
10
  import { loadFile, parseContinue, parseSummary, loadActiveOverrides, formatOverridesSection } from "./files.js";
11
- import { hasVerdict, getUatType, extractVerdict } from "./verdict-parser.js";
11
+ import { hasVerdict, extractVerdict } from "./verdict-parser.js";
12
12
  import { loadPrompt, inlineTemplate } from "./prompt-loader.js";
13
13
  import { resolveMilestoneFile, resolveSliceFile, resolveSlicePath, resolveTasksDir, resolveTaskFiles, resolveTaskFile, relMilestoneFile, relSliceFile, relSlicePath, relMilestonePath, resolveGsdRootFile, relGsdRootFile, resolveRuntimeFile, } from "./paths.js";
14
14
  import { resolveInlineLevel, loadEffectiveGSDPreferences } from "./preferences.js";
@@ -27,11 +27,11 @@ import { logWarning } from "./workflow-logger.js";
27
27
  import { inlineGraphSubgraph } from "./graph-context.js";
28
28
  import { buildExtractionStepsBlock } from "./commands-extract-learnings.js";
29
29
  import { classifyProject } from "./detection.js";
30
- import { hasBrowserRequiredText } from "./browser-evidence.js";
31
30
  import { debugLog } from "./debug-logger.js";
32
31
  import { buildSkillActivationBlock, buildSkillDiscoveryVars } from "./skill-activation.js";
33
32
  import { findMilestoneIds } from "./milestone-ids.js";
34
- import { buildRunUatResultPresentation, RUN_UAT_TOOL_PRESENTATION_PLAN_ID } from "./tool-presentation-plan.js";
33
+ import { buildRunUatPresentationForType, RUN_UAT_TOOL_PRESENTATION_PLAN_ID } from "./tool-presentation-plan.js";
34
+ import { resolveEffectiveUatType, shouldDispatchUatForContent } from "./uat-policy.js";
35
35
  export { buildSkillActivationBlock, buildSkillDiscoveryVars };
36
36
  // ─── Preamble Cap ─────────────────────────────────────────────────────────────
37
37
  /**
@@ -228,17 +228,6 @@ function prependContextModeToBlock(unitType, base, block, renderMode = "standalo
228
228
  return contextMode;
229
229
  return `${contextMode}\n\n${block}`;
230
230
  }
231
- function resolveEffectiveUatType(content) {
232
- const uatType = getUatType(content);
233
- if (uatType === "artifact-driven" && hasBrowserRequiredText(content)) {
234
- return "browser-executable";
235
- }
236
- return uatType;
237
- }
238
- function shouldDispatchUatForContent(content, prefs) {
239
- const uatType = resolveEffectiveUatType(content);
240
- return !!prefs?.uat_dispatch || uatType !== "artifact-driven" || hasBrowserRequiredText(content);
241
- }
242
231
  // ─── Executor Constraints ─────────────────────────────────────────────────────
243
232
  /**
244
233
  * Format executor context constraints for injection into the plan-slice prompt.
@@ -2940,7 +2929,7 @@ export async function buildRunUatPrompt(mid, sliceId, uatPath, uatContent, base)
2940
2929
  emitPromptContextTelemetry("run-uat", contextTelemetry, inlinedContext);
2941
2930
  const uatResultPath = join(base, relSliceFile(base, mid, sliceId, "ASSESSMENT"));
2942
2931
  const uatType = resolveEffectiveUatType(uatContent);
2943
- const canonicalPresentation = JSON.stringify(buildRunUatResultPresentation(), null, 2);
2932
+ const canonicalPresentation = JSON.stringify(buildRunUatPresentationForType(uatType), null, 2);
2944
2933
  return loadPrompt("run-uat", {
2945
2934
  workingDirectory: base,
2946
2935
  milestoneId: mid,
@@ -3077,7 +3066,23 @@ export async function buildReassessRoadmapPrompt(mid, midTitle, completedSliceId
3077
3066
  });
3078
3067
  }
3079
3068
  // ─── Reactive Execute Prompt ──────────────────────────────────────────────
3080
- export async function buildReactiveExecutePrompt(mid, midTitle, sid, sTitle, readyTaskIds, base, subagentModel, opts) {
3069
+ /**
3070
+ * Build the `with model: "…" and thinking: "…"` suffix injected into a prompt
3071
+ * that instructs the coordinator how to dispatch a `subagent` call. Either or
3072
+ * both may be absent (ADR-026 / #508).
3073
+ */
3074
+ function subagentCallSuffix(model, thinking) {
3075
+ const parts = [];
3076
+ if (model)
3077
+ parts.push(`model: "${model}"`);
3078
+ if (thinking)
3079
+ parts.push(`thinking: "${thinking}"`);
3080
+ return parts.length > 0 ? ` with ${parts.join(" and ")}` : "";
3081
+ }
3082
+ export async function buildReactiveExecutePrompt(mid, midTitle, sid, sTitle, readyTaskIds, base, subagentModel,
3083
+ // Reasoning effort travels inside opts here (not as a positional param) so
3084
+ // existing positional `opts` callers don't shift (#508).
3085
+ opts) {
3081
3086
  const { loadSliceTaskIO, deriveTaskGraph, graphMetrics } = await import("./reactive-graph.js");
3082
3087
  // Build graph for context
3083
3088
  const taskIO = await loadSliceTaskIO(base, mid, sid);
@@ -3151,7 +3156,7 @@ export async function buildReactiveExecutePrompt(mid, midTitle, sid, sTitle, rea
3151
3156
  "",
3152
3157
  `When done, say: "Task ${tid} complete."`,
3153
3158
  ].join("\n");
3154
- const modelSuffix = subagentModel ? ` with model: "${subagentModel}"` : "";
3159
+ const modelSuffix = subagentCallSuffix(subagentModel, opts?.subagentThinking);
3155
3160
  subagentSections.push([
3156
3161
  `### ${tid}: ${tTitle}`,
3157
3162
  "",
@@ -3217,10 +3222,10 @@ function renderGatesToCloseBlock(gates, opts) {
3217
3222
  }
3218
3223
  return lines.join("\n").trimEnd();
3219
3224
  }
3220
- export async function buildParallelResearchSlicesPrompt(mid, midTitle, slices, basePath, subagentModel) {
3225
+ export async function buildParallelResearchSlicesPrompt(mid, midTitle, slices, basePath, subagentModel, subagentThinking) {
3221
3226
  // Build individual research-slice prompts for each slice
3222
3227
  const subagentSections = [];
3223
- const modelSuffix = subagentModel ? ` with model: "${subagentModel}"` : "";
3228
+ const modelSuffix = subagentCallSuffix(subagentModel, subagentThinking);
3224
3229
  for (const slice of slices) {
3225
3230
  const slicePrompt = await buildResearchSlicePrompt(mid, midTitle, slice.id, slice.title, basePath, { contextModeRenderMode: "nested" });
3226
3231
  subagentSections.push([
@@ -3242,7 +3247,7 @@ export async function buildParallelResearchSlicesPrompt(mid, midTitle, slices, b
3242
3247
  subagentPrompts: subagentSections.join("\n\n---\n\n"),
3243
3248
  });
3244
3249
  }
3245
- export async function buildGateEvaluatePrompt(mid, midTitle, sid, sTitle, base, subagentModel) {
3250
+ export async function buildGateEvaluatePrompt(mid, midTitle, sid, sTitle, base, subagentModel, subagentThinking) {
3246
3251
  // Pull only the gates this turn actually owns (Q3/Q4). Filter via the
3247
3252
  // registry so that scope:"slice" gates owned by other turns (Q8) can't
3248
3253
  // leak into this prompt and can't block dispatch via silent skip.
@@ -3291,7 +3296,7 @@ export async function buildGateEvaluatePrompt(mid, midTitle, sid, sTitle, base,
3291
3296
  "- `rationale`: one-sentence justification",
3292
3297
  "- `findings`: detailed markdown findings (or empty if omitted)",
3293
3298
  ].join("\n");
3294
- const modelSuffix = subagentModel ? ` with model: "${subagentModel}"` : "";
3299
+ const modelSuffix = subagentCallSuffix(subagentModel, subagentThinking);
3295
3300
  subagentSections.push([
3296
3301
  `### ${def.id}: ${def.question}`,
3297
3302
  "",
@@ -14,7 +14,7 @@ import { appendEvent } from "./workflow-events.js";
14
14
  import { atomicWriteSync } from "./atomic-write.js";
15
15
  import { clearParseCache } from "./files.js";
16
16
  import { parseRoadmap as parseLegacyRoadmap, parsePlan as parseLegacyPlan } from "./parsers-legacy.js";
17
- import { isDbAvailable, getTask, getSlice, getSliceTasks, getPendingGates, updateTaskStatus, updateSliceStatus, insertSlice, getMilestone, getMilestoneSlices, getLatestAssessmentByScope, updateMilestoneStatus, refreshOpenDatabaseFromDisk, transaction } from "./gsd-db.js";
17
+ import { isDbAvailable, getTask, getSlice, getSliceTasks, getPendingGatesForTurn, updateTaskStatus, updateSliceStatus, insertSlice, getMilestone, getMilestoneSlices, getLatestAssessmentByScope, updateMilestoneStatus, refreshOpenDatabaseFromDisk, transaction, } from "./gsd-db.js";
18
18
  import { isValidationTerminal } from "./state.js";
19
19
  import { getErrorMessage } from "./error-utils.js";
20
20
  import { logWarning, logError } from "./workflow-logger.js";
@@ -329,7 +329,9 @@ export function verifyExpectedArtifact(unitType, unitId, base) {
329
329
  if (gateIds.length === 0)
330
330
  return true;
331
331
  try {
332
- const pending = getPendingGates(mid, sid, "slice");
332
+ if (!isDbAvailable())
333
+ return false;
334
+ const pending = getPendingGatesForTurn(mid, sid, "gate-evaluate");
333
335
  const pendingIds = new Set(pending.map((g) => g.gate_id));
334
336
  // All dispatched gates must no longer be pending
335
337
  for (const gid of gateIds) {