@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
@@ -3,13 +3,13 @@
3
3
  import { ensureDbOpen } from "../bootstrap/dynamic-tools.js";
4
4
  import { sanitizeCompleteMilestoneParams } from "../bootstrap/sanitize-complete-milestone.js";
5
5
  import { loadWriteGateSnapshot, shouldBlockContextArtifactSaveInSnapshot, shouldBlockRootArtifactSaveInSnapshot } from "../bootstrap/write-gate.js";
6
- import { getActiveRequirements, insertMilestone, getMilestone, getSliceStatusSummary, getSliceTaskCounts, insertGateRun, readTransaction, saveGateResult, upsertQualityGate, } from "../gsd-db.js";
6
+ import { getActiveRequirements, insertMilestone, getMilestone, getSliceStatusSummary, getSliceTaskCounts, insertAssessment, insertGateRun, readTransaction, saveGateResult, upsertQualityGate, } from "../gsd-db.js";
7
7
  import { GATE_REGISTRY } from "../gate-registry.js";
8
8
  import { generateRequirementsMd, saveArtifactToDb } from "../db-writer.js";
9
- import { clearPathCache, resolveGsdPathContract, resolveMilestoneFile, resolveSliceFile } from "../paths.js";
9
+ import { clearPathCache, relSliceFile, resolveGsdPathContract, resolveMilestoneFile, resolveSliceFile } from "../paths.js";
10
10
  import { saveFile, clearParseCache } from "../files.js";
11
- import { existsSync, readdirSync, readFileSync, unlinkSync } from "node:fs";
12
- import { isAbsolute, join, resolve } from "node:path";
11
+ import { unlinkSync } from "node:fs";
12
+ import { join } from "node:path";
13
13
  import { handleCompleteMilestone } from "./complete-milestone.js";
14
14
  import { handleCompleteTask } from "./complete-task.js";
15
15
  import { handleCompleteSlice } from "./complete-slice.js";
@@ -26,7 +26,8 @@ import { invalidateStateCache } from "../state.js";
26
26
  import { loadEffectiveGSDPreferences } from "../preferences.js";
27
27
  import { parseProject } from "../schemas/parsers.js";
28
28
  import { getAutoRuntimeSnapshot } from "../auto-runtime-state.js";
29
- import { buildRunUatResultPresentation, canonicalWorkflowToolName, parseMcpToolName, RUN_UAT_FORBIDDEN_TOOL_NAMES, RUN_UAT_TOOL_PRESENTATION_PLAN_ID, RUN_UAT_WORKFLOW_TOOL_NAMES, } from "../tool-presentation-plan.js";
29
+ import { renderPlanFromDb } from "../markdown-renderer.js";
30
+ import { prepareUatRun, saveUatAttemptArtifact, } from "../uat-run.js";
30
31
  export const SUPPORTED_SUMMARY_ARTIFACT_TYPES = [
31
32
  "SUMMARY",
32
33
  "RESEARCH",
@@ -790,6 +791,7 @@ export async function executeSaveGateResult(params, basePath = process.cwd()) {
790
791
  rationale: params.rationale,
791
792
  findings: params.findings ?? "",
792
793
  });
794
+ await renderPlanFromDb(basePath, params.milestoneId, params.sliceId);
793
795
  invalidateStateCache();
794
796
  return {
795
797
  content: [{ type: "text", text: `Gate ${params.gateId} result saved: verdict=${params.verdict}` }],
@@ -813,436 +815,90 @@ function errorResult(operation, message, error) {
813
815
  isError: true,
814
816
  };
815
817
  }
