@opengsd/gsd-pi 1.1.1-dev.9f86580 → 1.1.1-dev.b2556262

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (261) hide show
  1. package/dist/headless-recover.js +56 -1
  2. package/dist/resources/.managed-resources-content-hash +1 -1
  3. package/dist/resources/extensions/browser-tools/index.js +39 -22
  4. package/dist/resources/extensions/browser-tools/state.js +12 -0
  5. package/dist/resources/extensions/browser-tools/tools/session.js +3 -2
  6. package/dist/resources/extensions/browser-tools/utils.js +3 -3
  7. package/dist/resources/extensions/gsd/auto/loop.js +4 -2
  8. package/dist/resources/extensions/gsd/auto/phases.js +43 -10
  9. package/dist/resources/extensions/gsd/auto/session.js +20 -1
  10. package/dist/resources/extensions/gsd/auto/workflow-kernel.js +1 -0
  11. package/dist/resources/extensions/gsd/auto-dispatch.js +72 -12
  12. package/dist/resources/extensions/gsd/auto-model-selection.js +128 -9
  13. package/dist/resources/extensions/gsd/auto-post-unit.js +19 -2
  14. package/dist/resources/extensions/gsd/auto-prompts.js +24 -19
  15. package/dist/resources/extensions/gsd/auto-recovery.js +4 -2
  16. package/dist/resources/extensions/gsd/auto-runtime-state.js +3 -0
  17. package/dist/resources/extensions/gsd/auto-start.js +1 -1
  18. package/dist/resources/extensions/gsd/auto.js +14 -11
  19. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +3 -3
  20. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +172 -65
  21. package/dist/resources/extensions/gsd/closeout-wizard.js +32 -9
  22. package/dist/resources/extensions/gsd/commands/handlers/ops.js +2 -9
  23. package/dist/resources/extensions/gsd/commands-maintenance.js +93 -15
  24. package/dist/resources/extensions/gsd/commands-prefs-wizard.js +2 -2
  25. package/dist/resources/extensions/gsd/db-writer.js +35 -0
  26. package/dist/resources/extensions/gsd/docs/preferences-reference.md +50 -1
  27. package/dist/resources/extensions/gsd/gsd-db.js +480 -172
  28. package/dist/resources/extensions/gsd/markdown-renderer.js +37 -53
  29. package/dist/resources/extensions/gsd/md-importer.js +38 -3
  30. package/dist/resources/extensions/gsd/migration-auto-check.js +126 -31
  31. package/dist/resources/extensions/gsd/parsers-legacy.js +23 -0
  32. package/dist/resources/extensions/gsd/planning-path-scope.js +22 -4
  33. package/dist/resources/extensions/gsd/pre-execution-checks.js +10 -2
  34. package/dist/resources/extensions/gsd/preferences-models.js +110 -43
  35. package/dist/resources/extensions/gsd/preferences-types.js +13 -0
  36. package/dist/resources/extensions/gsd/preferences-validation.js +68 -3
  37. package/dist/resources/extensions/gsd/preferences.js +4 -1
  38. package/dist/resources/extensions/gsd/prompts/gate-evaluate.md +1 -1
  39. package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  40. package/dist/resources/extensions/gsd/prompts/refine-slice.md +1 -1
  41. package/dist/resources/extensions/gsd/roadmap-slices.js +5 -1
  42. package/dist/resources/extensions/gsd/safety/content-validator.js +6 -4
  43. package/dist/resources/extensions/gsd/source-observations.js +306 -0
  44. package/dist/resources/extensions/gsd/state-reconciliation/drift/completion.js +15 -8
  45. package/dist/resources/extensions/gsd/state-reconciliation/drift/stale-render.js +33 -5
  46. package/dist/resources/extensions/gsd/state-reconciliation/drift/stale-worker.js +34 -13
  47. package/dist/resources/extensions/gsd/state-reconciliation/index.js +39 -14
  48. package/dist/resources/extensions/gsd/state-reconciliation/spawn-gate.js +4 -4
  49. package/dist/resources/extensions/gsd/state.js +7 -3
  50. package/dist/resources/extensions/gsd/tool-contract.js +14 -0
  51. package/dist/resources/extensions/gsd/tool-presentation-plan.js +1 -9
  52. package/dist/resources/extensions/gsd/tools/complete-slice.js +7 -6
  53. package/dist/resources/extensions/gsd/tools/plan-slice.js +42 -11
  54. package/dist/resources/extensions/gsd/tools/plan-task.js +7 -1
  55. package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +57 -429
  56. package/dist/resources/extensions/gsd/uat-policy.js +130 -0
  57. package/dist/resources/extensions/gsd/uat-run.js +414 -0
  58. package/dist/resources/extensions/gsd/unit-context-manifest.js +3 -4
  59. package/dist/resources/extensions/gsd/verdict-parser.js +3 -8
  60. package/dist/resources/extensions/gsd/workflow-manifest.js +132 -5
  61. package/dist/resources/extensions/gsd/workflow-projections.js +8 -0
  62. package/dist/resources/extensions/gsd/worktree-state-projection.js +18 -17
  63. package/dist/resources/extensions/subagent/agents.js +1 -0
  64. package/dist/resources/extensions/subagent/index.js +27 -12
  65. package/dist/resources/extensions/subagent/launch.js +7 -2
  66. package/dist/web/standalone/.next/BUILD_ID +1 -1
  67. package/dist/web/standalone/.next/app-path-routes-manifest.json +8 -8
  68. package/dist/web/standalone/.next/build-manifest.json +2 -2
  69. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  70. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  71. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  72. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  73. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  74. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  75. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  76. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  77. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  78. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  79. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  80. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  81. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  82. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  83. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  84. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  85. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  86. package/dist/web/standalone/.next/server/app/index.html +1 -1
  87. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  88. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  89. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  90. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  91. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  92. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  93. package/dist/web/standalone/.next/server/app-paths-manifest.json +8 -8
  94. package/dist/web/standalone/.next/server/chunks/8357.js +1 -1
  95. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  96. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  97. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  98. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  99. package/dist/web/standalone/node_modules/@gsd/native/dist/native.js +22 -0
  100. package/dist/web/standalone/node_modules/node-pty/build/Makefile +1 -1
  101. package/package.json +4 -4
  102. package/packages/cloud-mcp-gateway/package.json +2 -2
  103. package/packages/contracts/package.json +1 -1
  104. package/packages/daemon/package.json +4 -4
  105. package/packages/gsd-agent-core/package.json +5 -5
  106. package/packages/gsd-agent-modes/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  107. package/packages/gsd-agent-modes/dist/modes/interactive/components/assistant-message.js +21 -23
  108. package/packages/gsd-agent-modes/dist/modes/interactive/components/assistant-message.js.map +1 -1
  109. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts +3 -0
  110. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  111. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js +25 -0
  112. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js.map +1 -1
  113. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.d.ts +1 -0
  114. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.d.ts.map +1 -1
  115. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js +66 -12
  116. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js.map +1 -1
  117. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  118. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js +18 -11
  119. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  120. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-chat-render.d.ts.map +1 -1
  121. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-chat-render.js +16 -0
  122. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-chat-render.js.map +1 -1
  123. package/packages/gsd-agent-modes/package.json +7 -7
  124. package/packages/mcp-server/dist/workflow-tools.js +1 -1
  125. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  126. package/packages/mcp-server/package.json +3 -3
  127. package/packages/native/dist/native.js +22 -0
  128. package/packages/native/package.json +1 -1
  129. package/packages/pi-agent-core/package.json +1 -1
  130. package/packages/pi-ai/dist/image-models.generated.d.ts +30 -0
  131. package/packages/pi-ai/dist/image-models.generated.d.ts.map +1 -1
  132. package/packages/pi-ai/dist/image-models.generated.js +30 -0
  133. package/packages/pi-ai/dist/image-models.generated.js.map +1 -1
  134. package/packages/pi-ai/dist/models.generated.d.ts +23 -17
  135. package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
  136. package/packages/pi-ai/dist/models.generated.js +25 -24
  137. package/packages/pi-ai/dist/models.generated.js.map +1 -1
  138. package/packages/pi-ai/package.json +1 -1
  139. package/packages/pi-coding-agent/dist/core/settings-manager.js +1 -1
  140. package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
  141. package/packages/pi-coding-agent/dist/theme/themes.js +1 -1
  142. package/packages/pi-coding-agent/dist/theme/themes.js.map +1 -1
  143. package/packages/pi-coding-agent/package.json +7 -7
  144. package/packages/pi-tui/dist/utils.d.ts +11 -0
  145. package/packages/pi-tui/dist/utils.d.ts.map +1 -1
  146. package/packages/pi-tui/dist/utils.js +119 -6
  147. package/packages/pi-tui/dist/utils.js.map +1 -1
  148. package/packages/pi-tui/package.json +2 -1
  149. package/packages/rpc-client/package.json +2 -2
  150. package/pkg/dist/theme/themes.js +1 -1
  151. package/pkg/dist/theme/themes.js.map +1 -1
  152. package/pkg/package.json +1 -1
  153. package/src/resources/extensions/browser-tools/index.ts +39 -22
  154. package/src/resources/extensions/browser-tools/state.ts +13 -0
  155. package/src/resources/extensions/browser-tools/tests/browser-tools-unit.test.cjs +57 -0
  156. package/src/resources/extensions/browser-tools/tools/session.ts +4 -2
  157. package/src/resources/extensions/browser-tools/utils.ts +3 -3
  158. package/src/resources/extensions/gsd/auto/loop-deps.ts +1 -0
  159. package/src/resources/extensions/gsd/auto/loop.ts +4 -2
  160. package/src/resources/extensions/gsd/auto/phases.ts +42 -10
  161. package/src/resources/extensions/gsd/auto/session.ts +22 -1
  162. package/src/resources/extensions/gsd/auto/workflow-kernel.ts +1 -0
  163. package/src/resources/extensions/gsd/auto-dispatch.ts +85 -12
  164. package/src/resources/extensions/gsd/auto-model-selection.ts +164 -12
  165. package/src/resources/extensions/gsd/auto-post-unit.ts +20 -2
  166. package/src/resources/extensions/gsd/auto-prompts.ts +23 -20
  167. package/src/resources/extensions/gsd/auto-recovery.ts +22 -3
  168. package/src/resources/extensions/gsd/auto-runtime-state.ts +5 -0
  169. package/src/resources/extensions/gsd/auto-start.ts +1 -1
  170. package/src/resources/extensions/gsd/auto.ts +13 -10
  171. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +3 -3
  172. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +225 -72
  173. package/src/resources/extensions/gsd/closeout-wizard.ts +47 -13
  174. package/src/resources/extensions/gsd/commands/handlers/ops.ts +2 -17
  175. package/src/resources/extensions/gsd/commands-maintenance.ts +124 -13
  176. package/src/resources/extensions/gsd/commands-prefs-wizard.ts +2 -2
  177. package/src/resources/extensions/gsd/db-writer.ts +38 -0
  178. package/src/resources/extensions/gsd/docs/preferences-reference.md +50 -1
  179. package/src/resources/extensions/gsd/gsd-db.ts +564 -186
  180. package/src/resources/extensions/gsd/markdown-renderer.ts +44 -66
  181. package/src/resources/extensions/gsd/md-importer.ts +49 -2
  182. package/src/resources/extensions/gsd/migration-auto-check.ts +154 -34
  183. package/src/resources/extensions/gsd/parsers-legacy.ts +20 -0
  184. package/src/resources/extensions/gsd/planning-path-scope.ts +22 -4
  185. package/src/resources/extensions/gsd/pre-execution-checks.ts +9 -2
  186. package/src/resources/extensions/gsd/preferences-models.ts +112 -43
  187. package/src/resources/extensions/gsd/preferences-types.ts +39 -0
  188. package/src/resources/extensions/gsd/preferences-validation.ts +76 -2
  189. package/src/resources/extensions/gsd/preferences.ts +5 -0
  190. package/src/resources/extensions/gsd/prompts/gate-evaluate.md +1 -1
  191. package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  192. package/src/resources/extensions/gsd/prompts/refine-slice.md +1 -1
  193. package/src/resources/extensions/gsd/roadmap-slices.ts +6 -1
  194. package/src/resources/extensions/gsd/safety/content-validator.ts +8 -5
  195. package/src/resources/extensions/gsd/source-observations.ts +402 -0
  196. package/src/resources/extensions/gsd/state-reconciliation/drift/completion.ts +20 -8
  197. package/src/resources/extensions/gsd/state-reconciliation/drift/stale-render.ts +44 -5
  198. package/src/resources/extensions/gsd/state-reconciliation/drift/stale-worker.ts +39 -11
  199. package/src/resources/extensions/gsd/state-reconciliation/index.ts +45 -15
  200. package/src/resources/extensions/gsd/state-reconciliation/spawn-gate.ts +4 -4
  201. package/src/resources/extensions/gsd/state.ts +7 -4
  202. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +15 -0
  203. package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +299 -1
  204. package/src/resources/extensions/gsd/tests/auto-paused-ui-cleanup.test.ts +32 -0
  205. package/src/resources/extensions/gsd/tests/auto-phases-lifecycle.test.ts +75 -3
  206. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +22 -1
  207. package/src/resources/extensions/gsd/tests/before-provider-context-management.test.ts +145 -0
  208. package/src/resources/extensions/gsd/tests/closeout-wizard.test.ts +44 -0
  209. package/src/resources/extensions/gsd/tests/commands-dispatcher-unmerged-milestone.test.ts +26 -1
  210. package/src/resources/extensions/gsd/tests/content-validator.test.ts +74 -0
  211. package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +16 -2
  212. package/src/resources/extensions/gsd/tests/doctor-scope-db-unavailable.test.ts +1 -11
  213. package/src/resources/extensions/gsd/tests/gate-dispatch.test.ts +64 -0
  214. package/src/resources/extensions/gsd/tests/gate-storage.test.ts +15 -0
  215. package/src/resources/extensions/gsd/tests/gsd-recover.test.ts +62 -1
  216. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +15 -0
  217. package/src/resources/extensions/gsd/tests/markdown-renderer.test.ts +42 -0
  218. package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +99 -0
  219. package/src/resources/extensions/gsd/tests/plan-slice.test.ts +99 -2
  220. package/src/resources/extensions/gsd/tests/plan-task.test.ts +19 -0
  221. package/src/resources/extensions/gsd/tests/preferences.test.ts +14 -0
  222. package/src/resources/extensions/gsd/tests/prefs-wizard-coverage.test.ts +1 -0
  223. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +9 -0
  224. package/src/resources/extensions/gsd/tests/register-hooks-depth-verification.test.ts +101 -1
  225. package/src/resources/extensions/gsd/tests/repository-registry.test.ts +2 -2
  226. package/src/resources/extensions/gsd/tests/runtime-invariant-modules.test.ts +8 -0
  227. package/src/resources/extensions/gsd/tests/schema-v21-sequence.test.ts +5 -3
  228. package/src/resources/extensions/gsd/tests/schema-v27-v28-sequence.test.ts +162 -18
  229. package/src/resources/extensions/gsd/tests/skipped-validation-db-atomicity.test.ts +8 -0
  230. package/src/resources/extensions/gsd/tests/source-observations.test.ts +275 -0
  231. package/src/resources/extensions/gsd/tests/stale-queued-milestone.test.ts +43 -0
  232. package/src/resources/extensions/gsd/tests/state-reconciliation-drift.test.ts +76 -21
  233. package/src/resources/extensions/gsd/tests/thinking-level-resolution.test.ts +203 -0
  234. package/src/resources/extensions/gsd/tests/uat-policy.test.ts +170 -0
  235. package/src/resources/extensions/gsd/tests/unit-context-manifest.test.ts +7 -1
  236. package/src/resources/extensions/gsd/tests/workflow-kernel.test.ts +7 -0
  237. package/src/resources/extensions/gsd/tests/workflow-manifest.test.ts +306 -1
  238. package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +73 -6
  239. package/src/resources/extensions/gsd/tests/worktree-db.test.ts +511 -1
  240. package/src/resources/extensions/gsd/tests/worktree-state-projection.test.ts +44 -0
  241. package/src/resources/extensions/gsd/tool-contract.ts +28 -0
  242. package/src/resources/extensions/gsd/tool-presentation-plan.ts +1 -11
  243. package/src/resources/extensions/gsd/tools/complete-slice.ts +7 -6
  244. package/src/resources/extensions/gsd/tools/plan-slice.ts +54 -12
  245. package/src/resources/extensions/gsd/tools/plan-task.ts +8 -1
  246. package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +66 -526
  247. package/src/resources/extensions/gsd/types.ts +1 -0
  248. package/src/resources/extensions/gsd/uat-policy.ts +191 -0
  249. package/src/resources/extensions/gsd/uat-run.ts +550 -0
  250. package/src/resources/extensions/gsd/unit-context-manifest.ts +3 -4
  251. package/src/resources/extensions/gsd/verdict-parser.ts +3 -10
  252. package/src/resources/extensions/gsd/workflow-manifest.ts +193 -7
  253. package/src/resources/extensions/gsd/workflow-projections.ts +9 -0
  254. package/src/resources/extensions/gsd/worktree-state-projection.ts +22 -22
  255. package/src/resources/extensions/shared/tests/format-utils.test.ts +8 -3
  256. package/src/resources/extensions/subagent/agents.ts +4 -0
  257. package/src/resources/extensions/subagent/index.ts +28 -3
  258. package/src/resources/extensions/subagent/launch.ts +8 -0
  259. package/src/resources/extensions/subagent/tests/model-override.test.ts +31 -0
  260. /package/dist/web/standalone/.next/static/{zzYMrKpPGfRQRxSFO32Jr → tJOKQbQRO-9MiFDO8DIDS}/_buildManifest.js +0 -0
  261. /package/dist/web/standalone/.next/static/{zzYMrKpPGfRQRxSFO32Jr → tJOKQbQRO-9MiFDO8DIDS}/_ssgManifest.js +0 -0
