@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
@@ -10,6 +10,7 @@ import {
10
10
  getMilestone,
11
11
  getSliceStatusSummary,
12
12
  getSliceTaskCounts,
13
+ insertAssessment,
13
14
  insertGateRun,
14
15
  readTransaction,
15
16
  saveGateResult,
@@ -17,10 +18,10 @@ import {
17
18
  } from "../gsd-db.js";
18
19
  import { GATE_REGISTRY } from "../gate-registry.js";
19
20
  import { generateRequirementsMd, saveArtifactToDb } from "../db-writer.js";
20
- import { clearPathCache, resolveGsdPathContract, resolveMilestoneFile, resolveSliceFile } from "../paths.js";
21
+ import { clearPathCache, relSliceFile, resolveGsdPathContract, resolveMilestoneFile, resolveSliceFile } from "../paths.js";
21
22
  import { saveFile, clearParseCache } from "../files.js";
22
- import { existsSync, readdirSync, readFileSync, unlinkSync } from "node:fs";
23
- import { isAbsolute, join, resolve } from "node:path";
23
+ import { unlinkSync } from "node:fs";
24
+ import { join } from "node:path";
24
25
  import type { CompleteMilestoneParams } from "./complete-milestone.js";
25
26
  import { handleCompleteMilestone } from "./complete-milestone.js";
26
27
  import { handleCompleteTask } from "./complete-task.js";
@@ -47,14 +48,17 @@ import { invalidateStateCache } from "../state.js";
47
48
  import { loadEffectiveGSDPreferences } from "../preferences.js";
48
49
  import { parseProject } from "../schemas/parsers.js";
49
50
  import { getAutoRuntimeSnapshot } from "../auto-runtime-state.js";
51
+ import { renderPlanFromDb } from "../markdown-renderer.js";
50
52
  import {
51
- buildRunUatResultPresentation,
52
- canonicalWorkflowToolName,
53
- parseMcpToolName,
54
- RUN_UAT_FORBIDDEN_TOOL_NAMES,
55
- RUN_UAT_TOOL_PRESENTATION_PLAN_ID,
56
- RUN_UAT_WORKFLOW_TOOL_NAMES,
57
- } from "../tool-presentation-plan.js";
53
+ prepareUatRun,
54
+ saveUatAttemptArtifact,
55
+ type UatResultSaveParams,
56
+ } from "../uat-run.js";
57
+ export type {
58
+ UatCheckResultInput,
59
+ UatEvidenceRef,
60
+ UatPresentationInput,
61
+ } from "../uat-run.js";
58
62
 
59
63
  export const SUPPORTED_SUMMARY_ARTIFACT_TYPES = [
60
64
  "SUMMARY",
@@ -436,58 +440,7 @@ export interface SaveGateResultParams {
436
440
  findings?: string;
437
441
  }
438
442
 
439
- export type UatType =
440
- | "artifact-driven"
441
- | "browser-executable"
442
- | "runtime-executable"
443
- | "live-runtime"
444
- | "mixed"
445
- | "human-experience";
446
-
447
- export type UatVerdict = "PASS" | "FAIL" | "PARTIAL";
448
- export type UatCheckResult = "PASS" | "FAIL" | "NEEDS-HUMAN";
449
-
450
- export interface UatEvidenceRef {
451
- kind: "gsd_uat_exec" | "gsd_exec" | "screenshot" | "log" | "url" | "browser";
452
- ref: string;
453
- note?: string;
454
- unitType?: string;
455
- tool?: string;
456
- executionId?: string;
457
- }
458
-
459
- export interface UatCheckResultInput {
460
- id: string;
461
- description: string;
462
- mode: "artifact" | "runtime" | "browser" | "human-follow-up";
463
- result: UatCheckResult;
464
- evidence?: UatEvidenceRef[];
465
- notes?: string;
466
- nonAutomatable?: boolean;
467
- }
468
-
469
- export interface UatPresentationInput {
470
- surface: "provider-tools" | "claude-code-sdk" | "mcp" | "hybrid";
471
- model?: { provider?: string; api?: string; id?: string };
472
- presentedTools: string[];
473
- blockedTools: Array<{ name: string; reason: string }>;
474
- aliases?: Array<{ requested: string; canonical: string }>;
475
- fallbackToolsUsed?: string[];
476
- toolPresentationPlanId?: string;
477
- notes?: string;
478
- }
479
-
480
- export interface UatResultSaveParams {
481
- milestoneId: string;
482
- sliceId: string;
483
- uatType: UatType;
484
- verdict: UatVerdict;
485
- checks: UatCheckResultInput[];
486
- presentation: UatPresentationInput;
487
- notes?: string;
488
- attempt?: number | string | "auto";
489
- previousAttemptId?: string;
490
- }
443
+ export type { UatResultSaveParams };
491
444
 
492
445
  export async function executeTaskComplete(
493
446
  params: TaskCompleteParams,
@@ -993,6 +946,7 @@ export async function executeSaveGateResult(
993
946
  rationale: params.rationale,
994
947
  findings: params.findings ?? "",
995
948
  });
949
+ await renderPlanFromDb(basePath, params.milestoneId, params.sliceId);
996
950
  invalidateStateCache();
997
951
  return {
998
952
  content: [{ type: "text", text: `Gate ${params.gateId} result saved: verdict=${params.verdict}` }],
@@ -1017,371 +971,6 @@ function errorResult(operation: string, message: string, error: string): ToolExe
1017
971
  };
1018
972
  }
