@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
@@ -7,7 +7,7 @@ import { fileURLToPath } from "node:url";
7
7
 
8
8
  const __dirname = dirname(fileURLToPath(import.meta.url));
9
9
 
10
- import { ModelPolicyDispatchBlockedError, resolvePreferredModelConfig, resolveModelId, selectAndApplyModel } from "../auto-model-selection.js";
10
+ import { ModelPolicyDispatchBlockedError, resolvePreferredModelConfig, resolveModelId, selectAndApplyModel, floorThinkingLevelForUnit } from "../auto-model-selection.js";
11
11
 
12
12
  function makeTempDir(prefix: string): string {
13
13
  return mkdtempSync(join(tmpdir(), prefix));
@@ -534,6 +534,304 @@ test("selectAndApplyModel re-applies captured thinking level after setModel succ
534
534
  assert.deepEqual(thinkingLevels, [{ effort: "high" }]);
535
535
  });
536
536
 
537
+ // ─── floorThinkingLevelForUnit (#read-bash-thrash) ─────────────────────
538
+ test("floorThinkingLevelForUnit raises minimal/low to the floor for execute-task", () => {
539
+ assert.equal(floorThinkingLevelForUnit("execute-task", "off" as any), "medium");
540
+ assert.equal(floorThinkingLevelForUnit("execute-task", "minimal" as any), "medium");
541
+ assert.equal(floorThinkingLevelForUnit("execute-task", "low" as any), "medium");
542
+ });
543
+
544
+ test("floorThinkingLevelForUnit never lowers a level already at/above the floor", () => {
545
+ assert.equal(floorThinkingLevelForUnit("execute-task", "medium" as any), "medium");
546
+ assert.equal(floorThinkingLevelForUnit("execute-task", "high" as any), "high");
547
+ assert.equal(floorThinkingLevelForUnit("execute-task", "xhigh" as any), "xhigh");
548
+ });
549
+
550
+ test("floorThinkingLevelForUnit leaves non-execute-task units untouched", () => {
551
+ for (const unit of ["plan-slice", "plan-milestone", "research-milestone", "complete-slice", "validate-milestone"]) {
552
+ assert.equal(floorThinkingLevelForUnit(unit, "minimal" as any), "minimal");
553
+ }
554
+ });
555
+
556
+ test("floorThinkingLevelForUnit passes through null/undefined and unrecognized shapes", () => {
557
+ assert.equal(floorThinkingLevelForUnit("execute-task", null), null);
558
+ assert.equal(floorThinkingLevelForUnit("execute-task", undefined), undefined);
559
+ // A richer host snapshot object must not be coerced into a bare string.
560
+ const snapshot = { effort: "minimal" } as any;
561
+ assert.deepEqual(floorThinkingLevelForUnit("execute-task", snapshot), snapshot);
562
+ });
563
+
564
+ test("selectAndApplyModel raises minimal thinking to the floor for execute-task", async (t) => {
565
+ const originalCwd = process.cwd();
566
+ const tempProject = makeTempDir("gsd-routing-thinking-floor-");
567
+ const thinkingLevels: unknown[] = [];
568
+ t.after(() => {
569
+ process.chdir(originalCwd);
570
+ rmSync(tempProject, { recursive: true, force: true });
571
+ });
572
+
573
+ mkdirSync(join(tempProject, ".gsd"), { recursive: true });
574
+ writeFileSync(
575
+ join(tempProject, ".gsd", "PREFERENCES.md"),
576
+ ["---", "models:", " execute-task: claude-sonnet-4-6", "---"].join("\n"),
577
+ "utf-8",
578
+ );
579
+ process.chdir(tempProject);
580
+
581
+ await selectAndApplyModel(
582
+ {
583
+ modelRegistry: { getAvailable: () => [{ id: "claude-sonnet-4-6", provider: "anthropic", api: "anthropic-messages" }] },
584
+ sessionManager: { getSessionId: () => "test-session" },
585
+ ui: { notify: () => {} },
586
+ model: { provider: "anthropic", id: "claude-sonnet-4-6", api: "anthropic-messages" },
587
+ } as any,
588
+ {
589
+ setModel: async () => true,
590
+ setThinkingLevel: (level: unknown) => { thinkingLevels.push(level); },
591
+ emitBeforeModelSelect: async () => undefined,
592
+ getActiveTools: () => [],
593
+ emitAdjustToolSet: async () => undefined,
594
+ setActiveTools: () => {},
595
+ } as any,
596
+ "execute-task",
597
+ "M001/S01/T01",
598
+ tempProject,
599
+ undefined,
600
+ false,
601
+ { provider: "anthropic", id: "claude-sonnet-4-6" },
602
+ undefined,
603
+ true,
604
+ undefined,
605
+ "minimal" as any,
606
+ );
607
+
608
+ assert.deepEqual(thinkingLevels, ["medium"]);
609
+ });
610
+
611
+ test("selectAndApplyModel capability-clamps an unsupported thinking level (ADR-026)", async (t) => {
612
+ const originalCwd = process.cwd();
613
+ const tempProject = makeTempDir("gsd-routing-thinking-clamp-");
614
+ const thinkingLevels: unknown[] = [];
615
+ t.after(() => {
616
+ process.chdir(originalCwd);
617
+ rmSync(tempProject, { recursive: true, force: true });
618
+ });
619
+
620
+ // Reasoning-capable model whose map omits xhigh → xhigh must clamp to high.
621
+ const model = {
622
+ id: "claude-sonnet-4-6",
623
+ provider: "anthropic",
624
+ api: "anthropic-messages",
625
+ reasoning: true,
626
+ thinkingLevelMap: { low: "low", medium: "medium", high: "high" },
627
+ };
628
+ mkdirSync(join(tempProject, ".gsd"), { recursive: true });
629
+ writeFileSync(
630
+ join(tempProject, ".gsd", "PREFERENCES.md"),
631
+ ["---", "models:", " planning: claude-sonnet-4-6", "---"].join("\n"),
632
+ "utf-8",
633
+ );
634
+ process.chdir(tempProject);
635
+
636
+ await selectAndApplyModel(
637
+ {
638
+ modelRegistry: { getAvailable: () => [model] },
639
+ sessionManager: { getSessionId: () => "test-session" },
640
+ ui: { notify: () => {} },
641
+ model: { provider: "anthropic", id: "claude-sonnet-4-6", api: "anthropic-messages" },
642
+ } as any,
643
+ {
644
+ setModel: async () => true,
645
+ setThinkingLevel: (level: unknown) => { thinkingLevels.push(level); },
646
+ emitBeforeModelSelect: async () => undefined,
647
+ getActiveTools: () => [],
648
+ emitAdjustToolSet: async () => undefined,
649
+ setActiveTools: () => {},
650
+ } as any,
651
+ "plan-slice",
652
+ "M001/S01",
653
+ tempProject,
654
+ undefined,
655
+ false,
656
+ { provider: "anthropic", id: "claude-sonnet-4-6" },
657
+ undefined,
658
+ true,
659
+ undefined,
660
+ "xhigh" as any,
661
+ );
662
+
663
+ assert.deepEqual(thinkingLevels, ["high"]);
664
+ });
665
+
666
+ test("selectAndApplyModel applies an explicit per-phase thinking level (ADR-026)", async (t) => {
667
+ const originalCwd = process.cwd();
668
+ const tempProject = makeTempDir("gsd-routing-thinking-explicit-");
669
+ const thinkingLevels: unknown[] = [];
670
+ t.after(() => {
671
+ process.chdir(originalCwd);
672
+ rmSync(tempProject, { recursive: true, force: true });
673
+ });
674
+
675
+ const model = {
676
+ id: "claude-sonnet-4-6",
677
+ provider: "anthropic",
678
+ api: "anthropic-messages",
679
+ reasoning: true,
680
+ thinkingLevelMap: { low: "low", medium: "medium", high: "high", xhigh: "xhigh" },
681
+ };
682
+ mkdirSync(join(tempProject, ".gsd"), { recursive: true });
683
+ writeFileSync(
684
+ join(tempProject, ".gsd", "PREFERENCES.md"),
685
+ ["---", "models:", " planning:", " model: claude-sonnet-4-6", " thinking: xhigh", "---"].join("\n"),
686
+ "utf-8",
687
+ );
688
+ process.chdir(tempProject);
689
+
690
+ await selectAndApplyModel(
691
+ {
692
+ modelRegistry: { getAvailable: () => [model] },
693
+ sessionManager: { getSessionId: () => "test-session" },
694
+ ui: { notify: () => {} },
695
+ model: { provider: "anthropic", id: "claude-sonnet-4-6", api: "anthropic-messages" },
696
+ } as any,
697
+ {
698
+ setModel: async () => true,
699
+ setThinkingLevel: (level: unknown) => { thinkingLevels.push(level); },
700
+ emitBeforeModelSelect: async () => undefined,
701
+ getActiveTools: () => [],
702
+ emitAdjustToolSet: async () => undefined,
703
+ setActiveTools: () => {},
704
+ } as any,
705
+ "plan-slice",
706
+ "M001/S01",
707
+ tempProject,
708
+ undefined,
709
+ false,
710
+ { provider: "anthropic", id: "claude-sonnet-4-6" },
711
+ undefined,
712
+ true,
713
+ undefined,
714
+ // Session level is "low"; the explicit planning thinking (xhigh) must win.
715
+ "low" as any,
716
+ );
717
+
718
+ assert.deepEqual(thinkingLevels, ["xhigh"]);
719
+ });
720
+
721
+ test("selectAndApplyModel applies explicit thinking with no model pin (interactive, ADR-026)", async (t) => {
722
+ const originalCwd = process.cwd();
723
+ const tempProject = makeTempDir("gsd-routing-thinking-nomodel-");
724
+ const thinkingLevels: unknown[] = [];
725
+ t.after(() => {
726
+ process.chdir(originalCwd);
727
+ rmSync(tempProject, { recursive: true, force: true });
728
+ });
729
+
730
+ const model = {
731
+ id: "claude-sonnet-4-6",
732
+ provider: "anthropic",
733
+ api: "anthropic-messages",
734
+ reasoning: true,
735
+ thinkingLevelMap: { low: "low", medium: "medium", high: "high", xhigh: "xhigh" },
736
+ };
737
+ mkdirSync(join(tempProject, ".gsd"), { recursive: true });
738
+ // A `thinking:` block with NO models config — the interactive guided-flow
739
+ // scenario the bug report flagged (no per-phase model, no start model).
740
+ writeFileSync(
741
+ join(tempProject, ".gsd", "PREFERENCES.md"),
742
+ ["---", "thinking:", " planning: high", "---"].join("\n"),
743
+ "utf-8",
744
+ );
745
+ process.chdir(tempProject);
746
+
747
+ await selectAndApplyModel(
748
+ {
749
+ modelRegistry: { getAvailable: () => [model] },
750
+ sessionManager: { getSessionId: () => "test-session" },
751
+ ui: { notify: () => {} },
752
+ model: { provider: "anthropic", id: "claude-sonnet-4-6", api: "anthropic-messages" },
753
+ } as any,
754
+ {
755
+ setModel: async () => true,
756
+ setThinkingLevel: (level: unknown) => { thinkingLevels.push(level); },
757
+ emitBeforeModelSelect: async () => undefined,
758
+ getActiveTools: () => [],
759
+ emitAdjustToolSet: async () => undefined,
760
+ setActiveTools: () => {},
761
+ } as any,
762
+ "plan-slice",
763
+ "M001/S01",
764
+ tempProject,
765
+ undefined,
766
+ false,
767
+ null, // no autoModeStartModel
768
+ undefined,
769
+ false, // isAutoMode = false (interactive)
770
+ undefined,
771
+ undefined, // no captured session thinking level
772
+ );
773
+
774
+ // No model branch runs, but the explicit block thinking must still apply.
775
+ assert.deepEqual(thinkingLevels, ["high"]);
776
+ });
777
+
778
+ test("selectAndApplyModel clamps explicit no-model thinking via ctx.model when registry lookup fails (ADR-026)", async (t) => {
779
+ const originalCwd = process.cwd();
780
+ const tempProject = makeTempDir("gsd-routing-thinking-ctxmodel-");
781
+ const thinkingLevels: unknown[] = [];
782
+ t.after(() => {
783
+ process.chdir(originalCwd);
784
+ rmSync(tempProject, { recursive: true, force: true });
785
+ });
786
+
787
+ // ctx.model carries reasoning capability (map omits xhigh) but the registry
788
+ // returns nothing, so resolveModelId fails and ctx.model is the clamp source.
789
+ const ctxModel = {
790
+ id: "claude-sonnet-4-6",
791
+ provider: "anthropic",
792
+ api: "anthropic-messages",
793
+ reasoning: true,
794
+ thinkingLevelMap: { low: "low", medium: "medium", high: "high" },
795
+ };
796
+ mkdirSync(join(tempProject, ".gsd"), { recursive: true });
797
+ writeFileSync(
798
+ join(tempProject, ".gsd", "PREFERENCES.md"),
799
+ ["---", "thinking:", " planning: xhigh", "---"].join("\n"),
800
+ "utf-8",
801
+ );
802
+ process.chdir(tempProject);
803
+
804
+ await selectAndApplyModel(
805
+ {
806
+ modelRegistry: { getAvailable: () => [] }, // registry lookup fails
807
+ sessionManager: { getSessionId: () => "test-session" },
808
+ ui: { notify: () => {} },
809
+ model: ctxModel,
810
+ } as any,
811
+ {
812
+ setModel: async () => true,
813
+ setThinkingLevel: (level: unknown) => { thinkingLevels.push(level); },
814
+ emitBeforeModelSelect: async () => undefined,
815
+ getActiveTools: () => [],
816
+ emitAdjustToolSet: async () => undefined,
817
+ setActiveTools: () => {},
818
+ } as any,
819
+ "plan-slice",
820
+ "M001/S01",
821
+ tempProject,
822
+ undefined,
823
+ false,
824
+ null,
825
+ undefined,
826
+ false,
827
+ undefined,
828
+ undefined,
829
+ );
830
+
831
+ // xhigh is unsupported by ctx.model → clamped to high, never sent verbatim.
832
+ assert.deepEqual(thinkingLevels, ["high"]);
833
+ });
834
+
537
835
  test("resolveModelId: anthropic wins over claude-code when session provider is not claude-code", () => {
538
836
  const availableModels = [
539
837
  { id: "claude-sonnet-4-6", provider: "claude-code" },
@@ -173,6 +173,38 @@ test("cleanupAfterLoopExit preserves completion closeout surface after stopAuto
173
173
  }
174
174
  });
175
175
 
176
+ test("cleanupAfterLoopExit clears completionStopInProgress even when preserveStepSurfaceAfterLoopExit is also set", async () => {
177
+ const statusCalls: unknown[] = [];
178
+ const widgetCalls: unknown[] = [];
179
+
180
+ autoSession.reset();
181
+ autoSession.active = true;
182
+ autoSession.paused = false;
183
+ autoSession.completionStopInProgress = true;
184
+ autoSession.preserveStepSurfaceAfterLoopExit = true;
185
+
186
+ try {
187
+ await cleanupAfterLoopExit({
188
+ hasUI: true,
189
+ ui: {
190
+ setStatus: (...args: unknown[]) => statusCalls.push(args),
191
+ setWidget: (...args: unknown[]) => widgetCalls.push(args),
192
+ setHeader: () => {},
193
+ notify: () => {},
194
+ },
195
+ } as any);
196
+
197
+ assert.equal(
198
+ autoSession.completionStopInProgress,
199
+ false,
200
+ "completionStopInProgress must be cleared even when preserveStepSurfaceAfterLoopExit was also set",
201
+ );
202
+ assert.equal(autoSession.preserveStepSurfaceAfterLoopExit, false);
203
+ } finally {
204
+ autoSession.reset();
205
+ }
206
+ });
207
+
176
208
  test("pauseAuto preserves artifact retry counts across pause/resume", async () => {
177
209
  const base = mkdtempSync(join(tmpdir(), "gsd-pause-retry-count-"));
178
210
  const previousCwd = process.cwd();
@@ -207,6 +207,7 @@ test("runFinalize merges a verified complete-milestone immediately and only once
207
207
  const startedAt = Date.now();
208
208
  let lifecycleMergeCalls = 0;
209
209
  let resolverMergeCalls = 0;
210
+ const stopAutoCalls: Array<{ reason?: string; options?: unknown }> = [];
210
211
  s.basePath = base;
211
212
  s.originalBasePath = base;
212
213
  s.currentMilestoneId = "M001";
@@ -219,6 +220,9 @@ test("runFinalize merges a verified complete-milestone immediately and only once
219
220
  const result = await runFinalizeWithDeps(s, {
220
221
  preflightCleanRoot: () => ({ stashPushed: false }),
221
222
  postflightPopStash: () => ({ needsManualRecovery: false }),
223
+ stopAuto: async (_ctx: unknown, _pi: unknown, reason?: string, options?: unknown) => {
224
+ stopAutoCalls.push({ reason, options });
225
+ },
222
226
  resolver: {
223
227
  mergeAndExit() {
224
228
  resolverMergeCalls++;
@@ -232,10 +236,19 @@ test("runFinalize merges a verified complete-milestone immediately and only once
232
236
  },
233
237
  });
234
238
 
235
- assert.equal(result.action, "next");
239
+ assert.equal(result.action, "break");
240
+ assert.equal(result.reason, "milestone-complete");
236
241
  assert.equal(lifecycleMergeCalls, 1);
237
242
  assert.equal(resolverMergeCalls, 0);
238
243
  assert.equal(s.milestoneMergedInPhases, true);
244
+ assert.equal(stopAutoCalls.length, 1);
245
+ assert.equal(stopAutoCalls[0]?.reason, "Milestone M001 complete");
246
+ assert.deepEqual(stopAutoCalls[0]?.options, {
247
+ completionWidget: {
248
+ milestoneId: "M001",
249
+ milestoneTitle: "Milestone",
250
+ },
251
+ });
239
252
 
240
253
  s.currentUnit = {
241
254
  type: "complete-milestone",
@@ -245,6 +258,9 @@ test("runFinalize merges a verified complete-milestone immediately and only once
245
258
  const second = await runFinalizeWithDeps(s, {
246
259
  preflightCleanRoot: () => ({ stashPushed: false }),
247
260
  postflightPopStash: () => ({ needsManualRecovery: false }),
261
+ stopAuto: async (_ctx: unknown, _pi: unknown, reason?: string, options?: unknown) => {
262
+ stopAutoCalls.push({ reason, options });
263
+ },
248
264
  resolver: {
249
265
  mergeAndExit() {
250
266
  resolverMergeCalls++;
@@ -258,9 +274,11 @@ test("runFinalize merges a verified complete-milestone immediately and only once
258
274
  },
259
275
  });
260
276
 
261
- assert.equal(second.action, "next");
277
+ assert.equal(second.action, "break");
278
+ assert.equal(second.reason, "milestone-complete");
262
279
  assert.equal(lifecycleMergeCalls, 1);
263
280
  assert.equal(resolverMergeCalls, 0);
281
+ assert.equal(stopAutoCalls.length, 2);
264
282
  });
265
283
 
266
284
  test("runFinalize does not render next-phase handoff for complete-milestone", async (t) => {
@@ -302,7 +320,7 @@ test("runFinalize does not render next-phase handoff for complete-milestone", as
302
320
  },
303
321
  );
304
322
 
305
- assert.equal(result.action, "next");
323
+ assert.equal(result.action, "break");
306
324
  assert.equal(
307
325
  widgetCalls.some(([key]) => key === "gsd-outcome"),
308
326
  false,
@@ -310,6 +328,60 @@ test("runFinalize does not render next-phase handoff for complete-milestone", as
310
328
  );
311
329
  });
312
330
 
331
+ test("runFinalize clears gsd-step and gsd-progress before stopAuto on complete-milestone", async (t) => {
332
+ const base = mkdtempSync(join(tmpdir(), "gsd-finalize-stale-widget-"));
333
+ t.after(() => {
334
+ rmSync(base, { recursive: true, force: true });
335
+ });
336
+
337
+ const s = new AutoSession();
338
+ s.basePath = base;
339
+ s.originalBasePath = base;
340
+ s.currentMilestoneId = "M001";
341
+ s.currentUnit = {
342
+ type: "complete-milestone",
343
+ id: "M001",
344
+ startedAt: Date.now(),
345
+ };
346
+
347
+ const statusCalls: Array<[string, unknown]> = [];
348
+ const widgetCalls: Array<[string, unknown]> = [];
349
+
350
+ await runFinalizeWithDeps(
351
+ s,
352
+ {
353
+ preflightCleanRoot: () => ({ stashPushed: false }),
354
+ postflightPopStash: () => ({ needsManualRecovery: false }),
355
+ lifecycle: {
356
+ exitMilestone() {
357
+ return { ok: true, merged: true, codeFilesChanged: false };
358
+ },
359
+ },
360
+ },
361
+ {
362
+ hasUI: true,
363
+ ui: {
364
+ notify() {},
365
+ setStatus(key: string, value: unknown) {
366
+ statusCalls.push([key, value]);
367
+ },
368
+ setWidget(key: string, value: unknown) {
369
+ widgetCalls.push([key, value]);
370
+ },
371
+ },
372
+ },
373
+ );
374
+
375
+ assert.ok(
376
+ statusCalls.some(([key, val]) => key === "gsd-step" && val === undefined),
377
+ "gsd-step status should be cleared before stopAuto",
378
+ );
379
+ assert.ok(
380
+ widgetCalls.some(([key, val]) => key === "gsd-progress" && val === undefined),
381
+ "gsd-progress widget should be cleared before stopAuto",
382
+ );
383
+ });
384
+
313
385
  test("runFinalize stops before merge when an isolated unit leaks app files into project root", async (t) => {
314
386
  const root = mkdtempSync(join(tmpdir(), "gsd-root-leak-root-"));
315
387
  const worktree = join(root, ".gsd", "worktrees", "M001");
@@ -9,7 +9,7 @@ import { randomUUID } from "node:crypto";
9
9
 
10
10
  import { verifyExpectedArtifact, hasImplementationArtifacts, resolveExpectedArtifactPath, diagnoseExpectedArtifact, diagnoseWorktreeIntegrityFailure, buildLoopRemediationSteps, writeBlockerPlaceholder, refreshRecoveryDbForArtifact, writeReactiveExecuteBlocker } from "../auto-recovery.ts";
11
11
  import { resolveMilestoneFile } from "../paths.ts";
12
- import { openDatabase, closeDatabase, insertMilestone, insertSlice, insertGateRow, insertTask, insertAssessment, getMilestone, getMilestoneCommitAttributionShas, getTask } from "../gsd-db.ts";
12
+ import { openDatabase, closeDatabase, insertMilestone, insertSlice, insertGateRow, insertTask, insertAssessment, getMilestone, getMilestoneCommitAttributionShas, getTask, saveGateResult } from "../gsd-db.ts";
13
13
  import { readEvents } from "../workflow-events.ts";
14
14
  import { clearParseCache } from "../files.ts";
15
15
  import { parseRoadmap } from "../parsers-legacy.ts";
@@ -1613,6 +1613,27 @@ test("verifyExpectedArtifact checks pending gate-evaluate artifacts without ESM
1613
1613
  assert.equal(verified, false, "pending gates should keep gate-evaluate unverified");
1614
1614
  });
1615
1615
 
1616
+ test("verifyExpectedArtifact fails closed for gate-evaluate when the DB is unavailable", () => {
1617
+ const base = makeTmpProject();
1618
+ closeDatabase();
1619
+
1620
+ const verified = verifyExpectedArtifact("gate-evaluate", "M001/S01/gates+Q3", base);
1621
+
1622
+ assert.equal(verified, false, "gate-evaluate must verify against the DB-backed gate rows");
1623
+ });
1624
+
1625
+ test("verifyExpectedArtifact ignores complete-slice gates in stale gate-evaluate unit ids", () => {
1626
+ const base = makeTmpProject();
1627
+ insertGateRow({ milestoneId: "M001", sliceId: "S01", gateId: "Q4", scope: "slice" });
1628
+ insertGateRow({ milestoneId: "M001", sliceId: "S01", gateId: "Q8", scope: "slice" });
1629
+ saveGateResult({ milestoneId: "M001", sliceId: "S01", gateId: "Q3", verdict: "pass", rationale: "OK", findings: "" });
1630
+ saveGateResult({ milestoneId: "M001", sliceId: "S01", gateId: "Q4", verdict: "pass", rationale: "OK", findings: "" });
1631
+
1632
+ const verified = verifyExpectedArtifact("gate-evaluate", "M001/S01/gates+Q3,Q4,Q8", base);
1633
+
1634
+ assert.equal(verified, true, "pending Q8 belongs to complete-slice and must not keep gate-evaluate unverified");
1635
+ });
1636
+
1616
1637
  // ─── #4414 regressions ────────────────────────────────────────────────────────
1617
1638
 
1618
1639
  test("#4414: writeBlockerPlaceholder invalidates path cache so dispatch guard sees file", () => {
@@ -20,6 +20,10 @@ test('resolveAutoSupervisorConfig provides safe timeout defaults', () => {
20
20
  assert.equal(supervisor.soft_timeout_minutes, 20);
21
21
  assert.equal(supervisor.idle_timeout_minutes, 10);
22
22
  assert.equal(supervisor.hard_timeout_minutes, 30);
23
+ // A single hung tool gets its own short budget, well below the idle window,
24
+ // so a genuinely stuck tool is recovered in minutes instead of waiting out
25
+ // the full idle timeout.
26
+ assert.equal(supervisor.stalled_tool_timeout_minutes, 5);
23
27
  } finally {
24
28
  if (previousGsdHome === undefined) {
25
29
  delete process.env.GSD_HOME;
@@ -0,0 +1,145 @@
1
+ import test from "node:test";
2
+ import assert from "node:assert/strict";
3
+ import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
4
+ import { tmpdir } from "node:os";
5
+ import { join } from "node:path";
6
+
7
+ import { _setAutoActiveForTest } from "../auto.ts";
8
+ import { autoSession } from "../auto-runtime-state.js";
9
+ import { registerHooks } from "../bootstrap/register-hooks.ts";
10
+
11
+ type HookHandler = (event: any, ctx?: any) => Promise<any> | any;
12
+
13
+ function createHookHandlers(): Map<string, HookHandler[]> {
14
+ const handlers = new Map<string, HookHandler[]>();
15
+ const pi = {
16
+ on(event: string, handler: HookHandler) {
17
+ const existing = handlers.get(event) ?? [];
18
+ existing.push(handler);
19
+ handlers.set(event, existing);
20
+ },
21
+ };
22
+
23
+ registerHooks(pi as any, []);
24
+ return handlers;
25
+ }
26
+
27
+ function requireHook(handlers: Map<string, HookHandler[]>, event: string): HookHandler {
28
+ const handler = handlers.get(event)?.[0];
29
+ assert.ok(handler, `${event} hook should be registered`);
30
+ return handler;
31
+ }
32
+
33
+ test("before_provider_request truncates tool results outside auto-mode", async (t) => {
34
+ const dir = mkdtempSync(join(tmpdir(), "gsd-before-provider-context-"));
35
+ const gsdHome = join(dir, "home");
36
+ const project = join(dir, "project");
37
+ const previousCwd = process.cwd();
38
+ const previousGsdHome = process.env.GSD_HOME;
39
+
40
+ mkdirSync(join(project, ".gsd"), { recursive: true });
41
+ mkdirSync(gsdHome, { recursive: true });
42
+ writeFileSync(
43
+ join(project, ".gsd", "PREFERENCES.md"),
44
+ [
45
+ "---",
46
+ "version: 1",
47
+ "context_management:",
48
+ " tool_result_max_chars: 200",
49
+ " observation_mask_turns: 1",
50
+ "---",
51
+ "",
52
+ ].join("\n"),
53
+ "utf-8",
54
+ );
55
+
56
+ process.env.GSD_HOME = gsdHome;
57
+ process.chdir(project);
58
+ _setAutoActiveForTest(false);
59
+
60
+ t.after(() => {
61
+ _setAutoActiveForTest(false);
62
+ process.chdir(previousCwd);
63
+ if (previousGsdHome === undefined) delete process.env.GSD_HOME;
64
+ else process.env.GSD_HOME = previousGsdHome;
65
+ rmSync(dir, { recursive: true, force: true });
66
+ });
67
+
68
+ const beforeProviderRequest = requireHook(createHookHandlers(), "before_provider_request");
69
+ const messageText = "m".repeat(250);
70
+ const responsesOutput = "r".repeat(250);
71
+ const payload = {
72
+ messages: [
73
+ { role: "user", content: [{ type: "text", text: "keep me" }] },
74
+ {
75
+ role: "toolResult",
76
+ toolCallId: "toolu_test",
77
+ toolName: "Read",
78
+ isError: false,
79
+ content: [{ type: "text", text: messageText }],
80
+ },
81
+ ],
82
+ input: [
83
+ { role: "user", content: [{ type: "input_text", text: "keep me" }] },
84
+ { type: "function_call_output", call_id: "call_test", output: responsesOutput },
85
+ ],
86
+ };
87
+
88
+ await beforeProviderRequest({ payload });
89
+
90
+ const truncatedMessage = (payload.messages[1]?.content as Array<{ text?: string }>)[0]?.text ?? "";
91
+ const truncatedResponsesOutput = String(payload.input[1]?.output ?? "");
92
+
93
+ assert.match(truncatedMessage, /\[truncated\]/);
94
+ assert.match(truncatedResponsesOutput, /\[truncated\]/);
95
+ assert.ok(truncatedMessage.length < messageText.length);
96
+ assert.ok(truncatedResponsesOutput.length < responsesOutput.length);
97
+ assert.doesNotMatch(truncatedMessage, /result masked/);
98
+ assert.doesNotMatch(truncatedResponsesOutput, /result masked/);
99
+ });
100
+
101
+ test("successful shell result clears source context before provider injection", async (t) => {
102
+ const dir = mkdtempSync(join(tmpdir(), "gsd-before-provider-source-"));
103
+ const project = join(dir, "project");
104
+ mkdirSync(project, { recursive: true });
105
+ writeFileSync(join(project, "app.ts"), "export const value = 'before';\n");
106
+
107
+ autoSession.reset();
108
+ autoSession.active = true;
109
+ autoSession.basePath = project;
110
+ autoSession.setCurrentUnit({
111
+ type: "execute-task",
112
+ id: "M001/S01/T01",
113
+ startedAt: 123,
114
+ workspaceRoot: project,
115
+ });
116
+ autoSession.sourceObservations.observeRead({ path: "app.ts" });
117
+
118
+ t.after(() => {
119
+ autoSession.reset();
120
+ rmSync(dir, { recursive: true, force: true });
121
+ });
122
+
123
+ assert.match(autoSession.sourceObservations.renderActiveBlock() ?? "", /before/);
124
+
125
+ const handlers = createHookHandlers();
126
+ const toolResult = requireHook(handlers, "tool_result");
127
+ const beforeProviderRequest = requireHook(handlers, "before_provider_request");
128
+
129
+ await toolResult({
130
+ toolCallId: "toolu_bash",
131
+ toolName: "bash",
132
+ input: { command: "printf after > app.ts" },
133
+ isError: false,
134
+ result: "ok",
135
+ }, { cwd: project });
136
+
137
+ const payload = {
138
+ messages: [{ role: "user", content: [{ type: "text", text: "continue" }] }],
139
+ };
140
+ await beforeProviderRequest({ payload });
141
+
142
+ assert.equal(autoSession.sourceObservations.renderActiveBlock(), null);
143
+ assert.equal(payload.messages.length, 1);
144
+ assert.doesNotMatch(payload.messages[0].content[0].text, /Source Context Block/);
145
+ });
@@ -76,3 +76,12 @@ test('BUNDLED_SKILL_TRIGGERS: skill ids are unique', () => {
76
76
  seen.add(skill);
77
77
  }
78
78
  });
79
+
80
+ test('BUNDLED_SKILL_TRIGGERS: gsd-browser and agent-browser stay distinct', () => {
81
+ const gsdBrowser = BUNDLED_SKILL_TRIGGERS.find(entry => entry.skill === 'gsd-browser');
82
+ const agentBrowser = BUNDLED_SKILL_TRIGGERS.find(entry => entry.skill === 'agent-browser');
83
+
84
+ assert.ok(gsdBrowser, 'gsd-browser trigger should be registered');
85
+ assert.ok(agentBrowser, 'agent-browser trigger should be registered');
86
+ assert.notStrictEqual(gsdBrowser.trigger, agentBrowser.trigger);
87
+ });