816
- function isNonEmptyString(value) {
817
- return typeof value === "string" && value.trim().length > 0;
818
- }
819
- function mergeBlockedTools(current, canonical) {
820
- const merged = new Map();
821
- for (const entry of [...(current ?? []), ...canonical]) {
822
- merged.set(canonicalWorkflowToolName(parseMcpToolName(entry.name)?.tool ?? entry.name), entry);
823
- }
824
- return [...merged.values()];
825
- }
826
- function mergePresentedTools(current, canonical) {
827
- return [...new Set([...(current ?? []), ...canonical])];
828
- }
829
- function normalizeUatVerdict(params) {
830
- const raw = params;
831
- if (typeof raw.verdict === "string") {
832
- return { ...params, verdict: raw.verdict.toUpperCase() };
833
- }
834
- return params;
835
- }
836
- function supplyDefaultPresentation(params) {
837
- const raw = params;
838
- if (!raw.presentation) {
839
- return { ...params, presentation: buildRunUatResultPresentation() };
840
- }
841
- return params;
842
- }
843
- function mergeCanonicalPresentation(params) {
844
- const canonicalPresentation = buildRunUatResultPresentation();
845
- const providedPresentation = params.presentation;
846
- return {
847
- ...params,
848
- presentation: {
849
- ...providedPresentation,
850
- surface: providedPresentation.surface ?? canonicalPresentation.surface,
851
- presentedTools: mergePresentedTools(providedPresentation.presentedTools, canonicalPresentation.presentedTools),
852
- blockedTools: mergeBlockedTools(providedPresentation.blockedTools, canonicalPresentation.blockedTools),
853
- toolPresentationPlanId: RUN_UAT_TOOL_PRESENTATION_PLAN_ID,
854
- },
855
- };
856
- }
857
- const VALID_UAT_TYPES = [
858
- "artifact-driven",
859
- "browser-executable",
860
- "runtime-executable",
861
- "live-runtime",
862
- "mixed",
863
- "human-experience",
864
- ];
865
- function ensureUatRequiredFields(params) {
866
- if (!isNonEmptyString(params.milestoneId))
867
- return "milestoneId is required";
868
- if (!isNonEmptyString(params.sliceId))
869
- return "sliceId is required";
870
- if (!isNonEmptyString(params.uatType))
871
- return "uatType is required";
872
- if (!VALID_UAT_TYPES.includes(params.uatType)) {
873
- return `uatType must be one of: ${VALID_UAT_TYPES.join(", ")}`;
874
- }
875
- if (!["PASS", "FAIL", "PARTIAL"].includes(params.verdict))
876
- return "verdict must be PASS, FAIL, or PARTIAL";
877
- if (!Array.isArray(params.checks) || params.checks.length === 0)
878
- return "checks must contain at least one UAT check";
879
- if (!params.presentation || !Array.isArray(params.presentation.presentedTools))
880
- return "presentation.presentedTools is required";
881
- if (!Array.isArray(params.presentation.blockedTools))
882
- return "presentation.blockedTools is required";
883
- return null;
884
- }
885
- function approvedEvidenceRoots(basePath) {
886
- const contract = resolveGsdPathContract(basePath);
887
- return [contract.worktreeGsd, contract.projectGsd].filter((root) => typeof root === "string");
888
- }
889
- function approvedBrowserArtifactRoots(basePath) {
890
- const contract = resolveGsdPathContract(basePath);
891
- const roots = [contract.workRoot, contract.projectRoot].map((root) => join(root, ".artifacts", "browser"));
892
- return [...new Set(roots)];
893
- }
894
- function pathStartsWithin(parent, target) {
895
- const normalizedParent = parent.replace(/\\/g, "/").replace(/\/+$/, "");
896
- const normalizedTarget = target.replace(/\\/g, "/").replace(/\/+$/, "");
897
- return normalizedTarget === normalizedParent || normalizedTarget.startsWith(`${normalizedParent}/`);
898
- }
899
- function pushUnique(paths, candidate) {
900
- if (!paths.includes(candidate))
901
- paths.push(candidate);
902
- }
903
- function execMetaPathCandidates(basePath, ref) {
904
- const trimmed = ref.trim();
905
- const candidates = [];
906
- const execDirs = approvedEvidenceRoots(basePath).map((root) => join(root, "exec"));
907
- const normalizedRef = trimmed.replace(/\\/g, "/");
908
- const pathLike = normalizedRef.endsWith(".meta.json") || normalizedRef.includes("/.gsd/exec/");
909
- if (pathLike) {
910
- const rawPath = isAbsolute(trimmed) ? resolve(trimmed) : resolve(basePath, trimmed);
911
- pushUnique(candidates, rawPath);
912
- const relativeExecMarker = ".gsd/exec/";
913
- const markerIndex = normalizedRef.indexOf(relativeExecMarker);
914
- if (markerIndex >= 0) {
915
- const execRelative = normalizedRef.slice(markerIndex + relativeExecMarker.length);
916
- for (const execDir of execDirs) {
917
- pushUnique(candidates, join(execDir, execRelative));
918
- }
919
- }
920
- return candidates.filter((candidate) => execDirs.some((execDir) => pathStartsWithin(execDir, candidate)));
921
- }
922
- for (const execDir of execDirs) {
923
- pushUnique(candidates, join(execDir, `${trimmed}.meta.json`));
924
- }
925
- return candidates;
926
- }
927
- function resolveExecMetaPath(basePath, ref) {
928
- for (const candidate of execMetaPathCandidates(basePath, ref)) {
929
- if (existsSync(candidate))
930
- return candidate;
931
- }
932
- return null;
933
- }
934
- function evidencePathIsApproved(basePath, ref) {
935
- const normalizedRef = ref.replace(/\\/g, "/");
936
- if (normalizedRef.startsWith(".gsd/exec/") || normalizedRef.startsWith(".gsd/uat/"))
937
- return true;
938
- if (normalizedRef.startsWith(".artifacts/browser/")) {
939
- const resolvedRef = resolve(basePath, ref);
940
- return approvedBrowserArtifactRoots(basePath).some((root) => pathStartsWithin(root, resolvedRef));
941
- }
942
- const gsdEvidenceApproved = approvedEvidenceRoots(basePath).some((root) => {
943
- return pathStartsWithin(join(root, "exec"), ref) || pathStartsWithin(join(root, "uat"), ref);
944
- });
945
- if (gsdEvidenceApproved)
946
- return true;
947
- return approvedBrowserArtifactRoots(basePath).some((root) => pathStartsWithin(root, ref));
948
- }
949
- function validateEvidenceRef(basePath, evidence) {
950
- if (!isNonEmptyString(evidence.ref))
951
- return "evidence.ref is required";
952
- if (evidence.kind === "gsd_uat_exec" || evidence.kind === "gsd_exec") {
953
- const path = resolveExecMetaPath(basePath, evidence.ref.trim());
954
- if (!path)
955
- return `missing gsd_exec metadata for evidence id "${evidence.ref}"`;
956
- if (evidence.kind === "gsd_uat_exec") {
957
- try {
958
- const meta = JSON.parse(readFileSync(path, "utf-8"));
959
- if (meta.metadata?.kind !== "uat_exec")
960
- return `evidence id "${evidence.ref}" is not typed as uat_exec`;
961
- }
962
- catch {
963
- return `invalid gsd_exec metadata JSON for evidence id "${evidence.ref}"`;
964
- }
965
- }
966
- return null;
967
- }
968
- if (evidence.kind === "url") {
969
- try {
970
- const parsed = new URL(evidence.ref);
971
- return parsed.protocol === "http:" || parsed.protocol === "https:"
972
- ? null
973
- : `invalid URL evidence ref "${evidence.ref}"`;
974
- }
975
- catch {
976
- return `invalid URL evidence ref "${evidence.ref}"`;
977
- }
978
- }
979
- return evidencePathIsApproved(basePath, evidence.ref)
980
- ? null
981
- : `evidence ref "${evidence.ref}" is outside approved evidence locations`;
982
- }
983
- function validateUatChecks(basePath, params) {
984
- for (const check of params.checks) {
985
- if (!isNonEmptyString(check.id))
986
- return "every check must have a non-empty id";
987
- if (!isNonEmptyString(check.description))
988
- return `check ${check.id} must have a description`;
989
- if (!["artifact", "runtime", "browser", "human-follow-up"].includes(check.mode)) {
990
- return `check ${check.id} has invalid mode "${check.mode}"`;
991
- }
992
- if (!["PASS", "FAIL", "NEEDS-HUMAN"].includes(check.result)) {
993
- return `check ${check.id} has invalid result "${check.result}"`;
994
- }
995
- if (check.result === "PASS" || check.result === "FAIL") {
996
- if (!Array.isArray(check.evidence) || check.evidence.length === 0) {
997
- return `check ${check.id} is ${check.result} but has no objective evidence`;
998
- }
999
- for (const evidence of check.evidence) {
1000
- const error = validateEvidenceRef(basePath, evidence);
1001
- if (error)
1002
- return `check ${check.id}: ${error}`;
1003
- }
1004
- }
1005
- else if (!isNonEmptyString(check.notes)) {
1006
- return `check ${check.id} is NEEDS-HUMAN but has no manual instruction or reason`;
1007
- }
1008
- }
1009
- return null;
1010
- }
1011
- function validateFreshUatOwnedEvidence(params) {
1012
- const hasFreshUatEvidence = params.checks.some((check) => (check.evidence ?? []).some((evidence) => evidence.kind === "gsd_uat_exec"));
1013
- return hasFreshUatEvidence
1014
- ? null
1015
- : "UAT Assessment requires at least one fresh gsd_uat_exec evidence reference from run-uat";
1016
- }
1017
- function validateUatMode(params) {
1018
- const modes = new Set(params.checks.map((check) => check.mode));
1019
- const hasHuman = params.checks.some((check) => check.result === "NEEDS-HUMAN");
1020
- if (params.uatType === "artifact-driven" && hasHuman && params.verdict === "PASS") {
1021
- return "artifact-driven UAT cannot PASS with human-only checks";
1022
- }
1023
- if (hasHuman &&
1024
- params.verdict === "PASS" &&
1025
- !["human-experience", "mixed", "live-runtime"].includes(params.uatType) &&
1026
- !params.checks.every((check) => check.result !== "NEEDS-HUMAN" || check.nonAutomatable === true)) {
1027
- return "NEEDS-HUMAN checks can only coexist with PASS for human-experience, mixed, live-runtime, or explicitly non-automatable checks";
1028
- }
1029
- if (params.uatType === "runtime-executable" && !modes.has("runtime")) {
1030
- return "runtime-executable UAT requires at least one runtime check";
1031
- }
1032
- if (params.uatType === "browser-executable" && !modes.has("browser")) {
1033
- return "browser-executable UAT requires at least one browser check";
1034
- }
1035
- if (params.uatType === "live-runtime" && !modes.has("runtime") && !modes.has("browser")) {
1036
- return "live-runtime UAT requires runtime or browser evidence";
1037
- }
1038
- return null;
1039
- }
1040
- function quoteToolNames(toolNames) {
1041
- return toolNames.map((toolName) => `"${toolName}"`).join(", ");
1042
- }
1043
- function validateCanonicalPresentation(params) {
1044
- const aliasHints = {
1045
- gsd_save_summary: "gsd_summary_save",
1046
- gsd_complete_task: "gsd_task_complete",
1047
- gsd_complete_slice: "gsd_slice_complete",
1048
- gsd_milestone_complete: "gsd_complete_milestone",
1049
- };
1050
- const errors = [];
1051
- for (const toolName of params.presentation.presentedTools) {
1052
- const baseName = parseMcpToolName(toolName)?.tool ?? toolName;
1053
- const canonical = aliasHints[baseName];
1054
- if (canonical)
1055
- errors.push(`presentation tool "${toolName}" uses an alias; use canonical "${canonical}"`);
1056
- }
1057
- const presentedCanonical = new Set(params.presentation.presentedTools.map((toolName) => canonicalWorkflowToolName(parseMcpToolName(toolName)?.tool ?? toolName)));
1058
- const missingRequiredTools = RUN_UAT_WORKFLOW_TOOL_NAMES.filter((requiredTool) => !presentedCanonical.has(requiredTool));
1059
- if (missingRequiredTools.length === 1) {
1060
- errors.push(`presentation is missing required UAT tool "${missingRequiredTools[0]}"`);
1061
- }
1062
- else if (missingRequiredTools.length > 1) {
1063
- errors.push(`presentation is missing required UAT tools ${quoteToolNames(missingRequiredTools)}`);
1064
- }
1065
- const forbiddenCanonical = new Set(RUN_UAT_FORBIDDEN_TOOL_NAMES
1066
- .filter((toolName) => !toolName.includes("*"))
1067
- .map((toolName) => canonicalWorkflowToolName(parseMcpToolName(toolName)?.tool ?? toolName)));
1068
- const forbiddenPresentedTools = [];
1069
- for (const toolName of params.presentation.presentedTools) {
1070
- const canonical = canonicalWorkflowToolName(parseMcpToolName(toolName)?.tool ?? toolName);
1071
- if (toolName === "mcp__gsd-workflow__*" || forbiddenCanonical.has(canonical)) {
1072
- forbiddenPresentedTools.push(toolName);
1073
- }
1074
- }
1075
- if (forbiddenPresentedTools.length === 1) {
1076
- errors.push(`presentation includes forbidden run-uat tool "${forbiddenPresentedTools[0]}"`);
1077
- }
1078
- else if (forbiddenPresentedTools.length > 1) {
1079
- errors.push(`presentation includes forbidden run-uat tools ${quoteToolNames(forbiddenPresentedTools)}`);
1080
- }
1081
- const blockedCanonical = new Set(params.presentation.blockedTools.map((entry) => canonicalWorkflowToolName(parseMcpToolName(entry.name)?.tool ?? entry.name)));
1082
- const missingBlockedTools = ["gsd_exec", "gsd_summary_save", "gsd_save_gate_result"].filter((blockedTool) => !blockedCanonical.has(blockedTool));
1083
- if (missingBlockedTools.length === 1) {
1084
- errors.push(`presentation must record "${missingBlockedTools[0]}" as blocked during run-uat`);
1085
- }
1086
- else if (missingBlockedTools.length > 1) {
1087
- errors.push(`presentation must record ${quoteToolNames(missingBlockedTools)} as blocked during run-uat`);
1088
- }
1089
- return errors.length > 0 ? errors.join("; ") : null;
1090
- }
1091
- function nextUatAttempt(basePath, milestoneId, sliceId) {
1092
- const contract = resolveGsdPathContract(basePath);
1093
- const dir = join(contract.projectGsd, "uat", milestoneId, sliceId);
1094
- if (!existsSync(dir))
1095
- return 1;
1096
- let max = 0;
1097
- for (const entry of readdirSync(dir)) {
1098
- const match = /^attempt-(\d+)\.json$/.exec(entry);
1099
- if (match)
1100
- max = Math.max(max, Number(match[1]));
1101
- }
1102
- return max + 1;
1103
- }
1104
- function escapeMarkdownTableCell(value) {
1105
- return String(value ?? "")
1106
- .replace(/[\\|]/g, (char) => `\\${char}`)
1107
- .replace(/\r?\n/g, "<br>");
1108
- }
1109
- function renderUatAssessment(params, attempt, gateVerdict) {
1110
- const lines = [
1111
- "---",
1112
- `sliceId: ${params.sliceId}`,
1113
- `uatType: ${params.uatType}`,
1114
- `verdict: ${params.verdict}`,
1115
- `attempt: ${attempt}`,
1116
- `date: ${new Date().toISOString()}`,
1117
- "---",
1118
- "",
1119
- `# UAT Result - ${params.sliceId}`,
1120
- "",
1121
- "## Checks",
1122
- "",
1123
- "| Check | Mode | Result | Evidence | Notes |",
1124
- "|-------|------|--------|----------|-------|",
1125
- ...params.checks.map((check) => {
1126
- const evidence = (check.evidence ?? []).map((entry) => `${entry.kind}:${entry.ref}`).join("<br>") || "-";
1127
- return `| ${escapeMarkdownTableCell(check.description)} | ${escapeMarkdownTableCell(check.mode)} | ${escapeMarkdownTableCell(check.result)} | ${escapeMarkdownTableCell(evidence)} | ${escapeMarkdownTableCell(check.notes)} |`;
1128
- }),
1129
- "",
1130
- "## Overall Verdict",
1131
- "",
1132
- `${params.verdict} - ${params.notes ?? "UAT result saved."}`,
1133
- "",
1134
- "## Tool Presentation",
1135
- "",
1136
- "```json",
1137
- JSON.stringify(params.presentation, null, 2),
1138
- "```",
1139
- "",
1140
- "## Gate",
1141
- "",
1142
- `Aggregate UAT gate saved as ${gateVerdict}.`,
1143
- ];
1144
- return `${lines.join("\n")}\n`;
1145
- }
1146
- async function saveUatAttemptArtifact(basePath, params, attempt) {
1147
- const contract = resolveGsdPathContract(basePath);
1148
- const relativePath = `uat/${params.milestoneId}/${params.sliceId}/attempt-${attempt}.json`;
1149
- await saveFile(join(contract.projectGsd, relativePath), `${JSON.stringify({ ...params, attempt }, null, 2)}\n`);
1150
- return relativePath;
1151
- }
1152
818
  export async function executeUatResultSave(params, basePath = process.cwd()) {
1153
819
  const unitGuard = blockIfWrongAutoUnit("run-uat", "save_uat_result");
1154
820
  if (unitGuard)
1155
821
  return unitGuard;
1156
- // Phase 1: normalize verdict and supply the canonical presentation when none was provided.
1157
- params = normalizeUatVerdict(params);
1158
- params = supplyDefaultPresentation(params);
1159
822
  const dbAvailable = await ensureDbOpen(basePath);
1160
823
  if (!dbAvailable)
1161
824
  return errorResult("save_uat_result", "GSD database is not available.", "db_unavailable");
1162
- // Phase 2: validate the submitted presentation before the canonical merge so that
1163
- // presentations missing required workflow tools are rejected rather than silently patched.
1164
- const requiredError = ensureUatRequiredFields(params);
1165
- if (requiredError)
1166
- return errorResult("save_uat_result", requiredError, "invalid_params");
1167
- const presentationError = validateCanonicalPresentation(params);
1168
- if (presentationError)
1169
- return errorResult("save_uat_result", presentationError, "alias_tool_name");
1170
- // Phase 3: merge in the canonical plan ID and read-only audit tools so the persisted
1171
- // artifact always carries the full audit surface even when the provider omitted them.
1172
- params = mergeCanonicalPresentation(params);
1173
- const checkError = validateUatChecks(basePath, params);
1174
- if (checkError)
1175
- return errorResult("save_uat_result", checkError, "invalid_evidence");
1176
- const freshEvidenceError = validateFreshUatOwnedEvidence(params);
1177
- if (freshEvidenceError)
1178
- return errorResult("save_uat_result", freshEvidenceError, "missing_fresh_uat_evidence");
1179
- const modeError = validateUatMode(params);
1180
- if (modeError)
1181
- return errorResult("save_uat_result", modeError, "uat_mode_mismatch");
825
+ const prepared = prepareUatRun(basePath, params);
826
+ if (!prepared.ok) {
827
+ return errorResult("save_uat_result", prepared.error.message, prepared.error.code);
828
+ }
829
+ const { run } = prepared;
1182
830
  try {
1183
- const attempt = params.attempt === "auto" || params.attempt === undefined
1184
- ? nextUatAttempt(basePath, params.milestoneId, params.sliceId)
1185
- : typeof params.attempt === "string"
1186
- ? Number.parseInt(params.attempt, 10)
1187
- : params.attempt;
1188
- if (!Number.isInteger(attempt) || attempt < 1) {
1189
- return errorResult("save_uat_result", "attempt must be a positive integer or auto", "invalid_attempt");
1190
- }
1191
- const gateVerdict = params.verdict === "PASS" ? "pass" : "flag";
1192
- const rationale = params.notes ?? `UAT ${params.verdict} for ${params.sliceId}.`;
1193
- const assessment = renderUatAssessment(params, attempt, gateVerdict);
1194
831
  const summary = await executeSummarySave({
1195
- milestone_id: params.milestoneId,
1196
- slice_id: params.sliceId,
832
+ milestone_id: run.params.milestoneId,
833
+ slice_id: run.params.sliceId,
1197
834
  artifact_type: "ASSESSMENT",
1198
- content: assessment,
835
+ content: run.assessment,
1199
836
  }, basePath);
1200
837
  if (summary.isError)
1201
838
  return summary;
1202
- const attemptPath = await saveUatAttemptArtifact(basePath, params, attempt);
1203
- const evaluatedAt = new Date().toISOString();
839
+ const assessmentPath = relSliceFile(basePath, run.params.milestoneId, run.params.sliceId, "ASSESSMENT");
840
+ insertAssessment({
841
+ path: assessmentPath,
842
+ milestoneId: run.params.milestoneId,
843
+ sliceId: run.params.sliceId,
844
+ taskId: null,
845
+ status: run.params.verdict.toLowerCase(),
846
+ scope: "run-uat",
847
+ fullContent: run.assessment,
848
+ });
849
+ const attemptPath = await saveUatAttemptArtifact(basePath, run);
1204
850
  upsertQualityGate({
1205
- milestoneId: params.milestoneId,
1206
- sliceId: params.sliceId,
851
+ milestoneId: run.params.milestoneId,
852
+ sliceId: run.params.sliceId,
1207
853
  gateId: "UAT",
1208
854
  scope: "slice",
1209
855
  taskId: "",
1210
856
  status: "complete",
1211
- verdict: gateVerdict,
1212
- rationale,
1213
- findings: assessment,
1214
- evaluatedAt,
857
+ verdict: run.gateVerdict,
858
+ rationale: run.rationale,
859
+ findings: run.assessment,
860
+ evaluatedAt: run.evaluatedAt,
1215
861
  });
1216
862
  insertGateRun({
1217
- traceId: `uat:${params.milestoneId}:${params.sliceId}`,
1218
- turnId: `uat:${params.sliceId}:attempt-${attempt}`,
863
+ traceId: `uat:${run.params.milestoneId}:${run.params.sliceId}`,
864
+ turnId: run.runId,
1219
865
  gateId: "UAT",
1220
866
  gateType: "uat",
1221
867
  unitType: "run-uat",
1222
- unitId: `run-uat:${params.milestoneId}/${params.sliceId}`,
1223
- milestoneId: params.milestoneId,
1224
- sliceId: params.sliceId,
1225
- outcome: params.verdict === "PASS" ? "pass" : "fail",
1226
- failureClass: params.verdict === "PASS" ? "none" : "verification",
1227
- rationale,
1228
- findings: assessment,
1229
- attempt,
1230
- maxAttempts: attempt,
1231
- retryable: params.verdict !== "PASS",
1232
- evaluatedAt,
868
+ unitId: `run-uat:${run.params.milestoneId}/${run.params.sliceId}`,
869
+ milestoneId: run.params.milestoneId,
870
+ sliceId: run.params.sliceId,
871
+ outcome: run.gateOutcome,
872
+ failureClass: run.params.verdict === "PASS" ? "none" : "verification",
873
+ rationale: run.rationale,
874
+ findings: run.assessment,
875
+ attempt: run.attempt,
876
+ maxAttempts: run.attempt,
877
+ retryable: run.params.verdict !== "PASS",
878
+ evaluatedAt: run.evaluatedAt,
1233
879
  });
1234
880
  invalidateStateCache();
881
+ const savedText = `UAT result saved for ${run.params.milestoneId}/${run.params.sliceId}: ${run.params.verdict}`;
1235
882
  return {
1236
- content: [{ type: "text", text: `UAT result saved for ${params.milestoneId}/${params.sliceId}: ${params.verdict}` }],
883
+ content: [{
884
+ type: "text",
885
+ text: run.manualGuidance ? `${savedText}\n\nManual validation needed:\n${run.manualGuidance}` : savedText,
886
+ }],
1237
887
  details: {
1238
888
  operation: "save_uat_result",
1239
- milestoneId: params.milestoneId,
1240
- sliceId: params.sliceId,
1241
- verdict: params.verdict,
1242
- gateVerdict,
1243
- attempt,
889
+ milestoneId: run.params.milestoneId,
890
+ sliceId: run.params.sliceId,
891
+ verdict: run.params.verdict,
892
+ gateVerdict: run.gateVerdict,
893
+ attempt: run.attempt,
1244
894
  attemptPath,
1245
- recommendedNextUnit: params.verdict === "PASS" ? null : "reactive-execute",
895
+ runId: run.runId,
896
+ worktreeRoot: run.worktreeRoot,
897
+ browserToolsPresented: run.browserToolsPresented,
898
+ recommendedNextUnit: run.params.verdict === "PASS" ? null : "reactive-execute",
899
+ ...(run.hasHuman
900
+ ? { manualValidationPath: run.worktreeRoot }
901
+ : {}),
1246
902
  },
1247
903
  };
1248
904
  }
@@ -0,0 +1,130 @@
1
+ // Project/App: gsd-pi
2
+ // File Purpose: Central UAT mode policy for dispatch, tool presentation, and result validation.
3
+ import { extractUatType } from "./files.js";
4
+ import { hasBrowserRequiredText } from "./browser-evidence.js";
5
+ export const UAT_TYPES = [
6
+ "artifact-driven",
7
+ "browser-executable",
8
+ "runtime-executable",
9
+ "live-runtime",
10
+ "mixed",
11
+ "human-experience",
12
+ ];
13
+ export const UAT_MODE_POLICIES = {
14
+ "artifact-driven": {
15
+ browserTools: false,
16
+ partialEligible: false,
17
+ passWithHumanFollowUp: false,
18
+ requiredAnyModes: [],
19
+ },
20
+ "browser-executable": {
21
+ browserTools: true,
22
+ partialEligible: false,
23
+ passWithHumanFollowUp: false,
24
+ requiredAnyModes: ["browser"],
25
+ },
26
+ "runtime-executable": {
27
+ browserTools: false,
28
+ partialEligible: false,
29
+ passWithHumanFollowUp: false,
30
+ requiredAnyModes: ["runtime"],
31
+ },
32
+ "live-runtime": {
33
+ browserTools: true,
34
+ partialEligible: true,
35
+ passWithHumanFollowUp: true,
36
+ requiredAnyModes: ["runtime", "browser"],
37
+ },
38
+ mixed: {
39
+ browserTools: true,
40
+ partialEligible: true,
41
+ passWithHumanFollowUp: true,
42
+ requiredAnyModes: [],
43
+ },
44
+ "human-experience": {
45
+ browserTools: true,
46
+ partialEligible: true,
47
+ passWithHumanFollowUp: true,
48
+ requiredAnyModes: [],
49
+ },
50
+ };
51
+ export function isUatType(value) {
52
+ return typeof value === "string" && UAT_TYPES.includes(value);
53
+ }
54
+ export function getDeclaredUatType(content) {
55
+ return extractUatType(content) ?? "artifact-driven";
56
+ }
57
+ export function classifyUatContent(content) {
58
+ const declaredType = getDeclaredUatType(content);
59
+ const browserRequired = hasBrowserRequiredText(content);
60
+ const effectiveType = declaredType === "artifact-driven" && browserRequired
61
+ ? "browser-executable"
62
+ : declaredType;
63
+ return {
64
+ declaredType,
65
+ effectiveType,
66
+ browserRequired,
67
+ shouldDispatchByDefault: effectiveType !== "artifact-driven" || browserRequired,
68
+ };
69
+ }
70
+ export function shouldEscalateArtifactUatToBrowser(content) {
71
+ const policy = classifyUatContent(content);
72
+ return policy.declaredType === "artifact-driven" && policy.browserRequired;
73
+ }
74
+ export function resolveEffectiveUatType(content) {
75
+ return classifyUatContent(content).effectiveType;
76
+ }
77
+ export function shouldDispatchUatForContent(content, prefs) {
78
+ return !!prefs?.uat_dispatch || classifyUatContent(content).shouldDispatchByDefault;
79
+ }
80
+ export function uatTypeIncludesBrowser(uatType) {
81
+ return isUatType(uatType) && UAT_MODE_POLICIES[uatType].browserTools;
82
+ }
83
+ function canonicalPresentedToolName(toolName) {
84
+ if (!toolName.startsWith("mcp__"))
85
+ return toolName;
86
+ const toolSeparator = toolName.indexOf("__", "mcp__".length);
87
+ return toolSeparator >= 0 ? toolName.slice(toolSeparator + 2) : toolName;
88
+ }
89
+ export function isUatBrowserToolName(toolName) {
90
+ return canonicalPresentedToolName(toolName).startsWith("browser_");
91
+ }
92
+ export function hasUatBrowserToolSurface(activeTools) {
93
+ return Array.isArray(activeTools) && activeTools.some(isUatBrowserToolName);
94
+ }
95
+ export function getUatBrowserToolSupportError(options) {
96
+ if (!uatTypeIncludesBrowser(options.uatType))
97
+ return null;
98
+ if (!Array.isArray(options.activeTools))
99
+ return null;
100
+ if (hasUatBrowserToolSurface(options.activeTools))
101
+ return null;
102
+ return `Cannot dispatch browser-backed run-uat for ${options.milestoneId}/${options.sliceId}: UAT mode "${options.uatType}" requires browser tools, but the active tool surface has none. Enable browser tools or change the UAT to a runtime-executable Playwright command, then rerun /gsd auto.`;
103
+ }
104
+ export function isPartialEligibleUatType(uatType) {
105
+ return !!uatType && UAT_MODE_POLICIES[uatType].partialEligible;
106
+ }
107
+ function modeList(modes) {
108
+ if (modes.length === 1)
109
+ return modes[0];
110
+ return modes.slice(0, -1).join(", ") + " or " + modes[modes.length - 1];
111
+ }
112
+ export function validateUatModePolicy(params) {
113
+ const policy = UAT_MODE_POLICIES[params.uatType];
114
+ const modes = new Set(params.checks.map((check) => check.mode));
115
+ const hasHuman = params.checks.some((check) => check.result === "NEEDS-HUMAN");
116
+ if (params.uatType === "artifact-driven" && hasHuman && params.verdict === "PASS") {
117
+ return "artifact-driven UAT cannot PASS with human-only checks";
118
+ }
119
+ if (hasHuman &&
120
+ params.verdict === "PASS" &&
121
+ !policy.passWithHumanFollowUp &&
122
+ !params.checks.every((check) => check.result !== "NEEDS-HUMAN" || check.nonAutomatable === true)) {
123
+ return "NEEDS-HUMAN checks can only coexist with PASS for human-experience, mixed, live-runtime, or explicitly non-automatable checks";
124
+ }
125
+ if (policy.requiredAnyModes.length > 0 &&
126
+ !policy.requiredAnyModes.some((mode) => modes.has(mode))) {
127
+ return `${params.uatType} UAT requires ${modeList(policy.requiredAnyModes)} evidence`;
128
+ }
129
+ return null;
130
+ }