@opengsd/gsd-pi 1.1.1-dev.9f86580 → 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 (261) 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/index.js +39 -22
  4. package/dist/resources/extensions/browser-tools/state.js +12 -0
  5. package/dist/resources/extensions/browser-tools/tools/session.js +3 -2
  6. package/dist/resources/extensions/browser-tools/utils.js +3 -3
  7. package/dist/resources/extensions/gsd/auto/loop.js +4 -2
  8. package/dist/resources/extensions/gsd/auto/phases.js +43 -10
  9. package/dist/resources/extensions/gsd/auto/session.js +20 -1
  10. package/dist/resources/extensions/gsd/auto/workflow-kernel.js +1 -0
  11. package/dist/resources/extensions/gsd/auto-dispatch.js +72 -12
  12. package/dist/resources/extensions/gsd/auto-model-selection.js +128 -9
  13. package/dist/resources/extensions/gsd/auto-post-unit.js +19 -2
  14. package/dist/resources/extensions/gsd/auto-prompts.js +24 -19
  15. package/dist/resources/extensions/gsd/auto-recovery.js +4 -2
  16. package/dist/resources/extensions/gsd/auto-runtime-state.js +3 -0
  17. package/dist/resources/extensions/gsd/auto-start.js +1 -1
  18. package/dist/resources/extensions/gsd/auto.js +14 -11
  19. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +3 -3
  20. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +172 -65
  21. package/dist/resources/extensions/gsd/closeout-wizard.js +32 -9
  22. package/dist/resources/extensions/gsd/commands/handlers/ops.js +2 -9
  23. package/dist/resources/extensions/gsd/commands-maintenance.js +93 -15
  24. package/dist/resources/extensions/gsd/commands-prefs-wizard.js +2 -2
  25. package/dist/resources/extensions/gsd/db-writer.js +35 -0
  26. package/dist/resources/extensions/gsd/docs/preferences-reference.md +50 -1
  27. package/dist/resources/extensions/gsd/gsd-db.js +480 -172
  28. package/dist/resources/extensions/gsd/markdown-renderer.js +37 -53
  29. package/dist/resources/extensions/gsd/md-importer.js +38 -3
  30. package/dist/resources/extensions/gsd/migration-auto-check.js +126 -31
  31. package/dist/resources/extensions/gsd/parsers-legacy.js +23 -0
  32. package/dist/resources/extensions/gsd/planning-path-scope.js +22 -4
  33. package/dist/resources/extensions/gsd/pre-execution-checks.js +10 -2
  34. package/dist/resources/extensions/gsd/preferences-models.js +110 -43
  35. package/dist/resources/extensions/gsd/preferences-types.js +13 -0
  36. package/dist/resources/extensions/gsd/preferences-validation.js +68 -3
  37. package/dist/resources/extensions/gsd/preferences.js +4 -1
  38. package/dist/resources/extensions/gsd/prompts/gate-evaluate.md +1 -1
  39. package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  40. package/dist/resources/extensions/gsd/prompts/refine-slice.md +1 -1
  41. package/dist/resources/extensions/gsd/roadmap-slices.js +5 -1
  42. package/dist/resources/extensions/gsd/safety/content-validator.js +6 -4
  43. package/dist/resources/extensions/gsd/source-observations.js +306 -0
  44. package/dist/resources/extensions/gsd/state-reconciliation/drift/completion.js +15 -8
  45. package/dist/resources/extensions/gsd/state-reconciliation/drift/stale-render.js +33 -5
  46. package/dist/resources/extensions/gsd/state-reconciliation/drift/stale-worker.js +34 -13
  47. package/dist/resources/extensions/gsd/state-reconciliation/index.js +39 -14
  48. package/dist/resources/extensions/gsd/state-reconciliation/spawn-gate.js +4 -4
  49. package/dist/resources/extensions/gsd/state.js +7 -3
  50. package/dist/resources/extensions/gsd/tool-contract.js +14 -0
  51. package/dist/resources/extensions/gsd/tool-presentation-plan.js +1 -9
  52. package/dist/resources/extensions/gsd/tools/complete-slice.js +7 -6
  53. package/dist/resources/extensions/gsd/tools/plan-slice.js +42 -11
  54. package/dist/resources/extensions/gsd/tools/plan-task.js +7 -1
  55. package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +57 -429
  56. package/dist/resources/extensions/gsd/uat-policy.js +130 -0
  57. package/dist/resources/extensions/gsd/uat-run.js +414 -0
  58. package/dist/resources/extensions/gsd/unit-context-manifest.js +3 -4
  59. package/dist/resources/extensions/gsd/verdict-parser.js +3 -8
  60. package/dist/resources/extensions/gsd/workflow-manifest.js +132 -5
  61. package/dist/resources/extensions/gsd/workflow-projections.js +8 -0
  62. package/dist/resources/extensions/gsd/worktree-state-projection.js +18 -17
  63. package/dist/resources/extensions/subagent/agents.js +1 -0
  64. package/dist/resources/extensions/subagent/index.js +27 -12
  65. package/dist/resources/extensions/subagent/launch.js +7 -2
  66. package/dist/web/standalone/.next/BUILD_ID +1 -1
  67. package/dist/web/standalone/.next/app-path-routes-manifest.json +8 -8
  68. package/dist/web/standalone/.next/build-manifest.json +2 -2
  69. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  70. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  71. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  72. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  73. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  74. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  75. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  76. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  77. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  78. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  79. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  80. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  81. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  82. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  83. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  84. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  85. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  86. package/dist/web/standalone/.next/server/app/index.html +1 -1
  87. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  88. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  89. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  90. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  91. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  92. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  93. package/dist/web/standalone/.next/server/app-paths-manifest.json +8 -8
  94. package/dist/web/standalone/.next/server/chunks/8357.js +1 -1
  95. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  96. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  97. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  98. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  99. package/dist/web/standalone/node_modules/@gsd/native/dist/native.js +22 -0
  100. package/dist/web/standalone/node_modules/node-pty/build/Makefile +1 -1
  101. package/package.json +4 -4
  102. package/packages/cloud-mcp-gateway/package.json +2 -2
  103. package/packages/contracts/package.json +1 -1
  104. package/packages/daemon/package.json +4 -4
  105. package/packages/gsd-agent-core/package.json +5 -5
  106. package/packages/gsd-agent-modes/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  107. package/packages/gsd-agent-modes/dist/modes/interactive/components/assistant-message.js +21 -23
  108. package/packages/gsd-agent-modes/dist/modes/interactive/components/assistant-message.js.map +1 -1
  109. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts +3 -0
  110. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  111. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js +25 -0
  112. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js.map +1 -1
  113. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.d.ts +1 -0
  114. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.d.ts.map +1 -1
  115. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js +66 -12
  116. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js.map +1 -1
  117. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  118. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js +18 -11
  119. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  120. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-chat-render.d.ts.map +1 -1
  121. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-chat-render.js +16 -0
  122. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-chat-render.js.map +1 -1
  123. package/packages/gsd-agent-modes/package.json +7 -7
  124. package/packages/mcp-server/dist/workflow-tools.js +1 -1
  125. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  126. package/packages/mcp-server/package.json +3 -3
  127. package/packages/native/dist/native.js +22 -0
  128. package/packages/native/package.json +1 -1
  129. package/packages/pi-agent-core/package.json +1 -1
  130. package/packages/pi-ai/dist/image-models.generated.d.ts +30 -0
  131. package/packages/pi-ai/dist/image-models.generated.d.ts.map +1 -1
  132. package/packages/pi-ai/dist/image-models.generated.js +30 -0
  133. package/packages/pi-ai/dist/image-models.generated.js.map +1 -1
  134. package/packages/pi-ai/dist/models.generated.d.ts +23 -17
  135. package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
  136. package/packages/pi-ai/dist/models.generated.js +25 -24
  137. package/packages/pi-ai/dist/models.generated.js.map +1 -1
  138. package/packages/pi-ai/package.json +1 -1
  139. package/packages/pi-coding-agent/dist/core/settings-manager.js +1 -1
  140. package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
  141. package/packages/pi-coding-agent/dist/theme/themes.js +1 -1
  142. package/packages/pi-coding-agent/dist/theme/themes.js.map +1 -1
  143. package/packages/pi-coding-agent/package.json +7 -7
  144. package/packages/pi-tui/dist/utils.d.ts +11 -0
  145. package/packages/pi-tui/dist/utils.d.ts.map +1 -1
  146. package/packages/pi-tui/dist/utils.js +119 -6
  147. package/packages/pi-tui/dist/utils.js.map +1 -1
  148. package/packages/pi-tui/package.json +2 -1
  149. package/packages/rpc-client/package.json +2 -2
  150. package/pkg/dist/theme/themes.js +1 -1
  151. package/pkg/dist/theme/themes.js.map +1 -1
  152. package/pkg/package.json +1 -1
  153. package/src/resources/extensions/browser-tools/index.ts +39 -22
  154. package/src/resources/extensions/browser-tools/state.ts +13 -0
  155. package/src/resources/extensions/browser-tools/tests/browser-tools-unit.test.cjs +57 -0
  156. package/src/resources/extensions/browser-tools/tools/session.ts +4 -2
  157. package/src/resources/extensions/browser-tools/utils.ts +3 -3
  158. package/src/resources/extensions/gsd/auto/loop-deps.ts +1 -0
  159. package/src/resources/extensions/gsd/auto/loop.ts +4 -2
  160. package/src/resources/extensions/gsd/auto/phases.ts +42 -10
  161. package/src/resources/extensions/gsd/auto/session.ts +22 -1
  162. package/src/resources/extensions/gsd/auto/workflow-kernel.ts +1 -0
  163. package/src/resources/extensions/gsd/auto-dispatch.ts +85 -12
  164. package/src/resources/extensions/gsd/auto-model-selection.ts +164 -12
  165. package/src/resources/extensions/gsd/auto-post-unit.ts +20 -2
  166. package/src/resources/extensions/gsd/auto-prompts.ts +23 -20
  167. package/src/resources/extensions/gsd/auto-recovery.ts +22 -3
  168. package/src/resources/extensions/gsd/auto-runtime-state.ts +5 -0
  169. package/src/resources/extensions/gsd/auto-start.ts +1 -1
  170. package/src/resources/extensions/gsd/auto.ts +13 -10
  171. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +3 -3
  172. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +225 -72
  173. package/src/resources/extensions/gsd/closeout-wizard.ts +47 -13
  174. package/src/resources/extensions/gsd/commands/handlers/ops.ts +2 -17
  175. package/src/resources/extensions/gsd/commands-maintenance.ts +124 -13
  176. package/src/resources/extensions/gsd/commands-prefs-wizard.ts +2 -2
  177. package/src/resources/extensions/gsd/db-writer.ts +38 -0
  178. package/src/resources/extensions/gsd/docs/preferences-reference.md +50 -1
  179. package/src/resources/extensions/gsd/gsd-db.ts +564 -186
  180. package/src/resources/extensions/gsd/markdown-renderer.ts +44 -66
  181. package/src/resources/extensions/gsd/md-importer.ts +49 -2
  182. package/src/resources/extensions/gsd/migration-auto-check.ts +154 -34
  183. package/src/resources/extensions/gsd/parsers-legacy.ts +20 -0
  184. package/src/resources/extensions/gsd/planning-path-scope.ts +22 -4
  185. package/src/resources/extensions/gsd/pre-execution-checks.ts +9 -2
  186. package/src/resources/extensions/gsd/preferences-models.ts +112 -43
  187. package/src/resources/extensions/gsd/preferences-types.ts +39 -0
  188. package/src/resources/extensions/gsd/preferences-validation.ts +76 -2
  189. package/src/resources/extensions/gsd/preferences.ts +5 -0
  190. package/src/resources/extensions/gsd/prompts/gate-evaluate.md +1 -1
  191. package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  192. package/src/resources/extensions/gsd/prompts/refine-slice.md +1 -1
  193. package/src/resources/extensions/gsd/roadmap-slices.ts +6 -1
  194. package/src/resources/extensions/gsd/safety/content-validator.ts +8 -5
  195. package/src/resources/extensions/gsd/source-observations.ts +402 -0
  196. package/src/resources/extensions/gsd/state-reconciliation/drift/completion.ts +20 -8
  197. package/src/resources/extensions/gsd/state-reconciliation/drift/stale-render.ts +44 -5
  198. package/src/resources/extensions/gsd/state-reconciliation/drift/stale-worker.ts +39 -11
  199. package/src/resources/extensions/gsd/state-reconciliation/index.ts +45 -15
  200. package/src/resources/extensions/gsd/state-reconciliation/spawn-gate.ts +4 -4
  201. package/src/resources/extensions/gsd/state.ts +7 -4
  202. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +15 -0
  203. package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +299 -1
  204. package/src/resources/extensions/gsd/tests/auto-paused-ui-cleanup.test.ts +32 -0
  205. package/src/resources/extensions/gsd/tests/auto-phases-lifecycle.test.ts +75 -3
  206. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +22 -1
  207. package/src/resources/extensions/gsd/tests/before-provider-context-management.test.ts +145 -0
  208. package/src/resources/extensions/gsd/tests/closeout-wizard.test.ts +44 -0
  209. package/src/resources/extensions/gsd/tests/commands-dispatcher-unmerged-milestone.test.ts +26 -1
  210. package/src/resources/extensions/gsd/tests/content-validator.test.ts +74 -0
  211. package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +16 -2
  212. package/src/resources/extensions/gsd/tests/doctor-scope-db-unavailable.test.ts +1 -11
  213. package/src/resources/extensions/gsd/tests/gate-dispatch.test.ts +64 -0
  214. package/src/resources/extensions/gsd/tests/gate-storage.test.ts +15 -0
  215. package/src/resources/extensions/gsd/tests/gsd-recover.test.ts +62 -1
  216. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +15 -0
  217. package/src/resources/extensions/gsd/tests/markdown-renderer.test.ts +42 -0
  218. package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +99 -0
  219. package/src/resources/extensions/gsd/tests/plan-slice.test.ts +99 -2
  220. package/src/resources/extensions/gsd/tests/plan-task.test.ts +19 -0
  221. package/src/resources/extensions/gsd/tests/preferences.test.ts +14 -0
  222. package/src/resources/extensions/gsd/tests/prefs-wizard-coverage.test.ts +1 -0
  223. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +9 -0
  224. package/src/resources/extensions/gsd/tests/register-hooks-depth-verification.test.ts +101 -1
  225. package/src/resources/extensions/gsd/tests/repository-registry.test.ts +2 -2
  226. package/src/resources/extensions/gsd/tests/runtime-invariant-modules.test.ts +8 -0
  227. package/src/resources/extensions/gsd/tests/schema-v21-sequence.test.ts +5 -3
  228. package/src/resources/extensions/gsd/tests/schema-v27-v28-sequence.test.ts +162 -18
  229. package/src/resources/extensions/gsd/tests/skipped-validation-db-atomicity.test.ts +8 -0
  230. package/src/resources/extensions/gsd/tests/source-observations.test.ts +275 -0
  231. package/src/resources/extensions/gsd/tests/stale-queued-milestone.test.ts +43 -0
  232. package/src/resources/extensions/gsd/tests/state-reconciliation-drift.test.ts +76 -21
  233. package/src/resources/extensions/gsd/tests/thinking-level-resolution.test.ts +203 -0
  234. package/src/resources/extensions/gsd/tests/uat-policy.test.ts +170 -0
  235. package/src/resources/extensions/gsd/tests/unit-context-manifest.test.ts +7 -1
  236. package/src/resources/extensions/gsd/tests/workflow-kernel.test.ts +7 -0
  237. package/src/resources/extensions/gsd/tests/workflow-manifest.test.ts +306 -1
  238. package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +73 -6
  239. package/src/resources/extensions/gsd/tests/worktree-db.test.ts +511 -1
  240. package/src/resources/extensions/gsd/tests/worktree-state-projection.test.ts +44 -0
  241. package/src/resources/extensions/gsd/tool-contract.ts +28 -0
  242. package/src/resources/extensions/gsd/tool-presentation-plan.ts +1 -11
  243. package/src/resources/extensions/gsd/tools/complete-slice.ts +7 -6
  244. package/src/resources/extensions/gsd/tools/plan-slice.ts +54 -12
  245. package/src/resources/extensions/gsd/tools/plan-task.ts +8 -1
  246. package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +66 -526
  247. package/src/resources/extensions/gsd/types.ts +1 -0
  248. package/src/resources/extensions/gsd/uat-policy.ts +191 -0
  249. package/src/resources/extensions/gsd/uat-run.ts +550 -0
  250. package/src/resources/extensions/gsd/unit-context-manifest.ts +3 -4
  251. package/src/resources/extensions/gsd/verdict-parser.ts +3 -10
  252. package/src/resources/extensions/gsd/workflow-manifest.ts +193 -7
  253. package/src/resources/extensions/gsd/workflow-projections.ts +9 -0
  254. package/src/resources/extensions/gsd/worktree-state-projection.ts +22 -22
  255. package/src/resources/extensions/shared/tests/format-utils.test.ts +8 -3
  256. package/src/resources/extensions/subagent/agents.ts +4 -0
  257. package/src/resources/extensions/subagent/index.ts +28 -3
  258. package/src/resources/extensions/subagent/launch.ts +8 -0
  259. package/src/resources/extensions/subagent/tests/model-override.test.ts +31 -0
  260. /package/dist/web/standalone/.next/static/{zzYMrKpPGfRQRxSFO32Jr → tJOKQbQRO-9MiFDO8DIDS}/_buildManifest.js +0 -0
  261. /package/dist/web/standalone/.next/static/{zzYMrKpPGfRQRxSFO32Jr → tJOKQbQRO-9MiFDO8DIDS}/_ssgManifest.js +0 -0