@@ -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
+ }
@@ -0,0 +1,414 @@
1
+ // Project/App: gsd-pi
2
+ // File Purpose: Owns the durable UAT run lifecycle behind gsd_uat_result_save.
3
+ import { existsSync, readdirSync, readFileSync } from "node:fs";
4
+ import { isAbsolute, join, resolve } from "node:path";
5
+ import { hasUatBrowserToolSurface, isUatType, UAT_MODE_POLICIES, UAT_TYPES, validateUatModePolicy, } from "./uat-policy.js";
6
+ import { buildRunUatPresentationForType, canonicalWorkflowToolName, parseMcpToolName, RUN_UAT_FORBIDDEN_TOOL_NAMES, RUN_UAT_TOOL_PRESENTATION_PLAN_ID, RUN_UAT_WORKFLOW_TOOL_NAMES, } from "./tool-presentation-plan.js";
7
+ import { saveFile } from "./files.js";
8
+ import { relSliceFile, resolveGsdPathContract } from "./paths.js";
9
+ import { buildManualValidationGuidance, resolveCanonicalMilestoneRoot } from "./worktree-manager.js";
10
+ function isNonEmptyString(value) {
11
+ return typeof value === "string" && value.trim().length > 0;
12
+ }
13
+ function mergeBlockedTools(current, canonical) {
14
+ const merged = new Map();
15
+ for (const entry of [...(current ?? []), ...canonical]) {
16
+ merged.set(canonicalWorkflowToolName(parseMcpToolName(entry.name)?.tool ?? entry.name), entry);
17
+ }
18
+ return [...merged.values()];
19
+ }
20
+ function mergePresentedTools(current, canonical) {
21
+ return [...new Set([...(current ?? []), ...canonical])];
22
+ }
23
+ function normalizeUatVerdict(params) {
24
+ const raw = params;
25
+ if (typeof raw.verdict === "string") {
26
+ return { ...params, verdict: raw.verdict.toUpperCase() };
27
+ }
28
+ return params;
29
+ }
30
+ function supplyDefaultPresentation(params) {
31
+ const raw = params;
32
+ if (!raw.presentation) {
33
+ return { ...params, presentation: buildRunUatPresentationForType(params.uatType) };
34
+ }
35
+ return params;
36
+ }
37
+ function mergeCanonicalPresentation(params) {
38
+ const canonicalPresentation = buildRunUatPresentationForType(params.uatType);
39
+ const providedPresentation = params.presentation;
40
+ return {
41
+ ...params,
42
+ presentation: {
43
+ ...providedPresentation,
44
+ surface: providedPresentation.surface ?? canonicalPresentation.surface,
45
+ presentedTools: mergePresentedTools(providedPresentation.presentedTools, canonicalPresentation.presentedTools),
46
+ blockedTools: mergeBlockedTools(providedPresentation.blockedTools, canonicalPresentation.blockedTools),
47
+ toolPresentationPlanId: RUN_UAT_TOOL_PRESENTATION_PLAN_ID,
48
+ },
49
+ };
50
+ }
51
+ function ensureUatRequiredFields(params) {
52
+ if (!isNonEmptyString(params.milestoneId))
53
+ return "milestoneId is required";
54
+ if (!isNonEmptyString(params.sliceId))
55
+ return "sliceId is required";
56
+ if (!isNonEmptyString(params.uatType))
57
+ return "uatType is required";
58
+ if (!isUatType(params.uatType)) {
59
+ return `uatType must be one of: ${UAT_TYPES.join(", ")}`;
60
+ }
61
+ if (!["PASS", "FAIL", "PARTIAL"].includes(params.verdict))
62
+ return "verdict must be PASS, FAIL, or PARTIAL";
63
+ if (!Array.isArray(params.checks) || params.checks.length === 0)
64
+ return "checks must contain at least one UAT check";
65
+ if (!params.presentation || !Array.isArray(params.presentation.presentedTools))
66
+ return "presentation.presentedTools is required";
67
+ if (!Array.isArray(params.presentation.blockedTools))
68
+ return "presentation.blockedTools is required";
69
+ return null;
70
+ }
71
+ function approvedEvidenceRoots(basePath) {
72
+ const contract = resolveGsdPathContract(basePath);
73
+ return [contract.worktreeGsd, contract.projectGsd].filter((root) => typeof root === "string");
74
+ }
75
+ function approvedBrowserArtifactRoots(basePath) {
76
+ const contract = resolveGsdPathContract(basePath);
77
+ const roots = [contract.workRoot, contract.projectRoot].map((root) => join(root, ".artifacts", "browser"));
78
+ return [...new Set(roots)];
79
+ }
80
+ function pathStartsWithin(parent, target) {
81
+ const normalizedParent = parent.replace(/\\/g, "/").replace(/\/+$/, "");
82
+ const normalizedTarget = target.replace(/\\/g, "/").replace(/\/+$/, "");
83
+ return normalizedTarget === normalizedParent || normalizedTarget.startsWith(`${normalizedParent}/`);
84
+ }
85
+ function pushUnique(paths, candidate) {
86
+ if (!paths.includes(candidate))
87
+ paths.push(candidate);
88
+ }
89
+ function execMetaPathCandidates(basePath, ref) {
90
+ const trimmed = ref.trim();
91
+ const candidates = [];
92
+ const execDirs = approvedEvidenceRoots(basePath).map((root) => join(root, "exec"));
93
+ const normalizedRef = trimmed.replace(/\\/g, "/");
94
+ const pathLike = normalizedRef.endsWith(".meta.json") || normalizedRef.includes("/.gsd/exec/");
95
+ if (pathLike) {
96
+ const rawPath = isAbsolute(trimmed) ? resolve(trimmed) : resolve(basePath, trimmed);
97
+ pushUnique(candidates, rawPath);
98
+ const relativeExecMarker = ".gsd/exec/";
99
+ const markerIndex = normalizedRef.indexOf(relativeExecMarker);
100
+ if (markerIndex >= 0) {
101
+ const execRelative = normalizedRef.slice(markerIndex + relativeExecMarker.length);
102
+ for (const execDir of execDirs) {
103
+ pushUnique(candidates, join(execDir, execRelative));
104
+ }
105
+ }
106
+ return candidates.filter((candidate) => execDirs.some((execDir) => pathStartsWithin(execDir, candidate)));
107
+ }
108
+ for (const execDir of execDirs) {
109
+ pushUnique(candidates, join(execDir, `${trimmed}.meta.json`));
110
+ }
111
+ return candidates;
112
+ }
113
+ function resolveExecMetaPath(basePath, ref) {
114
+ for (const candidate of execMetaPathCandidates(basePath, ref)) {
115
+ if (existsSync(candidate))
116
+ return candidate;
117
+ }
118
+ return null;
119
+ }
120
+ function evidencePathIsApproved(basePath, ref) {
121
+ const normalizedRef = ref.replace(/\\/g, "/");
122
+ if (normalizedRef.startsWith(".gsd/exec/") || normalizedRef.startsWith(".gsd/uat/"))
123
+ return true;
124
+ if (normalizedRef.startsWith(".artifacts/browser/")) {
125
+ const resolvedRef = resolve(basePath, ref);
126
+ return approvedBrowserArtifactRoots(basePath).some((root) => pathStartsWithin(root, resolvedRef));
127
+ }
128
+ const gsdEvidenceApproved = approvedEvidenceRoots(basePath).some((root) => {
129
+ return pathStartsWithin(join(root, "exec"), ref) || pathStartsWithin(join(root, "uat"), ref);
130
+ });
131
+ if (gsdEvidenceApproved)
132
+ return true;
133
+ return approvedBrowserArtifactRoots(basePath).some((root) => pathStartsWithin(root, ref));
134
+ }
135
+ function validateEvidenceRef(basePath, evidence) {
136
+ if (!isNonEmptyString(evidence.ref))
137
+ return "evidence.ref is required";
138
+ if (evidence.kind === "gsd_uat_exec" || evidence.kind === "gsd_exec") {
139
+ const path = resolveExecMetaPath(basePath, evidence.ref.trim());
140
+ if (!path)
141
+ return `missing gsd_exec metadata for evidence id "${evidence.ref}"`;
142
+ if (evidence.kind === "gsd_uat_exec") {
143
+ try {
144
+ const meta = JSON.parse(readFileSync(path, "utf-8"));
145
+ if (meta.metadata?.kind !== "uat_exec")
146
+ return `evidence id "${evidence.ref}" is not typed as uat_exec`;
147
+ }
148
+ catch {
149
+ return `invalid gsd_exec metadata JSON for evidence id "${evidence.ref}"`;
150
+ }
151
+ }
152
+ return null;
153
+ }
154
+ if (evidence.kind === "url") {
155
+ try {
156
+ const parsed = new URL(evidence.ref);
157
+ return parsed.protocol === "http:" || parsed.protocol === "https:"
158
+ ? null
159
+ : `invalid URL evidence ref "${evidence.ref}"`;
160
+ }
161
+ catch {
162
+ return `invalid URL evidence ref "${evidence.ref}"`;
163
+ }
164
+ }
165
+ return evidencePathIsApproved(basePath, evidence.ref)
166
+ ? null
167
+ : `evidence ref "${evidence.ref}" is outside approved evidence locations`;
168
+ }
169
+ function validateUatChecks(basePath, params) {
170
+ for (const check of params.checks) {
171
+ if (!isNonEmptyString(check.id))
172
+ return "every check must have a non-empty id";
173
+ if (!isNonEmptyString(check.description))
174
+ return `check ${check.id} must have a description`;
175
+ if (!["artifact", "runtime", "browser", "human-follow-up"].includes(check.mode)) {
176
+ return `check ${check.id} has invalid mode "${check.mode}"`;
177
+ }
178
+ if (!["PASS", "FAIL", "NEEDS-HUMAN"].includes(check.result)) {
179
+ return `check ${check.id} has invalid result "${check.result}"`;
180
+ }
181
+ if (check.result === "PASS" || check.result === "FAIL") {
182
+ if (!Array.isArray(check.evidence) || check.evidence.length === 0) {
183
+ return `check ${check.id} is ${check.result} but has no objective evidence`;
184
+ }
185
+ for (const evidence of check.evidence) {
186
+ const error = validateEvidenceRef(basePath, evidence);
187
+ if (error)
188
+ return `check ${check.id}: ${error}`;
189
+ }
190
+ }
191
+ else if (!isNonEmptyString(check.notes)) {
192
+ return `check ${check.id} is NEEDS-HUMAN but has no manual instruction or reason`;
193
+ }
194
+ }
195
+ return null;
196
+ }
197
+ function validateFreshUatOwnedEvidence(params) {
198
+ const hasFreshUatEvidence = params.checks.some((check) => (check.evidence ?? []).some((evidence) => evidence.kind === "gsd_uat_exec"));
199
+ return hasFreshUatEvidence
200
+ ? null
201
+ : "UAT Assessment requires at least one fresh gsd_uat_exec evidence reference from run-uat";
202
+ }
203
+ function quoteToolNames(toolNames) {
204
+ return toolNames.map((toolName) => `"${toolName}"`).join(", ");
205
+ }
206
+ function validateCanonicalPresentation(params) {
207
+ const aliasHints = {
208
+ gsd_save_summary: "gsd_summary_save",
209
+ gsd_complete_task: "gsd_task_complete",
210
+ gsd_complete_slice: "gsd_slice_complete",
211
+ gsd_milestone_complete: "gsd_complete_milestone",
212
+ };
213
+ const errors = [];
214
+ for (const toolName of params.presentation.presentedTools) {
215
+ const baseName = parseMcpToolName(toolName)?.tool ?? toolName;
216
+ const canonical = aliasHints[baseName];
217
+ if (canonical)
218
+ errors.push(`presentation tool "${toolName}" uses an alias; use canonical "${canonical}"`);
219
+ }
220
+ const presentedCanonical = new Set(params.presentation.presentedTools.map((toolName) => canonicalWorkflowToolName(parseMcpToolName(toolName)?.tool ?? toolName)));
221
+ const missingRequiredTools = RUN_UAT_WORKFLOW_TOOL_NAMES.filter((requiredTool) => !presentedCanonical.has(requiredTool));
222
+ if (missingRequiredTools.length === 1) {
223
+ errors.push(`presentation is missing required UAT tool "${missingRequiredTools[0]}"`);
224
+ }
225
+ else if (missingRequiredTools.length > 1) {
226
+ errors.push(`presentation is missing required UAT tools ${quoteToolNames(missingRequiredTools)}`);
227
+ }
228
+ const forbiddenCanonical = new Set(RUN_UAT_FORBIDDEN_TOOL_NAMES
229
+ .filter((toolName) => !toolName.includes("*"))
230
+ .map((toolName) => canonicalWorkflowToolName(parseMcpToolName(toolName)?.tool ?? toolName)));
231
+ const forbiddenPresentedTools = [];
232
+ for (const toolName of params.presentation.presentedTools) {
233
+ const canonical = canonicalWorkflowToolName(parseMcpToolName(toolName)?.tool ?? toolName);
234
+ if (toolName === "mcp__gsd-workflow__*" || forbiddenCanonical.has(canonical)) {
235
+ forbiddenPresentedTools.push(toolName);
236
+ }
237
+ }
238
+ if (forbiddenPresentedTools.length === 1) {
239
+ errors.push(`presentation includes forbidden run-uat tool "${forbiddenPresentedTools[0]}"`);
240
+ }
241
+ else if (forbiddenPresentedTools.length > 1) {
242
+ errors.push(`presentation includes forbidden run-uat tools ${quoteToolNames(forbiddenPresentedTools)}`);
243
+ }
244
+ const blockedCanonical = new Set(params.presentation.blockedTools.map((entry) => canonicalWorkflowToolName(parseMcpToolName(entry.name)?.tool ?? entry.name)));
245
+ const missingBlockedTools = ["gsd_exec", "gsd_summary_save", "gsd_save_gate_result"].filter((blockedTool) => !blockedCanonical.has(blockedTool));
246
+ if (missingBlockedTools.length === 1) {
247
+ errors.push(`presentation must record "${missingBlockedTools[0]}" as blocked during run-uat`);
248
+ }
249
+ else if (missingBlockedTools.length > 1) {
250
+ errors.push(`presentation must record ${quoteToolNames(missingBlockedTools)} as blocked during run-uat`);
251
+ }
252
+ return errors.length > 0 ? errors.join("; ") : null;
253
+ }
254
+ function nextUatAttempt(basePath, milestoneId, sliceId) {
255
+ const contract = resolveGsdPathContract(basePath);
256
+ const dir = join(contract.projectGsd, "uat", milestoneId, sliceId);
257
+ if (!existsSync(dir))
258
+ return 1;
259
+ let max = 0;
260
+ for (const entry of readdirSync(dir)) {
261
+ const match = /^attempt-(\d+)\.json$/.exec(entry);
262
+ if (match)
263
+ max = Math.max(max, Number(match[1]));
264
+ }
265
+ return max + 1;
266
+ }
267
+ function resolveUatAttempt(basePath, params) {
268
+ if (params.attempt === "auto" || params.attempt === undefined) {
269
+ return nextUatAttempt(basePath, params.milestoneId, params.sliceId);
270
+ }
271
+ const attempt = typeof params.attempt === "string"
272
+ ? Number.parseInt(params.attempt, 10)
273
+ : params.attempt;
274
+ if (!Number.isInteger(attempt) || attempt < 1) {
275
+ return {
276
+ code: "invalid_attempt",
277
+ message: "attempt must be a positive integer or auto",
278
+ };
279
+ }
280
+ return attempt;
281
+ }
282
+ function escapeMarkdownTableCell(value) {
283
+ return String(value ?? "")
284
+ .replace(/[\\|]/g, (char) => `\\${char}`)
285
+ .replace(/\r?\n/g, "<br>");
286
+ }
287
+ function renderCheckRow(check) {
288
+ const evidence = (check.evidence ?? []).map((entry) => `${entry.kind}:${entry.ref}`).join("<br>") || "-";
289
+ return `| ${escapeMarkdownTableCell(check.description)} | ${escapeMarkdownTableCell(check.mode)} | ${escapeMarkdownTableCell(check.result)} | ${escapeMarkdownTableCell(evidence)} | ${escapeMarkdownTableCell(check.notes)} |`;
290
+ }
291
+ function renderUatAssessment(params, run) {
292
+ const lines = [
293
+ "---",
294
+ `sliceId: ${params.sliceId}`,
295
+ `uatType: ${params.uatType}`,
296
+ `verdict: ${params.verdict}`,
297
+ `attempt: ${run.attempt}`,
298
+ `runId: ${run.runId}`,
299
+ `worktreeRoot: ${run.worktreeRoot}`,
300
+ `date: ${run.evaluatedAt}`,
301
+ "---",
302
+ "",
303
+ `# UAT Result - ${params.sliceId}`,
304
+ "",
305
+ "## Checks",
306
+ "",
307
+ "| Check | Mode | Result | Evidence | Notes |",
308
+ "|-------|------|--------|----------|-------|",
309
+ ...params.checks.map(renderCheckRow),
310
+ "",
311
+ "## Overall Verdict",
312
+ "",
313
+ `${params.verdict} - ${params.notes ?? "UAT result saved."}`,
314
+ "",
315
+ "## Tool Presentation",
316
+ "",
317
+ "```json",
318
+ JSON.stringify(params.presentation, null, 2),
319
+ "```",
320
+ "",
321
+ "## Gate",
322
+ "",
323
+ `Aggregate UAT gate saved as ${run.gateVerdict}.`,
324
+ ];
325
+ if (run.manualGuidance) {
326
+ lines.push("", "## Manual Validation", "", "One or more checks are marked `NEEDS-HUMAN` and require a person to validate:", "", ...run.manualGuidance.split("\n").map((line) => `- ${line}`));
327
+ }
328
+ return `${lines.join("\n")}\n`;
329
+ }
330
+ export function prepareUatRun(basePath, rawParams) {
331
+ let params = normalizeUatVerdict(rawParams);
332
+ params = supplyDefaultPresentation(params);
333
+ const requiredError = ensureUatRequiredFields(params);
334
+ if (requiredError)
335
+ return { ok: false, error: { code: "invalid_params", message: requiredError } };
336
+ const presentationError = validateCanonicalPresentation(params);
337
+ if (presentationError)
338
+ return { ok: false, error: { code: "alias_tool_name", message: presentationError } };
339
+ params = mergeCanonicalPresentation(params);
340
+ const checkError = validateUatChecks(basePath, params);
341
+ if (checkError)
342
+ return { ok: false, error: { code: "invalid_evidence", message: checkError } };
343
+ const freshEvidenceError = validateFreshUatOwnedEvidence(params);
344
+ if (freshEvidenceError) {
345
+ return { ok: false, error: { code: "missing_fresh_uat_evidence", message: freshEvidenceError } };
346
+ }
347
+ const modeError = validateUatModePolicy(params);
348
+ if (modeError)
349
+ return { ok: false, error: { code: "uat_mode_mismatch", message: modeError } };
350
+ const attempt = resolveUatAttempt(basePath, params);
351
+ if (typeof attempt !== "number")
352
+ return { ok: false, error: attempt };
353
+ const gateVerdict = params.verdict === "PASS" ? "pass" : "flag";
354
+ const gateOutcome = params.verdict === "PASS" ? "pass" : "fail";
355
+ const rationale = params.notes ?? `UAT ${params.verdict} for ${params.sliceId}.`;
356
+ const evaluatedAt = new Date().toISOString();
357
+ const runId = `uat:${params.milestoneId}:${params.sliceId}:attempt-${attempt}`;
358
+ const worktreeRoot = resolveCanonicalMilestoneRoot(basePath, params.milestoneId);
359
+ const hasHuman = params.checks.some((check) => check.result === "NEEDS-HUMAN");
360
+ const manualGuidance = hasHuman
361
+ ? buildManualValidationGuidance(basePath, params.milestoneId, {
362
+ uatPath: relSliceFile(basePath, params.milestoneId, params.sliceId, "UAT"),
363
+ })
364
+ : null;
365
+ const browserToolsPresented = hasUatBrowserToolSurface(params.presentation.presentedTools);
366
+ const assessment = renderUatAssessment(params, {
367
+ attempt,
368
+ gateVerdict,
369
+ runId,
370
+ worktreeRoot,
371
+ evaluatedAt,
372
+ manualGuidance,
373
+ });
374
+ return {
375
+ ok: true,
376
+ run: {
377
+ params,
378
+ runId,
379
+ attempt,
380
+ gateVerdict,
381
+ gateOutcome,
382
+ rationale,
383
+ assessment,
384
+ evaluatedAt,
385
+ hasHuman,
386
+ manualGuidance,
387
+ worktreeRoot,
388
+ browserToolsPresented,
389
+ },
390
+ };
391
+ }
392
+ export async function saveUatAttemptArtifact(basePath, run) {
393
+ const contract = resolveGsdPathContract(basePath);
394
+ const relativePath = `uat/${run.params.milestoneId}/${run.params.sliceId}/attempt-${run.attempt}.json`;
395
+ const payload = {
396
+ runId: run.runId,
397
+ attempt: run.attempt,
398
+ milestoneId: run.params.milestoneId,
399
+ sliceId: run.params.sliceId,
400
+ uatType: run.params.uatType,
401
+ verdict: run.params.verdict,
402
+ gateVerdict: run.gateVerdict,
403
+ evaluatedAt: run.evaluatedAt,
404
+ worktreeRoot: run.worktreeRoot,
405
+ browserToolsPresented: run.browserToolsPresented,
406
+ modePolicy: UAT_MODE_POLICIES[run.params.uatType],
407
+ checks: run.params.checks,
408
+ presentation: run.params.presentation,
409
+ notes: run.params.notes,
410
+ previousAttemptId: run.params.previousAttemptId,
411
+ };
412
+ await saveFile(join(contract.projectGsd, relativePath), `${JSON.stringify(payload, null, 2)}\n`);
413
+ return relativePath;
414
+ }
@@ -93,11 +93,10 @@ const TOOLS_PLANNING_DISPATCH_RECON = {
93
93
  mode: "planning-dispatch",
94
94
  allowedSubagents: ["scout", "planner"],
95
95
  };