1019
973
 
1020
- function isNonEmptyString(value: unknown): value is string {
1021
- return typeof value === "string" && value.trim().length > 0;
1022
- }
1023
-
1024
- function mergeBlockedTools(
1025
- current: UatPresentationInput["blockedTools"] | undefined,
1026
- canonical: UatPresentationInput["blockedTools"],
1027
- ): UatPresentationInput["blockedTools"] {
1028
- const merged = new Map<string, { name: string; reason: string }>();
1029
- for (const entry of [...(current ?? []), ...canonical]) {
1030
- merged.set(canonicalWorkflowToolName(parseMcpToolName(entry.name)?.tool ?? entry.name), entry);
1031
- }
1032
- return [...merged.values()];
1033
- }
1034
-
1035
- function mergePresentedTools(current: readonly string[] | undefined, canonical: readonly string[]): string[] {
1036
- return [...new Set([...(current ?? []), ...canonical])];
1037
- }
1038
-
1039
- function normalizeUatVerdict(params: UatResultSaveParams): UatResultSaveParams {
1040
- const raw = params as Partial<UatResultSaveParams> & Record<string, unknown>;
1041
- if (typeof raw.verdict === "string") {
1042
- return { ...params, verdict: raw.verdict.toUpperCase() as UatVerdict };
1043
- }
1044
- return params;
1045
- }
1046
-
1047
- function supplyDefaultPresentation(params: UatResultSaveParams): UatResultSaveParams {
1048
- const raw = params as Partial<UatResultSaveParams> & Record<string, unknown>;
1049
- if (!raw.presentation) {
1050
- return { ...params, presentation: buildRunUatResultPresentation() };
1051
- }
1052
- return params;
1053
- }
1054
-
1055
- function mergeCanonicalPresentation(params: UatResultSaveParams): UatResultSaveParams {
1056
- const canonicalPresentation = buildRunUatResultPresentation();
1057
- const providedPresentation = params.presentation as Partial<UatPresentationInput>;
1058
- return {
1059
- ...params,
1060
- presentation: {
1061
- ...providedPresentation,
1062
- surface: providedPresentation.surface ?? canonicalPresentation.surface,
1063
- presentedTools: mergePresentedTools(providedPresentation.presentedTools, canonicalPresentation.presentedTools),
1064
- blockedTools: mergeBlockedTools(providedPresentation.blockedTools, canonicalPresentation.blockedTools),
1065
- toolPresentationPlanId: RUN_UAT_TOOL_PRESENTATION_PLAN_ID,
1066
- } as UatPresentationInput,
1067
- };
1068
- }
1069
-
1070
- const VALID_UAT_TYPES: readonly UatType[] = [
1071
- "artifact-driven",
1072
- "browser-executable",
1073
- "runtime-executable",
1074
- "live-runtime",
1075
- "mixed",
1076
- "human-experience",
1077
- ];
1078
-
1079
- function ensureUatRequiredFields(params: UatResultSaveParams): string | null {
1080
- if (!isNonEmptyString(params.milestoneId)) return "milestoneId is required";
1081
- if (!isNonEmptyString(params.sliceId)) return "sliceId is required";
1082
- if (!isNonEmptyString(params.uatType)) return "uatType is required";
1083
- if (!(VALID_UAT_TYPES as readonly string[]).includes(params.uatType)) {
1084
- return `uatType must be one of: ${VALID_UAT_TYPES.join(", ")}`;
1085
- }
1086
- if (!["PASS", "FAIL", "PARTIAL"].includes(params.verdict)) return "verdict must be PASS, FAIL, or PARTIAL";
1087
- if (!Array.isArray(params.checks) || params.checks.length === 0) return "checks must contain at least one UAT check";
1088
- if (!params.presentation || !Array.isArray(params.presentation.presentedTools)) return "presentation.presentedTools is required";
1089
- if (!Array.isArray(params.presentation.blockedTools)) return "presentation.blockedTools is required";
1090
- return null;
1091
- }
1092
-
1093
- function approvedEvidenceRoots(basePath: string): string[] {
1094
- const contract = resolveGsdPathContract(basePath);
1095
- return [contract.worktreeGsd, contract.projectGsd].filter((root): root is string => typeof root === "string");
1096
- }
1097
-
1098
- function approvedBrowserArtifactRoots(basePath: string): string[] {
1099
- const contract = resolveGsdPathContract(basePath);
1100
- const roots = [contract.workRoot, contract.projectRoot].map((root) => join(root, ".artifacts", "browser"));
1101
- return [...new Set(roots)];
1102
- }
1103
-
1104
- function pathStartsWithin(parent: string, target: string): boolean {
1105
- const normalizedParent = parent.replace(/\\/g, "/").replace(/\/+$/, "");
1106
- const normalizedTarget = target.replace(/\\/g, "/").replace(/\/+$/, "");
1107
- return normalizedTarget === normalizedParent || normalizedTarget.startsWith(`${normalizedParent}/`);
1108
- }
1109
-
1110
- function pushUnique(paths: string[], candidate: string): void {
1111
- if (!paths.includes(candidate)) paths.push(candidate);
1112
- }
1113
-
1114
- function execMetaPathCandidates(basePath: string, ref: string): string[] {
1115
- const trimmed = ref.trim();
1116
- const candidates: string[] = [];
1117
- const execDirs = approvedEvidenceRoots(basePath).map((root) => join(root, "exec"));
1118
- const normalizedRef = trimmed.replace(/\\/g, "/");
1119
- const pathLike = normalizedRef.endsWith(".meta.json") || normalizedRef.includes("/.gsd/exec/");
1120
-
1121
- if (pathLike) {
1122
- const rawPath = isAbsolute(trimmed) ? resolve(trimmed) : resolve(basePath, trimmed);
1123
- pushUnique(candidates, rawPath);
1124
-
1125
- const relativeExecMarker = ".gsd/exec/";
1126
- const markerIndex = normalizedRef.indexOf(relativeExecMarker);
1127
- if (markerIndex >= 0) {
1128
- const execRelative = normalizedRef.slice(markerIndex + relativeExecMarker.length);
1129
- for (const execDir of execDirs) {
1130
- pushUnique(candidates, join(execDir, execRelative));
1131
- }
1132
- }
1133
-
1134
- return candidates.filter((candidate) =>
1135
- execDirs.some((execDir) => pathStartsWithin(execDir, candidate))
1136
- );
1137
- }
1138
-
1139
- for (const execDir of execDirs) {
1140
- pushUnique(candidates, join(execDir, `${trimmed}.meta.json`));
1141
- }
1142
- return candidates;
1143
- }
1144
-
1145
- function resolveExecMetaPath(basePath: string, ref: string): string | null {
1146
- for (const candidate of execMetaPathCandidates(basePath, ref)) {
1147
- if (existsSync(candidate)) return candidate;
1148
- }
1149
- return null;
1150
- }
1151
-
1152
- function evidencePathIsApproved(basePath: string, ref: string): boolean {
1153
- const normalizedRef = ref.replace(/\\/g, "/");
1154
- if (normalizedRef.startsWith(".gsd/exec/") || normalizedRef.startsWith(".gsd/uat/")) return true;
1155
- if (normalizedRef.startsWith(".artifacts/browser/")) {
1156
- const resolvedRef = resolve(basePath, ref);
1157
- return approvedBrowserArtifactRoots(basePath).some((root) => pathStartsWithin(root, resolvedRef));
1158
- }
1159
- const gsdEvidenceApproved = approvedEvidenceRoots(basePath).some((root) => {
1160
- return pathStartsWithin(join(root, "exec"), ref) || pathStartsWithin(join(root, "uat"), ref);
1161
- });
1162
- if (gsdEvidenceApproved) return true;
1163
- return approvedBrowserArtifactRoots(basePath).some((root) => pathStartsWithin(root, ref));
1164
- }
1165
-
1166
- function validateEvidenceRef(basePath: string, evidence: UatEvidenceRef): string | null {
1167
- if (!isNonEmptyString(evidence.ref)) return "evidence.ref is required";
1168
- if (evidence.kind === "gsd_uat_exec" || evidence.kind === "gsd_exec") {
1169
- const path = resolveExecMetaPath(basePath, evidence.ref.trim());
1170
- if (!path) return `missing gsd_exec metadata for evidence id "${evidence.ref}"`;
1171
- if (evidence.kind === "gsd_uat_exec") {
1172
- try {
1173
- const meta = JSON.parse(readFileSync(path, "utf-8")) as { metadata?: { kind?: unknown } };
1174
- if (meta.metadata?.kind !== "uat_exec") return `evidence id "${evidence.ref}" is not typed as uat_exec`;
1175
- } catch {
1176
- return `invalid gsd_exec metadata JSON for evidence id "${evidence.ref}"`;
1177
- }
1178
- }
1179
- return null;
1180
- }
1181
- if (evidence.kind === "url") {
1182
- try {
1183
- const parsed = new URL(evidence.ref);
1184
- return parsed.protocol === "http:" || parsed.protocol === "https:"
1185
- ? null
1186
- : `invalid URL evidence ref "${evidence.ref}"`;
1187
- } catch {
1188
- return `invalid URL evidence ref "${evidence.ref}"`;
1189
- }
1190
- }
1191
- return evidencePathIsApproved(basePath, evidence.ref)
1192
- ? null
1193
- : `evidence ref "${evidence.ref}" is outside approved evidence locations`;
1194
- }
1195
-
1196
- function validateUatChecks(basePath: string, params: UatResultSaveParams): string | null {
1197
- for (const check of params.checks) {
1198
- if (!isNonEmptyString(check.id)) return "every check must have a non-empty id";
1199
- if (!isNonEmptyString(check.description)) return `check ${check.id} must have a description`;
1200
- if (!["artifact", "runtime", "browser", "human-follow-up"].includes(check.mode)) {
1201
- return `check ${check.id} has invalid mode "${check.mode}"`;
1202
- }
1203
- if (!["PASS", "FAIL", "NEEDS-HUMAN"].includes(check.result)) {
1204
- return `check ${check.id} has invalid result "${check.result}"`;
1205
- }
1206
- if (check.result === "PASS" || check.result === "FAIL") {
1207
- if (!Array.isArray(check.evidence) || check.evidence.length === 0) {
1208
- return `check ${check.id} is ${check.result} but has no objective evidence`;
1209
- }
1210
- for (const evidence of check.evidence) {
1211
- const error = validateEvidenceRef(basePath, evidence);
1212
- if (error) return `check ${check.id}: ${error}`;
1213
- }
1214
- } else if (!isNonEmptyString(check.notes)) {
1215
- return `check ${check.id} is NEEDS-HUMAN but has no manual instruction or reason`;
1216
- }
1217
- }
1218
- return null;
1219
- }
1220
-
1221
- function validateFreshUatOwnedEvidence(params: UatResultSaveParams): string | null {
1222
- const hasFreshUatEvidence = params.checks.some((check) =>
1223
- (check.evidence ?? []).some((evidence) => evidence.kind === "gsd_uat_exec")
1224
- );
1225
- return hasFreshUatEvidence
1226
- ? null
1227
- : "UAT Assessment requires at least one fresh gsd_uat_exec evidence reference from run-uat";
1228
- }
1229
-
1230
- function validateUatMode(params: UatResultSaveParams): string | null {
1231
- const modes = new Set(params.checks.map((check) => check.mode));
1232
- const hasHuman = params.checks.some((check) => check.result === "NEEDS-HUMAN");
1233
- if (params.uatType === "artifact-driven" && hasHuman && params.verdict === "PASS") {
1234
- return "artifact-driven UAT cannot PASS with human-only checks";
1235
- }
1236
- if (
1237
- hasHuman &&
1238
- params.verdict === "PASS" &&
1239
- !["human-experience", "mixed", "live-runtime"].includes(params.uatType) &&
1240
- !params.checks.every((check) => check.result !== "NEEDS-HUMAN" || check.nonAutomatable === true)
1241
- ) {
1242
- return "NEEDS-HUMAN checks can only coexist with PASS for human-experience, mixed, live-runtime, or explicitly non-automatable checks";
1243
- }
1244
- if (params.uatType === "runtime-executable" && !modes.has("runtime")) {
1245
- return "runtime-executable UAT requires at least one runtime check";
1246
- }
1247
- if (params.uatType === "browser-executable" && !modes.has("browser")) {
1248
- return "browser-executable UAT requires at least one browser check";
1249
- }
1250
- if (params.uatType === "live-runtime" && !modes.has("runtime") && !modes.has("browser")) {
1251
- return "live-runtime UAT requires runtime or browser evidence";
1252
- }
1253
- return null;
1254
- }
1255
-
1256
- function quoteToolNames(toolNames: readonly string[]): string {
1257
- return toolNames.map((toolName) => `"${toolName}"`).join(", ");
1258
- }
1259
-
1260
- function validateCanonicalPresentation(params: UatResultSaveParams): string | null {
1261
- const aliasHints: Record<string, string> = {
1262
- gsd_save_summary: "gsd_summary_save",
1263
- gsd_complete_task: "gsd_task_complete",
1264
- gsd_complete_slice: "gsd_slice_complete",
1265
- gsd_milestone_complete: "gsd_complete_milestone",
1266
- };
1267
- const errors: string[] = [];
1268
- for (const toolName of params.presentation.presentedTools) {
1269
- const baseName = parseMcpToolName(toolName)?.tool ?? toolName;
1270
- const canonical = aliasHints[baseName];
1271
- if (canonical) errors.push(`presentation tool "${toolName}" uses an alias; use canonical "${canonical}"`);
1272
- }
1273
-
1274
- const presentedCanonical = new Set(
1275
- params.presentation.presentedTools.map((toolName) =>
1276
- canonicalWorkflowToolName(parseMcpToolName(toolName)?.tool ?? toolName)
1277
- ),
1278
- );
1279
- const missingRequiredTools = RUN_UAT_WORKFLOW_TOOL_NAMES.filter(
1280
- (requiredTool) => !presentedCanonical.has(requiredTool),
1281
- );
1282
- if (missingRequiredTools.length === 1) {
1283
- errors.push(`presentation is missing required UAT tool "${missingRequiredTools[0]}"`);
1284
- } else if (missingRequiredTools.length > 1) {
1285
- errors.push(`presentation is missing required UAT tools ${quoteToolNames(missingRequiredTools)}`);
1286
- }
1287
-
1288
- const forbiddenCanonical = new Set(
1289
- RUN_UAT_FORBIDDEN_TOOL_NAMES
1290
- .filter((toolName) => !toolName.includes("*"))
1291
- .map((toolName) => canonicalWorkflowToolName(parseMcpToolName(toolName)?.tool ?? toolName)),
1292
- );
1293
- const forbiddenPresentedTools: string[] = [];
1294
- for (const toolName of params.presentation.presentedTools) {
1295
- const canonical = canonicalWorkflowToolName(parseMcpToolName(toolName)?.tool ?? toolName);
1296
- if (toolName === "mcp__gsd-workflow__*" || forbiddenCanonical.has(canonical)) {
1297
- forbiddenPresentedTools.push(toolName);
1298
- }
1299
- }
1300
- if (forbiddenPresentedTools.length === 1) {
1301
- errors.push(`presentation includes forbidden run-uat tool "${forbiddenPresentedTools[0]}"`);
1302
- } else if (forbiddenPresentedTools.length > 1) {
1303
- errors.push(`presentation includes forbidden run-uat tools ${quoteToolNames(forbiddenPresentedTools)}`);
1304
- }
1305
-
1306
- const blockedCanonical = new Set(
1307
- params.presentation.blockedTools.map((entry) =>
1308
- canonicalWorkflowToolName(parseMcpToolName(entry.name)?.tool ?? entry.name)
1309
- ),
1310
- );
1311
- const missingBlockedTools = ["gsd_exec", "gsd_summary_save", "gsd_save_gate_result"].filter(
1312
- (blockedTool) => !blockedCanonical.has(blockedTool),
1313
- );
1314
- if (missingBlockedTools.length === 1) {
1315
- errors.push(`presentation must record "${missingBlockedTools[0]}" as blocked during run-uat`);
1316
- } else if (missingBlockedTools.length > 1) {
1317
- errors.push(`presentation must record ${quoteToolNames(missingBlockedTools)} as blocked during run-uat`);
1318
- }
1319
- return errors.length > 0 ? errors.join("; ") : null;
1320
- }
1321
-
1322
- function nextUatAttempt(basePath: string, milestoneId: string, sliceId: string): number {
1323
- const contract = resolveGsdPathContract(basePath);
1324
- const dir = join(contract.projectGsd, "uat", milestoneId, sliceId);
1325
- if (!existsSync(dir)) return 1;
1326
- let max = 0;
1327
- for (const entry of readdirSync(dir)) {
1328
- const match = /^attempt-(\d+)\.json$/.exec(entry);
1329
- if (match) max = Math.max(max, Number(match[1]));
1330
- }
1331
- return max + 1;
1332
- }
1333
-
1334
- function escapeMarkdownTableCell(value: unknown): string {
1335
- return String(value ?? "")
1336
- .replace(/[\\|]/g, (char) => `\\${char}`)
1337
- .replace(/\r?\n/g, "<br>");
1338
- }
1339
-
1340
- function renderUatAssessment(params: UatResultSaveParams, attempt: number, gateVerdict: "pass" | "flag"): string {
1341
- const lines = [
1342
- "---",
1343
- `sliceId: ${params.sliceId}`,
1344
- `uatType: ${params.uatType}`,
1345
- `verdict: ${params.verdict}`,
1346
- `attempt: ${attempt}`,
1347
- `date: ${new Date().toISOString()}`,
1348
- "---",
1349
- "",
1350
- `# UAT Result - ${params.sliceId}`,
1351
- "",
1352
- "## Checks",
1353
- "",
1354
- "| Check | Mode | Result | Evidence | Notes |",
1355
- "|-------|------|--------|----------|-------|",
1356
- ...params.checks.map((check) => {
1357
- const evidence = (check.evidence ?? []).map((entry) => `${entry.kind}:${entry.ref}`).join("<br>") || "-";
1358
- return `| ${escapeMarkdownTableCell(check.description)} | ${escapeMarkdownTableCell(check.mode)} | ${escapeMarkdownTableCell(check.result)} | ${escapeMarkdownTableCell(evidence)} | ${escapeMarkdownTableCell(check.notes)} |`;
1359
- }),
1360
- "",
1361
- "## Overall Verdict",
1362
- "",
1363
- `${params.verdict} - ${params.notes ?? "UAT result saved."}`,
1364
- "",
1365
- "## Tool Presentation",
1366
- "",
1367
- "```json",
1368
- JSON.stringify(params.presentation, null, 2),
1369
- "```",
1370
- "",
1371
- "## Gate",
1372
- "",
1373
- `Aggregate UAT gate saved as ${gateVerdict}.`,
1374
- ];
1375
- return `${lines.join("\n")}\n`;
1376
- }
1377
-
1378
- async function saveUatAttemptArtifact(basePath: string, params: UatResultSaveParams, attempt: number): Promise<string> {
1379
- const contract = resolveGsdPathContract(basePath);
1380
- const relativePath = `uat/${params.milestoneId}/${params.sliceId}/attempt-${attempt}.json`;
1381
- await saveFile(join(contract.projectGsd, relativePath), `${JSON.stringify({ ...params, attempt }, null, 2)}\n`);
1382
- return relativePath;
1383
- }
1384
-
1385
974
  export async function executeUatResultSave(
1386
975
  params: UatResultSaveParams,
1387
976
  basePath: string = process.cwd(),
@@ -1389,96 +978,89 @@ export async function executeUatResultSave(
1389
978
  const unitGuard = blockIfWrongAutoUnit("run-uat", "save_uat_result");
1390
979
  if (unitGuard) return unitGuard;
1391
980
 
1392
- // Phase 1: normalize verdict and supply the canonical presentation when none was provided.
1393
- params = normalizeUatVerdict(params);
1394
- params = supplyDefaultPresentation(params);
1395
-
1396
981
  const dbAvailable = await ensureDbOpen(basePath);
1397
982
  if (!dbAvailable) return errorResult("save_uat_result", "GSD database is not available.", "db_unavailable");
1398
983
 
1399
- // Phase 2: validate the submitted presentation before the canonical merge so that
1400
- // presentations missing required workflow tools are rejected rather than silently patched.
1401
- const requiredError = ensureUatRequiredFields(params);
1402
- if (requiredError) return errorResult("save_uat_result", requiredError, "invalid_params");
1403
- const presentationError = validateCanonicalPresentation(params);
1404
- if (presentationError) return errorResult("save_uat_result", presentationError, "alias_tool_name");
1405
-
1406
- // Phase 3: merge in the canonical plan ID and read-only audit tools so the persisted
1407
- // artifact always carries the full audit surface even when the provider omitted them.
1408
- params = mergeCanonicalPresentation(params);
1409
- const checkError = validateUatChecks(basePath, params);
1410
- if (checkError) return errorResult("save_uat_result", checkError, "invalid_evidence");
1411
- const freshEvidenceError = validateFreshUatOwnedEvidence(params);
1412
- if (freshEvidenceError) return errorResult("save_uat_result", freshEvidenceError, "missing_fresh_uat_evidence");
1413
- const modeError = validateUatMode(params);
1414
- if (modeError) return errorResult("save_uat_result", modeError, "uat_mode_mismatch");
984
+ const prepared = prepareUatRun(basePath, params);
985
+ if (!prepared.ok) {
986
+ return errorResult("save_uat_result", prepared.error.message, prepared.error.code);
987
+ }
988
+ const { run } = prepared;
1415
989
 
1416
990
  try {
1417
- const attempt = params.attempt === "auto" || params.attempt === undefined
1418
- ? nextUatAttempt(basePath, params.milestoneId, params.sliceId)
1419
- : typeof params.attempt === "string"
1420
- ? Number.parseInt(params.attempt, 10)
1421
- : params.attempt;
1422
- if (!Number.isInteger(attempt) || attempt < 1) {
1423
- return errorResult("save_uat_result", "attempt must be a positive integer or auto", "invalid_attempt");
1424
- }
1425
- const gateVerdict = params.verdict === "PASS" ? "pass" : "flag";
1426
- const rationale = params.notes ?? `UAT ${params.verdict} for ${params.sliceId}.`;
1427
- const assessment = renderUatAssessment(params, attempt, gateVerdict);
1428
991
  const summary = await executeSummarySave(
1429
992
  {
1430
- milestone_id: params.milestoneId,
1431
- slice_id: params.sliceId,
993
+ milestone_id: run.params.milestoneId,
994
+ slice_id: run.params.sliceId,
1432
995
  artifact_type: "ASSESSMENT",
1433
- content: assessment,
996
+ content: run.assessment,
1434
997
  },
1435
998
  basePath,
1436
999
  );
1437
1000
  if (summary.isError) return summary;
1438
- const attemptPath = await saveUatAttemptArtifact(basePath, params, attempt);
1439
- const evaluatedAt = new Date().toISOString();
1001
+ const assessmentPath = relSliceFile(basePath, run.params.milestoneId, run.params.sliceId, "ASSESSMENT");
1002
+ insertAssessment({
1003
+ path: assessmentPath,
1004
+ milestoneId: run.params.milestoneId,
1005
+ sliceId: run.params.sliceId,
1006
+ taskId: null,
1007
+ status: run.params.verdict.toLowerCase(),
1008
+ scope: "run-uat",
1009
+ fullContent: run.assessment,
1010
+ });
1011
+ const attemptPath = await saveUatAttemptArtifact(basePath, run);
1440
1012
  upsertQualityGate({
1441
- milestoneId: params.milestoneId,
1442
- sliceId: params.sliceId,
1013
+ milestoneId: run.params.milestoneId,
1014
+ sliceId: run.params.sliceId,
1443
1015
  gateId: "UAT",
1444
1016
  scope: "slice",
1445
1017
  taskId: "",
1446
1018
  status: "complete",
1447
- verdict: gateVerdict,
1448
- rationale,
1449
- findings: assessment,
1450
- evaluatedAt,
1019
+ verdict: run.gateVerdict,
1020
+ rationale: run.rationale,
1021
+ findings: run.assessment,
1022
+ evaluatedAt: run.evaluatedAt,
1451
1023
  });
1452
1024
  insertGateRun({
1453
- traceId: `uat:${params.milestoneId}:${params.sliceId}`,
1454
- turnId: `uat:${params.sliceId}:attempt-${attempt}`,
1025
+ traceId: `uat:${run.params.milestoneId}:${run.params.sliceId}`,
1026
+ turnId: run.runId,
1455
1027
  gateId: "UAT",
1456
1028
  gateType: "uat",
1457
1029
  unitType: "run-uat",
1458
- unitId: `run-uat:${params.milestoneId}/${params.sliceId}`,
1459
- milestoneId: params.milestoneId,
1460
- sliceId: params.sliceId,
1461
- outcome: params.verdict === "PASS" ? "pass" : "fail",
1462
- failureClass: params.verdict === "PASS" ? "none" : "verification",
1463
- rationale,
1464
- findings: assessment,
1465
- attempt,
1466
- maxAttempts: attempt,
1467
- retryable: params.verdict !== "PASS",
1468
- evaluatedAt,
1030
+ unitId: `run-uat:${run.params.milestoneId}/${run.params.sliceId}`,
1031
+ milestoneId: run.params.milestoneId,
1032
+ sliceId: run.params.sliceId,
1033
+ outcome: run.gateOutcome,
1034
+ failureClass: run.params.verdict === "PASS" ? "none" : "verification",
1035
+ rationale: run.rationale,
1036
+ findings: run.assessment,
1037
+ attempt: run.attempt,
1038
+ maxAttempts: run.attempt,
1039
+ retryable: run.params.verdict !== "PASS",
1040
+ evaluatedAt: run.evaluatedAt,
1469
1041
  });
1470
1042
  invalidateStateCache();
1043
+ const savedText = `UAT result saved for ${run.params.milestoneId}/${run.params.sliceId}: ${run.params.verdict}`;
1471
1044
  return {
1472
- content: [{ type: "text", text: `UAT result saved for ${params.milestoneId}/${params.sliceId}: ${params.verdict}` }],
1045
+ content: [{
1046
+ type: "text",
1047
+ text: run.manualGuidance ? `${savedText}\n\nManual validation needed:\n${run.manualGuidance}` : savedText,
1048
+ }],
1473
1049
  details: {
1474
1050
  operation: "save_uat_result",
1475
- milestoneId: params.milestoneId,
1476
- sliceId: params.sliceId,
1477
- verdict: params.verdict,
1478
- gateVerdict,
1479
- attempt,
1051
+ milestoneId: run.params.milestoneId,
1052
+ sliceId: run.params.sliceId,
1053
+ verdict: run.params.verdict,
1054
+ gateVerdict: run.gateVerdict,
1055
+ attempt: run.attempt,
1480
1056
  attemptPath,
1481
- recommendedNextUnit: params.verdict === "PASS" ? null : "reactive-execute",
1057
+ runId: run.runId,
1058
+ worktreeRoot: run.worktreeRoot,
1059
+ browserToolsPresented: run.browserToolsPresented,
1060
+ recommendedNextUnit: run.params.verdict === "PASS" ? null : "reactive-execute",
1061
+ ...(run.hasHuman
1062
+ ? { manualValidationPath: run.worktreeRoot }
1063
+ : {}),
1482
1064
  },
1483
1065
  };
1484
1066
  } catch (err) {
@@ -35,6 +35,7 @@ export interface RoadmapSliceEntry {
35
35
  depends: string[]; // e.g. ["S01", "S02"]
36
36
  done: boolean;
37
37
  demo: string; // the "After this:" sentence
38
+ isSketch?: boolean; // ADR-011: true when the roadmap shows the `[sketch]` badge
38
39
  }
39
40
 
40
41
  export interface BoundaryMapEntry {