@@ -14,15 +14,16 @@
14
14
 
15
15
  import type { GSDState } from "./types.js";
16
16
  import type { GSDPreferences } from "./preferences.js";
17
- import type { UatType } from "./files.js";
18
17
  import type { MinimalModelRegistry } from "./context-budget.js";
19
18
  import { loadFile, extractUatType, loadActiveOverrides } from "./files.js";
19
+ import { getUatBrowserToolSupportError, type UatType } from "./uat-policy.js";
20
20
  import {
21
21
  isDbAvailable,
22
22
  getMilestoneSlices,
23
- getPendingGates,
24
- markAllGatesOmitted,
23
+ getPendingGatesForTurn,
24
+ markPendingGatesOmittedForTurn,
25
25
  getMilestone,
26
+ insertArtifact,
26
27
  insertAssessment,
27
28
  setSliceSketchFlag,
28
29
  transaction,
@@ -77,7 +78,7 @@ import {
77
78
  checkNeedsReassessment,
78
79
  checkNeedsRunUat,
79
80
  } from "./auto-prompts.js";
80
- import { resolveModelWithFallbacksForUnit } from "./preferences-models.js";
81
+ import { resolveModelWithFallbacksForUnit, resolveThinkingLevelForUnit } from "./preferences-models.js";
81
82
  import { resolveUokFlags } from "./uok/flags.js";
82
83
  import { selectReactiveDispatchBatch } from "./uok/execution-graph.js";
83
84
  import { getMilestonePipelineVariant } from "./milestone-scope-classifier.js";
@@ -439,6 +440,53 @@ export function findMissingSummaries(basePath: string, mid: string): string[] {
439
440
  .map(s => s.id);
440
441
  }
441
442
 
443
+ function stringField(row: Record<string, unknown> | null, key: string): string | null {
444
+ const value = row?.[key];
445
+ return typeof value === "string" ? value : null;
446
+ }
447
+
448
+ function stripGsdPrefix(path: string): string {
449
+ return path.startsWith(".gsd/") ? path.slice(".gsd/".length) : path;
450
+ }
451
+
452
+ function persistSliceAssessmentBackfill(
453
+ assessmentRelPath: string,
454
+ mid: string,
455
+ sliceId: string,
456
+ content: string,
457
+ ): void {
458
+ const artifactPath = stripGsdPrefix(assessmentRelPath);
459
+ const existingAssessment =
460
+ getAssessment(assessmentRelPath) ??
461
+ getAssessment(artifactPath);
462
+ const scope = stringField(existingAssessment, "scope") ?? "run-uat";
463
+ const status = stringField(existingAssessment, "status") ??
464
+ extractVerdict(content)?.toLowerCase() ??
465
+ "unknown";
466
+
467
+ transaction(() => {
468
+ insertArtifact({
469
+ path: artifactPath,
470
+ artifact_type: "ASSESSMENT",
471
+ milestone_id: mid,
472
+ slice_id: sliceId,
473
+ task_id: null,
474
+ full_content: content,
475
+ });
476
+ if (!getAssessment(assessmentRelPath)) {
477
+ insertAssessment({
478
+ path: assessmentRelPath,
479
+ milestoneId: mid,
480
+ sliceId,
481
+ taskId: null,
482
+ status,
483
+ scope,
484
+ fullContent: content,
485
+ });
486
+ }
487
+ });
488
+ }
489
+
442
490
  function backfillMissingAssessmentsFromSummaries(basePath: string, mid: string): void {
443
491
  const completedSliceIds = new Set<string>();
444
492
  if (isDbAvailable()) {
@@ -467,11 +515,12 @@ function backfillMissingAssessmentsFromSummaries(basePath: string, mid: string):
467
515
  const slicePath = resolveSlicePath(basePath, mid, sliceId);
468
516
  const assessmentPath = resolveSliceFile(basePath, mid, sliceId, "ASSESSMENT")
469
517
  ?? (slicePath ? join(slicePath, buildSliceFileName(sliceId, "ASSESSMENT")) : null);
470
- if (!assessmentPath || existsSync(assessmentPath)) continue;
518
+ if (!assessmentPath) continue;
471
519
 
472
- mkdirSync(dirname(assessmentPath), { recursive: true });
520
+ const assessmentRelPath = relSliceFile(basePath, mid, sliceId, "ASSESSMENT");
473
521
  const now = new Date().toISOString();
474
- const content = [
522
+ const didCreateAssessment = !existsSync(assessmentPath);
523
+ const content = didCreateAssessment ? [
475
524
  "---",
476
525
  `sliceId: ${sliceId}`,
477
526
  "verdict: PASS",
@@ -483,8 +532,20 @@ function backfillMissingAssessmentsFromSummaries(basePath: string, mid: string):
483
532
  "Auto-created during milestone validation because this completed slice had a SUMMARY but no ASSESSMENT artifact.",
484
533
  "No additional reassessment changes were detected in this backfill step.",
485
534
  "",
486
- ].join("\n");
487
- writeFileSync(assessmentPath, content, "utf-8");
535
+ ].join("\n") : readFileSync(assessmentPath, "utf-8");
536
+
537
+ if (isDbAvailable()) {
538
+ try {
539
+ persistSliceAssessmentBackfill(assessmentRelPath, mid, sliceId, content);
540
+ } catch (err) {
541
+ logWarning("dispatch", `failed to backfill assessment DB rows for ${mid}/${sliceId}: ${(err as Error).message}`);
542
+ }
543
+ }
544
+
545
+ if (didCreateAssessment) {
546
+ mkdirSync(dirname(assessmentPath), { recursive: true });
547
+ writeFileSync(assessmentPath, content, "utf-8");
548
+ }
488
549
  }
489
550
  }
490
551
 
@@ -689,6 +750,15 @@ export const DISPATCH_RULES: DispatchRule[] = [
689
750
  if (transportError) {
690
751
  return { action: "stop" as const, reason: transportError, level: "warning" as const };
691
752
  }
753
+ const browserToolError = getUatBrowserToolSupportError({
754
+ uatType,
755
+ activeTools,
756
+ milestoneId: mid,
757
+ sliceId,
758
+ });
759
+ if (browserToolError) {
760
+ return { action: "stop" as const, reason: browserToolError, level: "warning" as const };
761
+ }
692
762
 
693
763
  // Cap run-uat dispatch attempts to prevent infinite replay (#3624).
694
764
  // Check before incrementing so an exhausted counter cannot create a
@@ -1095,6 +1165,7 @@ export const DISPATCH_RULES: DispatchRule[] = [
1095
1165
  researchReadySlices,
1096
1166
  basePath,
1097
1167
  resolveModelWithFallbacksForUnit("subagent")?.primary,
1168
+ resolveThinkingLevelForUnit("subagent"),
1098
1169
  ),
1099
1170
  };
1100
1171
  },