96
- // Like TOOLS_PLANNING_DISPATCH_RECON, but for closeout units that fan out
97
- // verification work to review-tier specialists.
96
+ // Like TOOLS_PLANNING_DISPATCH_RECON, but for gate-evaluate's tester fanout.
98
97
  const TOOLS_PLANNING_DISPATCH_REVIEW = {
99
98
  mode: "planning-dispatch",
100
- allowedSubagents: ["reviewer", "security", "tester"],
99
+ allowedSubagents: ["tester"],
101
100
  };
102
101
  const TOOLS_DOCS = {
103
102
  mode: "docs",
@@ -395,7 +394,7 @@ export const UNIT_MANIFESTS = {
395
394
  // plan and report via the DB-backed gate-result tool.
396
395
  tools: TOOLS_PLANNING_DISPATCH_REVIEW,
397
396
  artifacts: {
398
- inline: ["slice-plan", "prior-task-summaries"],
397
+ inline: ["slice-plan"],
399
398
  excerpt: [],
400
399
  onDemand: [],
401
400
  },
@@ -4,9 +4,9 @@
4
4
  * All verdict-related logic lives here so that normalization rules
5
5
  * (e.g. `passed` → `pass`) are applied consistently across the codebase.
6
6
  */
7
- import { extractUatType } from "./files.js";
8
7
  import { splitFrontmatter, parseFrontmatterMap } from "../shared/frontmatter.js";
9
8
  import { parse as parseYaml } from "yaml";
9
+ import { getDeclaredUatType, isPartialEligibleUatType } from "./uat-policy.js";
10
10
  function normalizeVerdict(value) {
11
11
  if (typeof value !== "string")
12
12
  return undefined;
@@ -98,11 +98,6 @@ export const UAT_ACCEPTABLE_VERDICTS = ["pass", "passed"];
98
98
  * UAT types whose results may legitimately produce a `partial` verdict
99
99
  * when all automatable checks pass but human-only checks remain.
100
100
  */
101
- const PARTIAL_ELIGIBLE_UAT_TYPES = [
102
- "mixed",
103
- "human-experience",
104
- "live-runtime",
105
- ];
106
101
  /**
107
102
  * Check whether a verdict is acceptable for a given UAT type.
108
103
  *
@@ -112,7 +107,7 @@ const PARTIAL_ELIGIBLE_UAT_TYPES = [
112
107
  export function isAcceptableUatVerdict(verdict, uatType) {
113
108
  if (UAT_ACCEPTABLE_VERDICTS.includes(verdict))
114
109
  return true;
115
- if (verdict === "partial" && uatType && PARTIAL_ELIGIBLE_UAT_TYPES.includes(uatType)) {
110
+ if (verdict === "partial" && isPartialEligibleUatType(uatType)) {
116
111
  return true;
117
112
  }
118
113
  return false;
@@ -136,5 +131,5 @@ export function isValidMilestoneVerdict(verdict) {
136
131
  * the codebase when a UAT file lacks an explicit `## UAT Type` section.
137
132
  */
138
133
  export function getUatType(content) {
139
- return extractUatType(content) ?? "artifact-driven";
134
+ return getDeclaredUatType(content);
140
135
  }