@@ -1264,11 +1335,11 @@ export const DISPATCH_RULES: DispatchRule[] = [
1264
1335
  // Gate evaluation is opt-in via preferences
1265
1336
  const gateConfig = prefs?.gate_evaluation;
1266
1337
  if (!gateConfig?.enabled) {
1267
- markAllGatesOmitted(mid, sid);
1338
+ markPendingGatesOmittedForTurn(mid, sid, "gate-evaluate");
1268
1339
  return { action: "skip" };
1269
1340
  }
1270
1341
 
1271
- const pending = getPendingGates(mid, sid, "slice");
1342
+ const pending = getPendingGatesForTurn(mid, sid, "gate-evaluate");
1272
1343
  if (pending.length === 0) return { action: "skip" };
1273
1344
 
1274
1345
  return {
@@ -1282,6 +1353,7 @@ export const DISPATCH_RULES: DispatchRule[] = [
1282
1353
  sTitle,
1283
1354
  basePath,
1284
1355
  resolveModelWithFallbacksForUnit("subagent")?.primary,
1356
+ resolveThinkingLevelForUnit("subagent"),
1285
1357
  ),
1286
1358
  };
1287
1359
  },
@@ -1327,6 +1399,7 @@ export const DISPATCH_RULES: DispatchRule[] = [
1327
1399
  if (resolveSliceFile(basePath, mid, sid, "REACTIVE-BLOCKER")) return null;
1328
1400
  const maxParallel = reactiveConfig?.max_parallel ?? 2;
1329
1401
  const subagentModel = reactiveConfig?.subagent_model ?? resolveModelWithFallbacksForUnit("subagent")?.primary;
1402
+ const subagentThinking = resolveThinkingLevelForUnit("subagent");
1330
1403
  // Default-on safety threshold: only activate reactive dispatch when at
1331
1404
  // least N tasks are ready. Users who explicitly enabled reactive_execution
1332
1405
  // keep the legacy threshold of 2 (matches the prior "any parallelism is
@@ -1413,7 +1486,7 @@ export const DISPATCH_RULES: DispatchRule[] = [
1413
1486
  selected,
1414
1487
  basePath,
1415
1488
  subagentModel,
1416
- { sessionContextWindow, modelRegistry, sessionProvider },
1489
+ { sessionContextWindow, modelRegistry, sessionProvider, subagentThinking },
1417
1490
  ),
1418
1491
  };
1419
1492
  } catch (err) {
@@ -4,11 +4,11 @@
4
4
  * and fallback chains.
5
5
  */
6
6
 
7
- import type { Api, Model } from "@gsd/pi-ai";
8
- import { getProviderCapabilities } from "@gsd/pi-ai";
7
+ import type { Api, Model, ModelThinkingLevel } from "@gsd/pi-ai";
8
+ import { getProviderCapabilities, clampThinkingLevel } from "@gsd/pi-ai";
9
9
  import type { ExtensionAPI, ExtensionContext } from "@gsd/pi-coding-agent";
10
10
  import type { GSDPreferences } from "./preferences.js";
11
- import { resolveModelWithFallbacksForUnit, resolveDynamicRoutingConfig } from "./preferences.js";
11
+ import { resolveModelWithFallbacksForUnit, resolveThinkingLevelForUnit, resolveDynamicRoutingConfig } from "./preferences.js";
12
12
  import type { ComplexityTier } from "./complexity-classifier.js";
13
13
  import { classifyUnitComplexity, extractTaskMetadata, tierLabel } from "./complexity-classifier.js";
14
14
  import { resolveModelForComplexity, escalateTier, getEligibleModels, loadCapabilityOverrides, adjustToolSet, filterToolsForProvider } from "./model-router.js";
@@ -57,6 +57,12 @@ export interface ModelSelectionResult {
57
57
  routing: { tier: string; modelDowngraded: boolean } | null;
58
58
  /** Concrete model applied before dispatch so it can be restored after a fresh session. */
59
59
  appliedModel: Model<Api> | null;
60
+ /**
61
+ * Reasoning effort applied for this dispatch after per-phase resolution,
62
+ * floor, and capability clamping (ADR-026). Null when no level was applied
63
+ * (e.g. no start level captured). Surfaced for metrics/telemetry.
64
+ */
65
+ appliedThinkingLevel?: ReturnType<ExtensionAPI["getThinkingLevel"]> | null;
60
66
  }
61
67
 
62
68
  export interface PreferredModelConfig {
@@ -278,12 +284,103 @@ function restoreToolBaseline(pi: ExtensionAPI): void {
278
284
  }
279
285
  }
280
286
 
281
- function reapplyThinkingLevel(
287
+ /**
288
+ * Apply the desired reasoning effort for the just-selected model, clamping to
289
+ * what the model actually supports (ADR-026). An unsupported level is never
290
+ * sent to the provider — it is clamped via `clampThinkingLevel` and the
291
+ * mismatch is surfaced once per (model, requested-level). Returns the level
292
+ * actually applied so callers can record it.
293
+ */
294
+ export function applyThinkingLevelForModel(
282
295
  pi: ExtensionAPI,
296
+ desired: ReturnType<ExtensionAPI["getThinkingLevel"]> | null | undefined,
297
+ model: Model<Api>,
298
+ ctx: ExtensionContext,
299
+ ): ReturnType<ExtensionAPI["getThinkingLevel"]> | null | undefined {
300
+ if (!desired) return desired;
301
+ // Capability-clamp only when we have a bare string level AND the model
302
+ // advertises reasoning capability (`reasoning` is always present on real
303
+ // registry models). Richer host snapshot shapes (e.g. `{ effort: "high" }`)
304
+ // and partial model objects are applied verbatim — we never coerce an unknown
305
+ // shape into a string or guess capability we can't see.
306
+ if (typeof desired === "string" && model != null && typeof model === "object" && "reasoning" in model) {
307
+ const clamped = clampThinkingLevel(model, desired as ModelThinkingLevel) as ReturnType<ExtensionAPI["getThinkingLevel"]>;
308
+ pi.setThinkingLevel(clamped);
309
+ if (clamped !== desired) {
310
+ const key = `${model.provider}/${model.id}:${desired}`;
311
+ if (!_warnedThinkingClamp.has(key)) {
312
+ _warnedThinkingClamp.add(key);
313
+ ctx.ui.notify(
314
+ `Thinking level '${desired}' not supported by ${model.provider}/${model.id}; using '${clamped}'.`,
315
+ "warning",
316
+ );
317
+ }
318
+ }
319
+ return clamped;
320
+ }
321
+ pi.setThinkingLevel(desired);
322
+ return desired;
323
+ }
324
+
325
+ /** Warn-once guard for capability clamps, keyed by `provider/id:requested`. */
326
+ const _warnedThinkingClamp = new Set<string>();
327
+ /** Warn-once guard for the execute-task floor punch-through advisory. */
328
+ let _warnedExecuteTaskFloorBypass = false;
329
+
330
+ type EffectiveThinkingLevel = ReturnType<ExtensionAPI["getThinkingLevel"]>;
331
+
332
+ /**
333
+ * Ascending severity order for reasoning levels (matches @gsd/pi-agent-core
334
+ * `ThinkingLevel`). Used only for floor comparisons below.
335
+ */
336
+ const THINKING_LEVEL_ORDER: readonly EffectiveThinkingLevel[] = [
337
+ "off",
338
+ "minimal",
339
+ "low",
340
+ "medium",
341
+ "high",
342
+ "xhigh",
343
+ ] as EffectiveThinkingLevel[];
344
+
345
+ /**
346
+ * Minimum reasoning level for code-writing units.
347
+ *
348
+ * `execute-task` is the only unit that edits source. With a low/minimal
349
+ * thinking level a model does not plan its edits and compensates by re-reading
350
+ * the same files dozens of times per task (measured: index.html read ~49× in a
351
+ * single task on a minimal-thinking model) and shelling out to `nl`/`sed` to
352
+ * re-locate code after every edit invalidates its line numbers. Flooring the
353
+ * level for this unit type removes that read/bash thrash. Planning, research,
354
+ * and lifecycle units are unaffected.
355
+ */
356
+ const EXECUTE_TASK_MIN_THINKING_LEVEL: EffectiveThinkingLevel = "medium";
357
+
358
+ function thinkingLevelRank(level: EffectiveThinkingLevel): number {
359
+ const idx = THINKING_LEVEL_ORDER.indexOf(level);
360
+ return idx === -1 ? 0 : idx;
361
+ }
362
+
363
+ /**
364
+ * Raise (never lower) the thinking level for code-writing units to a sane
365
+ * floor. Returns the input unchanged for non-`execute-task` units, when no
366
+ * level was captured, or when the captured level already meets the floor.
367
+ */
368
+ export function floorThinkingLevelForUnit(
369
+ unitType: string,
283
370
  level: ReturnType<ExtensionAPI["getThinkingLevel"]> | null | undefined,
284
- ): void {
285
- if (!level) return;
286
- pi.setThinkingLevel(level);
371
+ ): ReturnType<ExtensionAPI["getThinkingLevel"]> | null | undefined {
372
+ if (unitType !== "execute-task") return level;
373
+ if (!level) return level;
374
+ // Only act on the recognized string levels. Any other shape (e.g. a richer
375
+ // host snapshot object) is passed through untouched so we never coerce an
376
+ // unknown representation into a bare string the host can't apply.
377
+ if (!THINKING_LEVEL_ORDER.includes(level as EffectiveThinkingLevel)) {
378
+ return level;
379
+ }
380
+ if (thinkingLevelRank(level as EffectiveThinkingLevel) >= thinkingLevelRank(EXECUTE_TASK_MIN_THINKING_LEVEL)) {
381
+ return level;
382
+ }
383
+ return EXECUTE_TASK_MIN_THINKING_LEVEL;
287
384
  }
288
385
 
289
386
  export function resolvePreferredModelConfig(
@@ -354,6 +451,42 @@ export async function selectAndApplyModel(
354
451
  autoModeStartThinkingLevel?: ReturnType<ExtensionAPI["getThinkingLevel"]> | null,
355
452
  ): Promise<ModelSelectionResult> {
356
453
  const uokFlags = resolveUokFlags(prefs);
454
+ // Resolve reasoning effort for this dispatch (ADR-026). An explicit per-phase
455
+ // thinking config (inline `models.<phase>.thinking` or the separate `thinking`
456
+ // block) expresses hard user intent: it bypasses the execute-task floor and is
457
+ // honored verbatim, then capability-clamped per model at apply time below.
458
+ // With no explicit level, fall back to the auto-start session level and raise
459
+ // the code-writing floor — preserving prior behavior exactly. Recomputed per
460
+ // dispatch so neither the floor nor a phase override leaks to other units.
461
+ const explicitThinkingLevel =
462
+ resolveThinkingLevelForUnit(unitType) as ReturnType<ExtensionAPI["getThinkingLevel"]> | undefined;
463
+ const desiredThinkingLevel = explicitThinkingLevel
464
+ ?? floorThinkingLevelForUnit(unitType, autoModeStartThinkingLevel);
465
+ if (explicitThinkingLevel) {
466
+ if (
467
+ unitType === "execute-task" &&
468
+ thinkingLevelRank(explicitThinkingLevel) < thinkingLevelRank(EXECUTE_TASK_MIN_THINKING_LEVEL) &&
469
+ !_warnedExecuteTaskFloorBypass
470
+ ) {
471
+ _warnedExecuteTaskFloorBypass = true;
472
+ ctx.ui.notify(
473
+ `Explicit execution thinking '${explicitThinkingLevel}' is below the measured execute-task floor ` +
474
+ `(${EXECUTE_TASK_MIN_THINKING_LEVEL}); honoring it as configured. Low reasoning on code edits can ` +
475
+ `cause repeated file re-reads.`,
476
+ "warning",
477
+ );
478
+ }
479
+ } else if (
480
+ verbose &&
481
+ desiredThinkingLevel &&
482
+ desiredThinkingLevel !== autoModeStartThinkingLevel
483
+ ) {
484
+ ctx.ui.notify(
485
+ `Thinking level raised to ${desiredThinkingLevel} for ${unitType} (was ${autoModeStartThinkingLevel ?? "unset"})`,
486
+ "info",
487
+ );
488
+ }
489
+ let appliedThinkingLevel: ReturnType<ExtensionAPI["getThinkingLevel"]> | null | undefined = null;
357
490
  const effectiveSessionModelOverride = sessionModelOverride === undefined
358
491
  ? getSessionModelOverride(ctx.sessionManager.getSessionId())
359
492
  : (sessionModelOverride ?? undefined);
@@ -699,7 +832,7 @@ export async function selectAndApplyModel(
699
832
  const ok = await pi.setModel(model, { persist: false });
700
833
  if (ok) {
701
834
  appliedModel = model;
702
- reapplyThinkingLevel(pi, autoModeStartThinkingLevel);
835
+ appliedThinkingLevel = applyThinkingLevelForModel(pi, desiredThinkingLevel, model, ctx);
703
836
 
704
837
  // ADR-005: Adjust active tool set for the selected model's provider capabilities.
705
838
  // Hard-filter incompatible tools, then let extensions override via adjust_tool_set hook.
@@ -759,7 +892,7 @@ export async function selectAndApplyModel(
759
892
  const ok = await pi.setModel(model, { persist: false });
760
893
  if (!ok) continue;
761
894
  appliedModel = model;
762
- reapplyThinkingLevel(pi, autoModeStartThinkingLevel);
895
+ appliedThinkingLevel = applyThinkingLevelForModel(pi, desiredThinkingLevel, model, ctx);
763
896
  attemptedPolicyEligible = true;
764
897
  if (verbose) {
765
898
  ctx.ui.notify(
@@ -805,18 +938,37 @@ export async function selectAndApplyModel(
805
938
  const fallbackOk = await pi.setModel(byId, { persist: false });
806
939
  if (fallbackOk) {
807
940
  appliedModel = byId;
808
- reapplyThinkingLevel(pi, autoModeStartThinkingLevel);
941
+ appliedThinkingLevel = applyThinkingLevelForModel(pi, desiredThinkingLevel, byId, ctx);
809
942
  }
810
943
  }
811
944
  } else {
812
945
  appliedModel = startModel;
813
- reapplyThinkingLevel(pi, autoModeStartThinkingLevel);
946
+ appliedThinkingLevel = applyThinkingLevelForModel(pi, desiredThinkingLevel, startModel, ctx);
814
947
  }
815
948
  }
816
949
  }
817
950
  }
818
951
 
819
- return { routing, appliedModel };
952
+ // If no model branch applied a thinking level (e.g. interactive guided-flow
953
+ // with a `thinking:` block but no per-phase model and no start model), still
954
+ // honor an explicitly configured phase thinking level against the current
955
+ // session model. Only the explicit path runs here — the floored session
956
+ // default is intentionally left untouched so no-config interactive runs keep
957
+ // the user's /model thinking level. (ADR-026)
958
+ if (appliedThinkingLevel == null && explicitThinkingLevel && ctx.model) {
959
+ // Prefer the full registry model (carries reasoning capability so the level
960
+ // can be clamped); fall back to ctx.model. Always route through
961
+ // applyThinkingLevelForModel so the clamp runs whenever capability metadata
962
+ // exists — never a raw verbatim setThinkingLevel that bypasses it (ADR-026).
963
+ const current = resolveModelId(
964
+ `${ctx.model.provider}/${ctx.model.id}`,
965
+ ctx.modelRegistry?.getAvailable?.() ?? [],
966
+ ctx.model.provider,
967
+ ) ?? (ctx.model as Model<Api>);
968
+ appliedThinkingLevel = applyThinkingLevelForModel(pi, explicitThinkingLevel, current, ctx);
969
+ }
970
+
971
+ return { routing, appliedModel, appliedThinkingLevel };
820
972
  }
821
973
 
822
974
  /**
@@ -1521,6 +1521,8 @@ export async function postUnitPreVerification(pctx: PostUnitContext, opts?: PreV
1521
1521
  }
1522
1522
  }
1523
1523
 
1524
+ let blockingContentViolation: string | null = null;
1525
+
1524
1526
  // ── Safety harness: post-unit validation ──
1525
1527
  try {
1526
1528
  const { loadEffectiveGSDPreferences } = await import("./preferences.js");
@@ -1668,8 +1670,14 @@ export async function postUnitPreVerification(pctx: PostUnitContext, opts?: PreV
1668
1670
  const artifactPath = resolveArtifactForContent(s.currentUnit.type, s.currentUnit.id, s.basePath);
1669
1671
  const contentViolations = validateContent(s.currentUnit.type, artifactPath);
1670
1672
  for (const v of contentViolations) {
1671
- logWarning("safety", `content: ${v.reason}`);
1672
- ctx.ui.notify(`Content validation: ${v.reason}`, "warning");
1673
+ if (v.severity === "error") {
1674
+ blockingContentViolation ??= v.reason;
1675
+ logError("safety", `content: ${v.reason}`);
1676
+ ctx.ui.notify(`Content validation: ${v.reason}`, "error");
1677
+ } else {
1678
+ logWarning("safety", `content: ${v.reason}`);
1679
+ ctx.ui.notify(`Content validation: ${v.reason}`, "warning");
1680
+ }
1673
1681
  }
1674
1682
  } catch (e) {
1675
1683
  debugLog("postUnit", { phase: "safety-content-validation", error: String(e) });
@@ -1868,6 +1876,16 @@ export async function postUnitPreVerification(pctx: PostUnitContext, opts?: PreV
1868
1876
  }
1869
1877
  }
1870
1878
 
1879
+ if (blockingContentViolation && triggerArtifactVerified) {
1880
+ triggerArtifactVerified = false;
1881
+ debugLog("postUnit", {
1882
+ phase: "content-validation-blocked-artifact",
1883
+ unitType: s.currentUnit.type,
1884
+ unitId: s.currentUnit.id,
1885
+ reason: blockingContentViolation,
1886
+ });
1887
+ }
1888
+
1871
1889
  // When artifact verification fails for a unit type that has a known expected
1872
1890
  // artifact, ask the caller to retry so it re-dispatches with failure context
1873
1891
  // instead of blindly re-dispatching the same unit (#1571).
@@ -10,8 +10,8 @@
10
10
  */
11
11
 
12
12
  import { loadFile, parseContinue, parseSummary, loadActiveOverrides, formatOverridesSection, parseTaskPlanFile } from "./files.js";
13
- import type { Override, UatType } from "./files.js";
14
- import { hasVerdict, getUatType, extractVerdict } from "./verdict-parser.js";
13
+ import type { Override } from "./files.js";
14
+ import { hasVerdict, extractVerdict } from "./verdict-parser.js";
15
15
  import { loadPrompt, inlineTemplate } from "./prompt-loader.js";
16
16
  import {
17
17
  resolveMilestoneFile, resolveSliceFile, resolveSlicePath,
@@ -42,11 +42,11 @@ import { logWarning } from "./workflow-logger.js";
42
42
  import { inlineGraphSubgraph } from "./graph-context.js";
43
43
  import { buildExtractionStepsBlock } from "./commands-extract-learnings.js";
44
44
  import { classifyProject, type ProjectClassification } from "./detection.js";
45
- import { hasBrowserRequiredText } from "./browser-evidence.js";
46
45
  import { debugLog } from "./debug-logger.js";
47
46
  import { buildSkillActivationBlock, buildSkillDiscoveryVars } from "./skill-activation.js";
48
47
  import { findMilestoneIds } from "./milestone-ids.js";
49
48
  import { buildRunUatPresentationForType, RUN_UAT_TOOL_PRESENTATION_PLAN_ID } from "./tool-presentation-plan.js";
49
+ import { resolveEffectiveUatType, shouldDispatchUatForContent, type UatType } from "./uat-policy.js";
50
50
 
51
51
  export { buildSkillActivationBlock, buildSkillDiscoveryVars };
52
52
 
@@ -286,19 +286,6 @@ function prependContextModeToBlock(
286
286
  return `${contextMode}\n\n${block}`;
287
287
  }
288
288
 
289
- function resolveEffectiveUatType(content: string): UatType {
290
- const uatType = getUatType(content);
291
- if (uatType === "artifact-driven" && hasBrowserRequiredText(content)) {
292
- return "browser-executable";
293
- }
294
- return uatType;
295
- }
296
-
297
- function shouldDispatchUatForContent(content: string, prefs: GSDPreferences | undefined): boolean {
298
- const uatType = resolveEffectiveUatType(content);
299
- return !!prefs?.uat_dispatch || uatType !== "artifact-driven" || hasBrowserRequiredText(content);
300
- }
301
-
302
289
  // ─── Executor Constraints ─────────────────────────────────────────────────────
303
290
 
304
291
  /**
@@ -3543,11 +3530,25 @@ export async function buildReassessRoadmapPrompt(
3543
3530
 
3544
3531
  // ─── Reactive Execute Prompt ──────────────────────────────────────────────
3545
3532
 
3533
+ /**
3534
+ * Build the `with model: "…" and thinking: "…"` suffix injected into a prompt
3535
+ * that instructs the coordinator how to dispatch a `subagent` call. Either or
3536
+ * both may be absent (ADR-026 / #508).
3537
+ */
3538
+ function subagentCallSuffix(model?: string, thinking?: string): string {
3539
+ const parts: string[] = [];
3540
+ if (model) parts.push(`model: "${model}"`);
3541
+ if (thinking) parts.push(`thinking: "${thinking}"`);
3542
+ return parts.length > 0 ? ` with ${parts.join(" and ")}` : "";
3543
+ }
3544
+
3546
3545
  export async function buildReactiveExecutePrompt(
3547
3546
  mid: string, midTitle: string, sid: string, sTitle: string,
3548
3547
  readyTaskIds: string[], base: string,
3549
3548
  subagentModel?: string,
3550
- opts?: { sessionContextWindow?: number; modelRegistry?: MinimalModelRegistry; sessionProvider?: string },
3549
+ // Reasoning effort travels inside opts here (not as a positional param) so
3550
+ // existing positional `opts` callers don't shift (#508).
3551
+ opts?: { sessionContextWindow?: number; modelRegistry?: MinimalModelRegistry; sessionProvider?: string; subagentThinking?: string },
3551
3552
  ): Promise<string> {
3552
3553
  const { loadSliceTaskIO, deriveTaskGraph, graphMetrics } = await import("./reactive-graph.js");
3553
3554
 
@@ -3640,7 +3641,7 @@ export async function buildReactiveExecutePrompt(
3640
3641
  `When done, say: "Task ${tid} complete."`,
3641
3642
  ].join("\n");
3642
3643
 
3643
- const modelSuffix = subagentModel ? ` with model: "${subagentModel}"` : "";
3644
+ const modelSuffix = subagentCallSuffix(subagentModel, opts?.subagentThinking);
3644
3645
  subagentSections.push([
3645
3646
  `### ${tid}: ${tTitle}`,
3646
3647
  "",
@@ -3724,10 +3725,11 @@ export async function buildParallelResearchSlicesPrompt(
3724
3725
  slices: Array<{ id: string; title: string }>,
3725
3726
  basePath: string,
3726
3727
  subagentModel?: string,
3728
+ subagentThinking?: string,
3727
3729
  ): Promise<string> {
3728
3730
  // Build individual research-slice prompts for each slice
3729
3731
  const subagentSections: string[] = [];
3730
- const modelSuffix = subagentModel ? ` with model: "${subagentModel}"` : "";
3732
+ const modelSuffix = subagentCallSuffix(subagentModel, subagentThinking);
3731
3733
  for (const slice of slices) {
3732
3734
  const slicePrompt = await buildResearchSlicePrompt(mid, midTitle, slice.id, slice.title, basePath, { contextModeRenderMode: "nested" });
3733
3735
  subagentSections.push([
@@ -3755,6 +3757,7 @@ export async function buildGateEvaluatePrompt(
3755
3757
  mid: string, midTitle: string, sid: string, sTitle: string,
3756
3758
  base: string,
3757
3759
  subagentModel?: string,
3760
+ subagentThinking?: string,
3758
3761
  ): Promise<string> {
3759
3762
  // Pull only the gates this turn actually owns (Q3/Q4). Filter via the
3760
3763
  // registry so that scope:"slice" gates owned by other turns (Q8) can't
@@ -3811,7 +3814,7 @@ export async function buildGateEvaluatePrompt(
3811
3814
  "- `findings`: detailed markdown findings (or empty if omitted)",
3812
3815
  ].join("\n");
3813
3816
 
3814
- const modelSuffix = subagentModel ? ` with model: "${subagentModel}"` : "";
3817
+ const modelSuffix = subagentCallSuffix(subagentModel, subagentThinking);
3815
3818
  subagentSections.push([
3816
3819
  `### ${def.id}: ${def.question}`,
3817
3820
  "",
@@ -15,7 +15,25 @@ import { appendEvent } from "./workflow-events.js";
15
15
  import { atomicWriteSync } from "./atomic-write.js";
16
16
  import { clearParseCache } from "./files.js";
17
17
  import { parseRoadmap as parseLegacyRoadmap, parsePlan as parseLegacyPlan } from "./parsers-legacy.js";
18
- import { isDbAvailable, getTask, getSlice, getSliceTasks, getPendingGates, updateTaskStatus, updateSliceStatus, insertSlice, getMilestone, getMilestoneSlices, getLatestAssessmentByScope, updateMilestoneStatus, refreshOpenDatabaseFromDisk, getCompletedMilestoneTaskFileHints, getMilestoneCommitAttributionShas, recordMilestoneCommitAttribution, transaction } from "./gsd-db.js";
18
+ import {
19
+ isDbAvailable,
20
+ getTask,
21
+ getSlice,
22
+ getSliceTasks,
23
+ getPendingGatesForTurn,
24
+ updateTaskStatus,
25
+ updateSliceStatus,
26
+ insertSlice,
27
+ getMilestone,
28
+ getMilestoneSlices,
29
+ getLatestAssessmentByScope,
30
+ updateMilestoneStatus,
31
+ refreshOpenDatabaseFromDisk,
32
+ getCompletedMilestoneTaskFileHints,
33
+ getMilestoneCommitAttributionShas,
34
+ recordMilestoneCommitAttribution,
35
+ transaction,
36
+ } from "./gsd-db.js";
19
37
  import { isValidationTerminal } from "./state.js";
20
38
  import { getErrorMessage } from "./error-utils.js";
21
39
  import { logWarning, logError } from "./workflow-logger.js";
@@ -390,8 +408,9 @@ export function verifyExpectedArtifact(
390
408
  if (gateIds.length === 0) return true;
391
409
 
392
410
  try {
393
- const pending = getPendingGates(mid, sid, "slice");
394
- const pendingIds = new Set(pending.map((g: any) => g.gate_id));
411
+ if (!isDbAvailable()) return false;
412
+ const pending = getPendingGatesForTurn(mid, sid, "gate-evaluate");
413
+ const pendingIds = new Set<string>(pending.map((g) => g.gate_id));
395
414
  // All dispatched gates must no longer be pending
396
415
  for (const gid of gateIds) {
397
416
  if (pendingIds.has(gid)) return false;
@@ -1,6 +1,7 @@
1
1
  // GSD auto-mode runtime state
2
2
  import { AutoSession } from "./auto/session.js";
3
3
  import type { CurrentUnit } from "./auto/session.js";
4
+ import type { SourceObservationStore } from "./source-observations.js";
4
5
  import {
5
6
  isDeterministicPolicyError,
6
7
  isQueuedUserMessageSkip,
@@ -65,3 +66,7 @@ export function clearToolInvocationError(): void {
65
66
  if (!autoSession.active) return;
66
67
  autoSession.lastToolInvocationError = null;
67
68
  }
69
+
70
+ export function getSourceObservationStore(): SourceObservationStore {
71
+ return autoSession.sourceObservations;
72
+ }
@@ -1557,7 +1557,7 @@ export async function bootstrapAutoSession(
1557
1557
  s.autoStartTime = Date.now();
1558
1558
  s.resourceVersionOnStart = readResourceVersion();
1559
1559
  s.pendingQuickTasks = [];
1560
- s.currentUnit = null;
1560
+ s.clearCurrentUnit();
1561
1561
  s.currentMilestoneId ??=
1562
1562
  strandedRecoveryAction?.milestoneId ??
1563
1563
  (deepProjectStagePending ? null : state.activeMilestone?.id ?? null);