@opengsd/gsd-pi 1.2.0-dev.844675c9 → 1.2.0-dev.b1abb545

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 (375) hide show
  1. package/dist/cli-web-branch.d.ts +2 -0
  2. package/dist/cli-web-branch.js +9 -2
  3. package/dist/help-text.js +5 -0
  4. package/dist/resources/.managed-resources-content-hash +1 -1
  5. package/dist/resources/extensions/ask-user-questions.js +78 -23
  6. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +84 -228
  7. package/dist/resources/extensions/claude-code-cli/turn-assembler.js +224 -0
  8. package/dist/resources/extensions/github-sync/templates.js +3 -3
  9. package/dist/resources/extensions/gsd/artifact-projection.js +14 -0
  10. package/dist/resources/extensions/gsd/auto/loop.js +74 -56
  11. package/dist/resources/extensions/gsd/auto/orchestrator.js +109 -11
  12. package/dist/resources/extensions/gsd/auto/phases.js +28 -3
  13. package/dist/resources/extensions/gsd/auto/run-unit.js +2 -1
  14. package/dist/resources/extensions/gsd/auto/session.js +3 -0
  15. package/dist/resources/extensions/gsd/auto-dashboard.js +16 -4
  16. package/dist/resources/extensions/gsd/auto-dispatch.js +6 -5
  17. package/dist/resources/extensions/gsd/auto-model-selection.js +8 -0
  18. package/dist/resources/extensions/gsd/auto-post-unit.js +4 -3
  19. package/dist/resources/extensions/gsd/auto-prompts.js +81 -8
  20. package/dist/resources/extensions/gsd/auto-recovery.js +48 -49
  21. package/dist/resources/extensions/gsd/auto-runtime-state.js +14 -0
  22. package/dist/resources/extensions/gsd/auto-start.js +12 -23
  23. package/dist/resources/extensions/gsd/auto-timers.js +16 -2
  24. package/dist/resources/extensions/gsd/auto-tool-tracking.js +32 -0
  25. package/dist/resources/extensions/gsd/auto-unit-tool-scope.js +4 -29
  26. package/dist/resources/extensions/gsd/auto-verification.js +7 -7
  27. package/dist/resources/extensions/gsd/auto-worktree.js +21 -19
  28. package/dist/resources/extensions/gsd/auto.js +11 -7
  29. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +28 -37
  30. package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +11 -37
  31. package/dist/resources/extensions/gsd/bootstrap/query-tools.js +2 -2
  32. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +100 -138
  33. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +63 -4
  34. package/dist/resources/extensions/gsd/closeout-consistency-gate.js +21 -4
  35. package/dist/resources/extensions/gsd/codebase-generator.js +8 -4
  36. package/dist/resources/extensions/gsd/commands/handlers/auto.js +3 -0
  37. package/dist/resources/extensions/gsd/commands-handlers.js +20 -0
  38. package/dist/resources/extensions/gsd/commands-inspect.js +4 -8
  39. package/dist/resources/extensions/gsd/commands-maintenance.js +61 -41
  40. package/dist/resources/extensions/gsd/commands-ship.js +2 -2
  41. package/dist/resources/extensions/gsd/commands-verdict.js +12 -2
  42. package/dist/resources/extensions/gsd/db-workspace.js +103 -0
  43. package/dist/resources/extensions/gsd/delegation-policy.js +2 -10
  44. package/dist/resources/extensions/gsd/discussion-handoff.js +218 -0
  45. package/dist/resources/extensions/gsd/docs/preferences-reference.md +9 -0
  46. package/dist/resources/extensions/gsd/doctor.js +16 -9
  47. package/dist/resources/extensions/gsd/error-classifier.js +1 -1
  48. package/dist/resources/extensions/gsd/git-conflict-state.js +16 -1
  49. package/dist/resources/extensions/gsd/gsd-db.js +12 -0
  50. package/dist/resources/extensions/gsd/guided-flow.js +34 -468
  51. package/dist/resources/extensions/gsd/guided-unit-completion.js +225 -0
  52. package/dist/resources/extensions/gsd/markdown-renderer.js +2 -1
  53. package/dist/resources/extensions/gsd/mcp-filter.js +2 -1
  54. package/dist/resources/extensions/gsd/mcp-tool-name.js +26 -0
  55. package/dist/resources/extensions/gsd/md-importer.js +4 -3
  56. package/dist/resources/extensions/gsd/migrate/safety.js +2 -2
  57. package/dist/resources/extensions/gsd/migration-auto-check.js +3 -2
  58. package/dist/resources/extensions/gsd/milestone-closeout-proof.js +72 -0
  59. package/dist/resources/extensions/gsd/milestone-closeout.js +12 -4
  60. package/dist/resources/extensions/gsd/milestone-merge-transaction.js +10 -0
  61. package/dist/resources/extensions/gsd/milestone-planning-persistence.js +156 -0
  62. package/dist/resources/extensions/gsd/milestone-readiness.js +77 -0
  63. package/dist/resources/extensions/gsd/milestone-settlement.js +50 -0
  64. package/dist/resources/extensions/gsd/milestone-validation-evidence.js +73 -0
  65. package/dist/resources/extensions/gsd/milestone-validation-verdict.js +57 -0
  66. package/dist/resources/extensions/gsd/parallel-eligibility.js +3 -6
  67. package/dist/resources/extensions/gsd/parallel-orchestrator.js +3 -2
  68. package/dist/resources/extensions/gsd/preferences-diagnostics.js +67 -0
  69. package/dist/resources/extensions/gsd/preferences.js +147 -29
  70. package/dist/resources/extensions/gsd/prompts/complete-slice.md +1 -0
  71. package/dist/resources/extensions/gsd/prompts/execute-task.md +2 -0
  72. package/dist/resources/extensions/gsd/prompts/guided-discuss-project.md +3 -1
  73. package/dist/resources/extensions/gsd/prompts/plan-milestone.md +2 -0
  74. package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -0
  75. package/dist/resources/extensions/gsd/prompts/refine-slice.md +1 -0
  76. package/dist/resources/extensions/gsd/prompts/system.md +1 -1
  77. package/dist/resources/extensions/gsd/provider-payload-policy.js +83 -0
  78. package/dist/resources/extensions/gsd/pull-request-process.js +13 -0
  79. package/dist/resources/extensions/gsd/quality-gate-closure.js +109 -0
  80. package/dist/resources/extensions/gsd/question-transport.js +86 -0
  81. package/dist/resources/extensions/gsd/roadmap-slices.js +8 -2
  82. package/dist/resources/extensions/gsd/slice-parallel-orchestrator.js +3 -2
  83. package/dist/resources/extensions/gsd/state.js +13 -5
  84. package/dist/resources/extensions/gsd/templates/plan.md +7 -0
  85. package/dist/resources/extensions/gsd/templates/project.md +1 -0
  86. package/dist/resources/extensions/gsd/templates/roadmap.md +1 -1
  87. package/dist/resources/extensions/gsd/templates/uat.md +5 -1
  88. package/dist/resources/extensions/gsd/tool-contract.js +52 -8
  89. package/dist/resources/extensions/gsd/tool-presentation-plan.js +15 -34
  90. package/dist/resources/extensions/gsd/tool-surface-snapshot.js +17 -0
  91. package/dist/resources/extensions/gsd/tools/plan-milestone.js +15 -143
  92. package/dist/resources/extensions/gsd/tools/reassess-roadmap.js +39 -0
  93. package/dist/resources/extensions/gsd/tools/validate-milestone.js +15 -78
  94. package/dist/resources/extensions/gsd/uat-policy.js +16 -10
  95. package/dist/resources/extensions/gsd/uat-run.js +9 -14
  96. package/dist/resources/extensions/gsd/unit-context-composer.js +40 -20
  97. package/dist/resources/extensions/gsd/unit-runtime.js +3 -2
  98. package/dist/resources/extensions/gsd/unit-tool-contracts.js +2 -1
  99. package/dist/resources/extensions/gsd/user-input-boundary.js +23 -0
  100. package/dist/resources/extensions/gsd/validation-block-guard.js +2 -0
  101. package/dist/resources/extensions/gsd/web-app-uat.js +80 -0
  102. package/dist/resources/extensions/gsd/workflow-mcp.js +15 -102
  103. package/dist/resources/extensions/gsd/workflow-reconcile.js +4 -3
  104. package/dist/resources/extensions/gsd/workflow-tool-surface.js +46 -0
  105. package/dist/resources/extensions/gsd/workspace-git-guard.js +2 -0
  106. package/dist/resources/extensions/gsd/worktree-state-projection.js +33 -4
  107. package/dist/resources/extensions/gsd/worktree-telemetry.js +12 -0
  108. package/dist/resources/extensions/shared/interview-ui.js +2 -2
  109. package/dist/resources/shared/claude-runtime-floor.js +182 -0
  110. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  111. package/dist/update-cmd.js +20 -0
  112. package/dist/web/standalone/.next/BUILD_ID +1 -1
  113. package/dist/web/standalone/.next/app-path-routes-manifest.json +7 -7
  114. package/dist/web/standalone/.next/build-manifest.json +3 -3
  115. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  116. package/dist/web/standalone/.next/react-loadable-manifest.json +8 -8
  117. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  118. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  119. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  120. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  121. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  122. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  123. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  124. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  125. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  126. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  127. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  128. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  129. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  130. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  131. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  132. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  133. package/dist/web/standalone/.next/server/app/index.html +1 -1
  134. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  135. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  136. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  137. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  138. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  139. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  140. package/dist/web/standalone/.next/server/app-paths-manifest.json +7 -7
  141. package/dist/web/standalone/.next/server/chunks/8357.js +1 -1
  142. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  143. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  144. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  145. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  146. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  147. package/dist/web/standalone/.next/static/chunks/2659.b7b129ee6a769448.js +1 -0
  148. package/dist/web/standalone/.next/static/chunks/2772.bfa657f49f955239.js +1 -0
  149. package/dist/web/standalone/.next/static/chunks/{3616.4113d484a994e411.js → 3616.3c60753b8ffcbd2e.js} +1 -1
  150. package/dist/web/standalone/.next/static/chunks/4283.e4873b058df143a1.js +2 -0
  151. package/dist/web/standalone/.next/static/chunks/5826.a46ecdd1cfe8dabc.js +1 -0
  152. package/dist/web/standalone/.next/static/chunks/796.cf859a427a2cb2ac.js +10 -0
  153. package/dist/web/standalone/.next/static/chunks/8785.2e5a118797fb2dd2.js +1 -0
  154. package/dist/web/standalone/.next/static/chunks/{webpack-dda80a1ef5587410.js → webpack-fbea77b5f9953368.js} +1 -1
  155. package/dist/web/standalone/node_modules/node-pty/build/Makefile +1 -1
  156. package/dist/web-mode.d.ts +2 -0
  157. package/dist/web-mode.js +20 -8
  158. package/package.json +2 -1
  159. package/packages/cloud-mcp-gateway/package.json +2 -2
  160. package/packages/contracts/package.json +1 -1
  161. package/packages/daemon/package.json +4 -4
  162. package/packages/gsd-agent-core/dist/session/agent-session-extensions.d.ts +2 -0
  163. package/packages/gsd-agent-core/dist/session/agent-session-extensions.d.ts.map +1 -1
  164. package/packages/gsd-agent-core/dist/session/agent-session-extensions.js +14 -0
  165. package/packages/gsd-agent-core/dist/session/agent-session-extensions.js.map +1 -1
  166. package/packages/gsd-agent-core/package.json +5 -5
  167. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  168. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js +106 -40
  169. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  170. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-extension-widgets.d.ts.map +1 -1
  171. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-extension-widgets.js +6 -0
  172. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-extension-widgets.js.map +1 -1
  173. package/packages/gsd-agent-modes/package.json +7 -7
  174. package/packages/mcp-server/dist/server.d.ts +10 -0
  175. package/packages/mcp-server/dist/server.d.ts.map +1 -1
  176. package/packages/mcp-server/dist/server.js +8 -0
  177. package/packages/mcp-server/dist/server.js.map +1 -1
  178. package/packages/mcp-server/dist/workflow-tools.d.ts +41 -0
  179. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  180. package/packages/mcp-server/dist/workflow-tools.js +2 -1
  181. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  182. package/packages/mcp-server/package.json +3 -3
  183. package/packages/native/package.json +1 -1
  184. package/packages/pi-agent-core/package.json +1 -1
  185. package/packages/pi-ai/dist/models.generated.d.ts +8 -93
  186. package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
  187. package/packages/pi-ai/dist/models.generated.js +35 -120
  188. package/packages/pi-ai/dist/models.generated.js.map +1 -1
  189. package/packages/pi-ai/package.json +1 -1
  190. package/packages/pi-coding-agent/package.json +7 -7
  191. package/packages/pi-tui/dist/components/input.js +1 -1
  192. package/packages/pi-tui/dist/components/input.js.map +1 -1
  193. package/packages/pi-tui/dist/keys.d.ts.map +1 -1
  194. package/packages/pi-tui/dist/keys.js +39 -30
  195. package/packages/pi-tui/dist/keys.js.map +1 -1
  196. package/packages/pi-tui/dist/stdin-buffer.d.ts.map +1 -1
  197. package/packages/pi-tui/dist/stdin-buffer.js +22 -0
  198. package/packages/pi-tui/dist/stdin-buffer.js.map +1 -1
  199. package/packages/pi-tui/package.json +2 -2
  200. package/packages/rpc-client/package.json +2 -2
  201. package/pkg/package.json +1 -1
  202. package/src/resources/extensions/ask-user-questions.ts +87 -24
  203. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +108 -281
  204. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +240 -0
  205. package/src/resources/extensions/claude-code-cli/turn-assembler.ts +287 -0
  206. package/src/resources/extensions/github-sync/templates.ts +3 -3
  207. package/src/resources/extensions/github-sync/tests/templates.test.ts +2 -2
  208. package/src/resources/extensions/gsd/artifact-projection.ts +31 -0
  209. package/src/resources/extensions/gsd/auto/contracts.ts +32 -2
  210. package/src/resources/extensions/gsd/auto/loop-deps.ts +2 -0
  211. package/src/resources/extensions/gsd/auto/loop.ts +83 -61
  212. package/src/resources/extensions/gsd/auto/orchestrator.ts +125 -12
  213. package/src/resources/extensions/gsd/auto/phases.ts +35 -3
  214. package/src/resources/extensions/gsd/auto/run-unit.ts +2 -1
  215. package/src/resources/extensions/gsd/auto/session.ts +4 -0
  216. package/src/resources/extensions/gsd/auto-dashboard.ts +18 -4
  217. package/src/resources/extensions/gsd/auto-dispatch.ts +20 -7
  218. package/src/resources/extensions/gsd/auto-model-selection.ts +8 -0
  219. package/src/resources/extensions/gsd/auto-post-unit.ts +4 -3
  220. package/src/resources/extensions/gsd/auto-prompts.ts +107 -9
  221. package/src/resources/extensions/gsd/auto-recovery.ts +50 -50
  222. package/src/resources/extensions/gsd/auto-runtime-state.ts +26 -0
  223. package/src/resources/extensions/gsd/auto-start.ts +17 -20
  224. package/src/resources/extensions/gsd/auto-timers.ts +16 -2
  225. package/src/resources/extensions/gsd/auto-tool-tracking.ts +35 -0
  226. package/src/resources/extensions/gsd/auto-unit-tool-scope.ts +9 -30
  227. package/src/resources/extensions/gsd/auto-verification.ts +7 -8
  228. package/src/resources/extensions/gsd/auto-worktree.ts +33 -26
  229. package/src/resources/extensions/gsd/auto.ts +15 -8
  230. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +29 -37
  231. package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +10 -37
  232. package/src/resources/extensions/gsd/bootstrap/query-tools.ts +2 -2
  233. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +116 -151
  234. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +107 -3
  235. package/src/resources/extensions/gsd/closeout-consistency-gate.ts +27 -5
  236. package/src/resources/extensions/gsd/codebase-generator.ts +9 -5
  237. package/src/resources/extensions/gsd/commands/handlers/auto.ts +3 -0
  238. package/src/resources/extensions/gsd/commands-handlers.ts +18 -0
  239. package/src/resources/extensions/gsd/commands-inspect.ts +7 -8
  240. package/src/resources/extensions/gsd/commands-maintenance.ts +74 -40
  241. package/src/resources/extensions/gsd/commands-ship.ts +2 -2
  242. package/src/resources/extensions/gsd/commands-verdict.ts +19 -2
  243. package/src/resources/extensions/gsd/db-workspace.ts +170 -0
  244. package/src/resources/extensions/gsd/delegation-policy.ts +3 -11
  245. package/src/resources/extensions/gsd/discussion-handoff.ts +276 -0
  246. package/src/resources/extensions/gsd/docs/preferences-reference.md +9 -0
  247. package/src/resources/extensions/gsd/doctor.ts +15 -5
  248. package/src/resources/extensions/gsd/error-classifier.ts +1 -1
  249. package/src/resources/extensions/gsd/git-conflict-state.ts +17 -1
  250. package/src/resources/extensions/gsd/gsd-db.ts +12 -0
  251. package/src/resources/extensions/gsd/guided-flow.ts +47 -558
  252. package/src/resources/extensions/gsd/guided-unit-completion.ts +275 -0
  253. package/src/resources/extensions/gsd/markdown-renderer.ts +2 -1
  254. package/src/resources/extensions/gsd/mcp-filter.ts +2 -1
  255. package/src/resources/extensions/gsd/mcp-tool-name.ts +35 -0
  256. package/src/resources/extensions/gsd/md-importer.ts +3 -3
  257. package/src/resources/extensions/gsd/migrate/safety.ts +2 -2
  258. package/src/resources/extensions/gsd/migration-auto-check.ts +2 -2
  259. package/src/resources/extensions/gsd/milestone-closeout-proof.ts +131 -0
  260. package/src/resources/extensions/gsd/milestone-closeout.ts +12 -4
  261. package/src/resources/extensions/gsd/milestone-merge-transaction.ts +47 -0
  262. package/src/resources/extensions/gsd/milestone-planning-persistence.ts +224 -0
  263. package/src/resources/extensions/gsd/milestone-readiness.ts +125 -0
  264. package/src/resources/extensions/gsd/milestone-settlement.ts +81 -0
  265. package/src/resources/extensions/gsd/milestone-validation-evidence.ts +95 -0
  266. package/src/resources/extensions/gsd/milestone-validation-verdict.ts +80 -0
  267. package/src/resources/extensions/gsd/parallel-eligibility.ts +4 -5
  268. package/src/resources/extensions/gsd/parallel-orchestrator.ts +6 -2
  269. package/src/resources/extensions/gsd/preferences-diagnostics.ts +98 -0
  270. package/src/resources/extensions/gsd/preferences-types.ts +16 -0
  271. package/src/resources/extensions/gsd/preferences.ts +173 -28
  272. package/src/resources/extensions/gsd/prompts/complete-slice.md +1 -0
  273. package/src/resources/extensions/gsd/prompts/execute-task.md +2 -0
  274. package/src/resources/extensions/gsd/prompts/guided-discuss-project.md +3 -1
  275. package/src/resources/extensions/gsd/prompts/plan-milestone.md +2 -0
  276. package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -0
  277. package/src/resources/extensions/gsd/prompts/refine-slice.md +1 -0
  278. package/src/resources/extensions/gsd/prompts/system.md +1 -1
  279. package/src/resources/extensions/gsd/provider-payload-policy.ts +140 -0
  280. package/src/resources/extensions/gsd/pull-request-process.ts +41 -0
  281. package/src/resources/extensions/gsd/quality-gate-closure.ts +140 -0
  282. package/src/resources/extensions/gsd/question-transport.ts +138 -0
  283. package/src/resources/extensions/gsd/roadmap-slices.ts +8 -2
  284. package/src/resources/extensions/gsd/slice-parallel-orchestrator.ts +6 -2
  285. package/src/resources/extensions/gsd/state.ts +15 -5
  286. package/src/resources/extensions/gsd/templates/plan.md +7 -0
  287. package/src/resources/extensions/gsd/templates/project.md +1 -0
  288. package/src/resources/extensions/gsd/templates/roadmap.md +1 -1
  289. package/src/resources/extensions/gsd/templates/uat.md +5 -1
  290. package/src/resources/extensions/gsd/tests/ask-user-questions-render.test.ts +92 -0
  291. package/src/resources/extensions/gsd/tests/auto-dashboard.test.ts +29 -1
  292. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +321 -5
  293. package/src/resources/extensions/gsd/tests/auto-milestone-target.test.ts +23 -0
  294. package/src/resources/extensions/gsd/tests/auto-model-selection-tool-poisoning.test.ts +18 -0
  295. package/src/resources/extensions/gsd/tests/auto-orchestrator.test.ts +133 -4
  296. package/src/resources/extensions/gsd/tests/auto-runtime-state.test.ts +34 -0
  297. package/src/resources/extensions/gsd/tests/canonical-milestone-root.test.ts +20 -0
  298. package/src/resources/extensions/gsd/tests/codebase-generator.test.ts +22 -0
  299. package/src/resources/extensions/gsd/tests/commands-dispatcher-workspace-git.test.ts +11 -0
  300. package/src/resources/extensions/gsd/tests/commands-verdict.test.ts +38 -1
  301. package/src/resources/extensions/gsd/tests/dispatch-complete-milestone-guard.test.ts +34 -3
  302. package/src/resources/extensions/gsd/tests/dispatch-run-uat-browser-tools.test.ts +88 -0
  303. package/src/resources/extensions/gsd/tests/doctor-scope-db-unavailable.test.ts +18 -0
  304. package/src/resources/extensions/gsd/tests/execute-task-rendering.test.ts +1 -0
  305. package/src/resources/extensions/gsd/tests/fixtures/pr-body/swarm-lane-no-blockers.md +1 -5
  306. package/src/resources/extensions/gsd/tests/fixtures/pr-body/swarm-lane-with-blockers.md +1 -5
  307. package/src/resources/extensions/gsd/tests/gate-state-canonicalization.test.ts +48 -1
  308. package/src/resources/extensions/gsd/tests/mcp-tool-name.test.ts +34 -0
  309. package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +58 -0
  310. package/src/resources/extensions/gsd/tests/milestone-closeout-proof.test.ts +99 -0
  311. package/src/resources/extensions/gsd/tests/milestone-closeout.test.ts +25 -0
  312. package/src/resources/extensions/gsd/tests/milestone-merge-transaction.test.ts +46 -0
  313. package/src/resources/extensions/gsd/tests/milestone-readiness.test.ts +65 -0
  314. package/src/resources/extensions/gsd/tests/milestone-validation-evidence.test.ts +41 -0
  315. package/src/resources/extensions/gsd/tests/milestone-validation-verdict.test.ts +55 -0
  316. package/src/resources/extensions/gsd/tests/plan-milestone.test.ts +45 -0
  317. package/src/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +2 -0
  318. package/src/resources/extensions/gsd/tests/planning-crossval.test.ts +45 -0
  319. package/src/resources/extensions/gsd/tests/preferences-diagnostics.test.ts +67 -0
  320. package/src/resources/extensions/gsd/tests/preferences.test.ts +183 -0
  321. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +46 -0
  322. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +9 -0
  323. package/src/resources/extensions/gsd/tests/provider-payload-policy.test.ts +165 -0
  324. package/src/resources/extensions/gsd/tests/pull-request-process.test.ts +47 -0
  325. package/src/resources/extensions/gsd/tests/register-hooks-depth-verification.test.ts +94 -0
  326. package/src/resources/extensions/gsd/tests/roadmap-parse-regression.test.ts +40 -0
  327. package/src/resources/extensions/gsd/tests/runtime-invariant-modules.test.ts +25 -1
  328. package/src/resources/extensions/gsd/tests/session-start-footer.test.ts +80 -0
  329. package/src/resources/extensions/gsd/tests/single-writer-invariant.test.ts +101 -1
  330. package/src/resources/extensions/gsd/tests/stale-queued-milestone.test.ts +27 -0
  331. package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +2 -1
  332. package/src/resources/extensions/gsd/tests/tool-availability-audit.test.ts +35 -0
  333. package/src/resources/extensions/gsd/tests/tool-naming.test.ts +35 -42
  334. package/src/resources/extensions/gsd/tests/uat-policy.test.ts +23 -0
  335. package/src/resources/extensions/gsd/tests/unit-context-composer.test.ts +47 -0
  336. package/src/resources/extensions/gsd/tests/user-input-boundary.test.ts +86 -1
  337. package/src/resources/extensions/gsd/tests/validate-milestone-stuck-guard.test.ts +39 -0
  338. package/src/resources/extensions/gsd/tests/web-app-uat.test.ts +150 -0
  339. package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +126 -9
  340. package/src/resources/extensions/gsd/tests/workspace-git-preflight.test.ts +15 -0
  341. package/src/resources/extensions/gsd/tests/worktree-manager.test.ts +21 -0
  342. package/src/resources/extensions/gsd/tests/worktree-projection-writers.test.ts +1 -1
  343. package/src/resources/extensions/gsd/tests/worktree-telemetry.test.ts +22 -0
  344. package/src/resources/extensions/gsd/tests/write-gate.test.ts +79 -0
  345. package/src/resources/extensions/gsd/tool-contract.ts +86 -8
  346. package/src/resources/extensions/gsd/tool-presentation-plan.ts +16 -33
  347. package/src/resources/extensions/gsd/tool-surface-snapshot.ts +47 -0
  348. package/src/resources/extensions/gsd/tools/plan-milestone.ts +19 -160
  349. package/src/resources/extensions/gsd/tools/reassess-roadmap.ts +43 -0
  350. package/src/resources/extensions/gsd/tools/validate-milestone.ts +25 -84
  351. package/src/resources/extensions/gsd/uat-policy.ts +19 -10
  352. package/src/resources/extensions/gsd/uat-run.ts +10 -14
  353. package/src/resources/extensions/gsd/unit-context-composer.ts +85 -20
  354. package/src/resources/extensions/gsd/unit-runtime.ts +3 -2
  355. package/src/resources/extensions/gsd/unit-tool-contracts.ts +2 -1
  356. package/src/resources/extensions/gsd/user-input-boundary.ts +18 -0
  357. package/src/resources/extensions/gsd/validation-block-guard.ts +2 -0
  358. package/src/resources/extensions/gsd/web-app-uat.ts +101 -0
  359. package/src/resources/extensions/gsd/workflow-mcp.ts +22 -110
  360. package/src/resources/extensions/gsd/workflow-reconcile.ts +3 -3
  361. package/src/resources/extensions/gsd/workflow-tool-surface.ts +73 -0
  362. package/src/resources/extensions/gsd/workspace-git-guard.ts +1 -0
  363. package/src/resources/extensions/gsd/worktree-lifecycle.ts +7 -16
  364. package/src/resources/extensions/gsd/worktree-state-projection.ts +55 -7
  365. package/src/resources/extensions/gsd/worktree-telemetry.ts +16 -0
  366. package/src/resources/extensions/shared/interview-ui.ts +15 -2
  367. package/src/resources/shared/claude-runtime-floor.ts +248 -0
  368. package/dist/web/standalone/.next/static/chunks/2659.feb6499ca863ebfc.js +0 -1
  369. package/dist/web/standalone/.next/static/chunks/2772.151789db0edea835.js +0 -1
  370. package/dist/web/standalone/.next/static/chunks/4283.10a065467b5340d8.js +0 -2
  371. package/dist/web/standalone/.next/static/chunks/5826.960dc4634cc9b0d3.js +0 -1
  372. package/dist/web/standalone/.next/static/chunks/796.46f811c0fac23aab.js +0 -10
  373. package/dist/web/standalone/.next/static/chunks/8785.d32f7a61f55c1600.js +0 -1
  374. /package/dist/web/standalone/.next/static/{Qbr81pQ-pbQXP4bq2VXLv → 3PtrU9qGPEXwNLWkIyiqk}/_buildManifest.js +0 -0
  375. /package/dist/web/standalone/.next/static/{Qbr81pQ-pbQXP4bq2VXLv → 3PtrU9qGPEXwNLWkIyiqk}/_ssgManifest.js +0 -0
@@ -1,7 +1,10 @@
1
1
  // Project/App: gsd-pi
2
2
  // File Purpose: Resolve phase-aware tool surfaces for GSD model presentations.
3
3
  import { RUN_UAT_BROWSER_TOOL_NAMES, RUN_UAT_READ_ONLY_TOOL_NAMES, RUN_UAT_TOOL_PRESENTATION_PLAN_ID, RUN_UAT_WORKFLOW_TOOL_NAMES, } from "./unit-tool-contracts.js";
4
+ import { toMcpToolName } from "./mcp-tool-name.js";
5
+ import { createToolSurfaceSnapshot } from "./tool-surface-snapshot.js";
4
6
  import { uatTypeIncludesBrowser } from "./uat-policy.js";
7
+ import { canonicalWorkflowSurfaceToolName } from "./workflow-tool-surface.js";
5
8
  export { RUN_UAT_BROWSER_TOOL_NAMES, RUN_UAT_READ_ONLY_TOOL_NAMES, RUN_UAT_TOOL_PRESENTATION_PLAN_ID, RUN_UAT_WORKFLOW_TOOL_NAMES, } from "./unit-tool-contracts.js";
6
9
  export const RUN_UAT_FORBIDDEN_TOOL_NAMES = [
7
10
  "edit",
@@ -21,43 +24,12 @@ export const RUN_UAT_CLAUDE_NATIVE_TOOL_NAMES = [
21
24
  "Glob",
22
25
  "Grep",
23
26
  ];
24
- const WORKFLOW_ALIAS_TO_CANONICAL = {
25
- gsd_save_decision: "gsd_decision_save",
26
- gsd_update_requirement: "gsd_requirement_update",
27
- gsd_save_requirement: "gsd_requirement_save",
28
- gsd_save_summary: "gsd_summary_save",
29
- gsd_generate_milestone_id: "gsd_milestone_generate_id",
30
- gsd_milestone_plan: "gsd_plan_milestone",
31
- gsd_slice_plan: "gsd_plan_slice",
32
- gsd_task_plan: "gsd_plan_task",
33
- gsd_slice_replan: "gsd_replan_slice",
34
- gsd_complete_slice: "gsd_slice_complete",
35
- gsd_milestone_complete: "gsd_complete_milestone",
36
- gsd_milestone_validate: "gsd_validate_milestone",
37
- gsd_roadmap_reassess: "gsd_reassess_roadmap",
38
- gsd_complete_task: "gsd_task_complete",
39
- gsd_reopen_task: "gsd_task_reopen",
40
- gsd_reopen_slice: "gsd_slice_reopen",
41
- gsd_reopen_milestone: "gsd_milestone_reopen",
42
- };
43
27
  export function canonicalWorkflowToolName(toolName) {
44
- const mcp = parseMcpToolName(toolName);
45
- const baseName = mcp?.tool ?? toolName;
46
- return WORKFLOW_ALIAS_TO_CANONICAL[baseName] ?? baseName;
47
- }
48
- export function parseMcpToolName(toolName) {
49
- if (!toolName.startsWith("mcp__"))
50
- return null;
51
- const toolSeparator = toolName.indexOf("__", "mcp__".length);
52
- if (toolSeparator < 0)
53
- return null;
54
- return {
55
- server: toolName.slice("mcp__".length, toolSeparator),
56
- tool: toolName.slice(toolSeparator + 2),
57
- };
28
+ return canonicalWorkflowSurfaceToolName(toolName);
58
29
  }
30
+ export { parseMcpToolName } from "./mcp-tool-name.js";
59
31
  export function toWorkflowMcpToolName(serverName, toolName) {
60
- return `mcp__${serverName}__${canonicalWorkflowToolName(toolName)}`;
32
+ return toMcpToolName(serverName, canonicalWorkflowToolName(toolName));
61
33
  }
62
34
  function dedupe(values) {
63
35
  return [...new Set(values)];
@@ -134,6 +106,14 @@ export function resolveToolPresentationPlan(options) {
134
106
  ? toWorkflowMcpToolName(workflowServerName, name)
135
107
  : name)
136
108
  : allowedToolNames;
109
+ const toolSurface = createToolSurfaceSnapshot({
110
+ source: "presentation-plan",
111
+ phase: options.phase,
112
+ modelFacingToolNames: options.availableToolNames ?? requested,
113
+ registeredToolNames: options.availableToolNames ?? requested,
114
+ scopedToolNames: allowedToolNames,
115
+ presentedToolNames,
116
+ });
137
117
  if (options.phase === "run-uat") {
138
118
  for (const forbidden of RUN_UAT_FORBIDDEN_TOOL_NAMES) {
139
119
  addBlockedTool(blockedToolNames, forbidden, "forbidden during run-uat");
@@ -148,5 +128,6 @@ export function resolveToolPresentationPlan(options) {
148
128
  blockedToolNames,
149
129
  aliases,
150
130
  diagnostics: [],
131
+ toolSurface,
151
132
  };
152
133
  }
@@ -0,0 +1,17 @@
1
+ // Project/App: gsd-pi
2
+ // File Purpose: Typed snapshots for model-facing, registered, scoped, and presented tool surfaces.
3
+ function dedupeToolNames(toolNames) {
4
+ return [...new Set(toolNames ?? [])];
5
+ }
6
+ export function createToolSurfaceSnapshot(input) {
7
+ return {
8
+ source: input.source,
9
+ unitType: input.unitType,
10
+ phase: input.phase,
11
+ modelFacingToolNames: dedupeToolNames(input.modelFacingToolNames),
12
+ registeredToolNames: dedupeToolNames(input.registeredToolNames),
13
+ scopedToolNames: dedupeToolNames(input.scopedToolNames),
14
+ presentedToolNames: dedupeToolNames(input.presentedToolNames),
15
+ capturedAt: input.capturedAt ?? Date.now(),
16
+ };
17
+ }
@@ -1,15 +1,7 @@
1
1
  // Project/App: gsd-pi
2
2
  // File Purpose: Plans milestone roadmap state through DB-backed workflow tools.
3
- import { clearParseCache } from "../files.js";
4
- import { isClosedStatus } from "../status-guards.js";
5
3
  import { isNonEmptyString, validateStringArray, validateTitle } from "../validation.js";
6
- import { transaction, getMilestone, getMilestoneSlices, getSlice, insertMilestone, insertSlice, upsertMilestonePlanning, upsertSlicePlanning, } from "../gsd-db.js";
7
- import { invalidateStateCache } from "../state.js";
8
- import { renderRoadmapFromDb } from "../markdown-renderer.js";
9
- import { renderAllProjections } from "../workflow-projections.js";
10
- import { writeManifest } from "../workflow-manifest.js";
11
- import { appendEvent } from "../workflow-events.js";
12
- import { logWarning } from "../workflow-logger.js";
4
+ import { persistMilestonePlan } from "../milestone-planning-persistence.js";
13
5
  function validateRiskEntries(value) {
14
6
  if (!Array.isArray(value)) {
15
7
  throw new Error("keyRisks must be an array");
@@ -43,10 +35,16 @@ function validateProofStrategy(value) {
43
35
  return { riskOrUnknown, retireIn, whatWillBeProven };
44
36
  });
45
37
  }
38
+ const SLICE_ID_RE = /^[A-Za-z0-9][A-Za-z0-9-]*$/;
46
39
  function validateSlices(value) {
47
40
  if (!Array.isArray(value) || value.length === 0) {
48
41
  throw new Error("slices must be a non-empty array");
49
42
  }
43
+ // Pre-collect all slice IDs so depends cross-validation can reference the full set.
44
+ const allSliceIds = new Set(value
45
+ .filter((e) => !!e && typeof e === "object")
46
+ .map(e => e.sliceId)
47
+ .filter((id) => isNonEmptyString(id)));
50
48
  const seen = new Set();
51
49
  return value.map((entry, index) => {
52
50
  if (!entry || typeof entry !== "object") {
@@ -84,8 +82,13 @@ function validateSlices(value) {
84
82
  throw new Error(`slices[${index}].title is invalid: ${titleIssue}`);
85
83
  if (!isNonEmptyString(risk))
86
84
  throw new Error(`slices[${index}].risk must be a non-empty string`);
87
- if (!Array.isArray(depends) || depends.some((item) => !isNonEmptyString(item))) {
88
- throw new Error(`slices[${index}].depends must be an array of non-empty strings`);
85
+ if (!Array.isArray(depends) || depends.some((item) => !isNonEmptyString(item) || !SLICE_ID_RE.test(item))) {
86
+ throw new Error(`slices[${index}].depends must be an array of valid slice IDs (e.g. "S01")`);
87
+ }
88
+ for (const dep of depends) {
89
+ if (!allSliceIds.has(dep)) {
90
+ throw new Error(`slices[${index}].depends references unknown slice "${dep}" — check that it is defined in the same milestone`);
91
+ }
89
92
  }
90
93
  if (!isNonEmptyString(demo))
91
94
  throw new Error(`slices[${index}].demo must be a non-empty string`);
@@ -160,136 +163,5 @@ export async function handlePlanMilestone(rawParams, basePath) {
160
163
  catch (err) {
161
164
  return { error: `validation failed: ${err.message}` };
162
165
  }
163
- // ── Guards + DB writes inside a single transaction (prevents TOCTOU) ───
164
- // Guards must be inside the transaction so the state they check cannot
165
- // change between the read and the write (#2723).
166
- let guardError = null;
167
- try {
168
- transaction(() => {
169
- const existingMilestone = getMilestone(params.milestoneId);
170
- if (existingMilestone && isClosedStatus(existingMilestone.status)) {
171
- guardError = `cannot re-plan milestone ${params.milestoneId}: it is already complete`;
172
- return;
173
- }
174
- // Guard: refuse to re-plan a milestone that would drop completed slices (#2960).
175
- // Allow re-planning when all completed slices are still present in the
176
- // incoming plan — their status is preserved below (#2558). Block only when
177
- // the new plan omits a completed slice, which could shadow completed work.
178
- const existingSlices = getMilestoneSlices(params.milestoneId);
179
- const completedSlices = existingSlices.filter(s => isClosedStatus(s.status));
180
- if (completedSlices.length > 0) {
181
- const incomingSliceIds = new Set(params.slices.map(s => s.sliceId));
182
- const droppedCompleted = completedSlices.filter(s => !incomingSliceIds.has(s.id));
183
- if (droppedCompleted.length > 0) {
184
- guardError = `cannot re-plan milestone ${params.milestoneId}: ${droppedCompleted.length} completed slice(s) would be dropped (${droppedCompleted.map(s => s.id).join(", ")}). Use gsd_reassess_roadmap to modify the roadmap.`;
185
- return;
186
- }
187
- }
188
- // Validate depends_on: all dependencies must exist and be complete
189
- if (params.dependsOn && params.dependsOn.length > 0) {
190
- for (const depId of params.dependsOn) {
191
- const dep = getMilestone(depId);
192
- if (!dep) {
193
- guardError = `depends_on references unknown milestone: ${depId}`;
194
- return;
195
- }
196
- if (!isClosedStatus(dep.status)) {
197
- guardError = `depends_on milestone ${depId} is not yet complete (status: ${dep.status})`;
198
- return;
199
- }
200
- }
201
- }
202
- insertMilestone({
203
- id: params.milestoneId,
204
- title: params.title,
205
- status: params.status ?? "active",
206
- depends_on: params.dependsOn ?? [],
207
- });
208
- upsertMilestonePlanning(params.milestoneId, {
209
- title: params.title,
210
- status: params.status ?? "active",
211
- depends_on: params.dependsOn ?? [],
212
- vision: params.vision,
213
- successCriteria: params.successCriteria,
214
- keyRisks: params.keyRisks,
215
- proofStrategy: params.proofStrategy,
216
- verificationContract: params.verificationContract,
217
- verificationIntegration: params.verificationIntegration,
218
- verificationOperational: params.verificationOperational,
219
- verificationUat: params.verificationUat,
220
- definitionOfDone: params.definitionOfDone,
221
- requirementCoverage: params.requirementCoverage,
222
- boundaryMapMarkdown: params.boundaryMapMarkdown,
223
- });
224
- for (let i = 0; i < params.slices.length; i++) {
225
- const slice = params.slices[i];
226
- // Preserve completed/done status on re-plan (#2558).
227
- // Without this, a re-plan after milestone transition would reset
228
- // already-completed slices back to "pending".
229
- const existing = getSlice(params.milestoneId, slice.sliceId);
230
- const status = existing && (existing.status === "complete" || existing.status === "done")
231
- ? existing.status
232
- : "pending";
233
- insertSlice({
234
- id: slice.sliceId,
235
- milestoneId: params.milestoneId,
236
- title: slice.title,
237
- status,
238
- risk: slice.risk,
239
- depends: slice.depends,
240
- demo: slice.demo,
241
- sequence: i + 1, // Preserve agent-ordered sequence (#3356)
242
- // ADR-011: pass undefined through so ON CONFLICT preserves existing values
243
- // when the caller omitted the fields on a re-plan.
244
- isSketch: slice.isSketch,
245
- sketchScope: slice.sketchScope,
246
- });
247
- upsertSlicePlanning(params.milestoneId, slice.sliceId, {
248
- goal: slice.goal,
249
- successCriteria: slice.successCriteria,
250
- proofLevel: slice.proofLevel,
251
- integrationClosure: slice.integrationClosure,
252
- observabilityImpact: slice.observabilityImpact,
253
- });
254
- }
255
- });
256
- }
257
- catch (err) {
258
- return { error: `db write failed: ${err.message}` };
259
- }
260
- if (guardError) {
261
- return { error: guardError };
262
- }
263
- let roadmapPath;
264
- try {
265
- const renderResult = await renderRoadmapFromDb(basePath, params.milestoneId);
266
- roadmapPath = renderResult.roadmapPath;
267
- }
268
- catch (renderErr) {
269
- logWarning("tool", `plan_milestone — render failed (DB rows preserved for debugging): ${renderErr.message}`);
270
- invalidateStateCache();
271
- return { error: `render failed: ${renderErr.message}` };
272
- }
273
- invalidateStateCache();
274
- clearParseCache();
275
- // ── Post-mutation hook: projections, manifest, event log ───────────────
276
- try {
277
- await renderAllProjections(basePath, params.milestoneId);
278
- writeManifest(basePath);
279
- appendEvent(basePath, {
280
- cmd: "plan-milestone",
281
- params: { milestoneId: params.milestoneId },
282
- ts: new Date().toISOString(),
283
- actor: "agent",
284
- actor_name: params.actorName,
285
- trigger_reason: params.triggerReason,
286
- });
287
- }
288
- catch (hookErr) {
289
- logWarning("tool", `plan-milestone post-mutation hook warning: ${hookErr.message}`);
290
- }
291
- return {
292
- milestoneId: params.milestoneId,
293
- roadmapPath,
294
- };
166
+ return persistMilestonePlan(params, basePath);
295
167
  }
@@ -31,6 +31,7 @@ function validateParams(params) {
31
31
  if (!Array.isArray(params.sliceChanges.removed)) {
32
32
  throw new Error("sliceChanges.removed must be an array");
33
33
  }
34
+ const SLICE_ID_RE = /^[A-Za-z0-9][A-Za-z0-9-]*$/;
34
35
  // Validate each modified slice
35
36
  for (let i = 0; i < params.sliceChanges.modified.length; i++) {
36
37
  const s = params.sliceChanges.modified[i];
@@ -40,6 +41,11 @@ function validateParams(params) {
40
41
  throw new Error(`sliceChanges.modified[${i}].sliceId is required`);
41
42
  if (!isNonEmptyString(s.title))
42
43
  throw new Error(`sliceChanges.modified[${i}].title is required`);
44
+ if (s.depends !== undefined) {
45
+ if (!Array.isArray(s.depends) || s.depends.some((item) => !isNonEmptyString(item) || !SLICE_ID_RE.test(item))) {
46
+ throw new Error(`sliceChanges.modified[${i}].depends must be an array of valid slice IDs (e.g. "S01")`);
47
+ }
48
+ }
43
49
  }
44
50
  // Validate each added slice
45
51
  for (let i = 0; i < params.sliceChanges.added.length; i++) {
@@ -50,6 +56,11 @@ function validateParams(params) {
50
56
  throw new Error(`sliceChanges.added[${i}].sliceId is required`);
51
57
  if (!isNonEmptyString(s.title))
52
58
  throw new Error(`sliceChanges.added[${i}].title is required`);
59
+ if (s.depends !== undefined) {
60
+ if (!Array.isArray(s.depends) || s.depends.some((item) => !isNonEmptyString(item) || !SLICE_ID_RE.test(item))) {
61
+ throw new Error(`sliceChanges.added[${i}].depends must be an array of valid slice IDs (e.g. "S01")`);
62
+ }
63
+ }
53
64
  }
54
65
  return params;
55
66
  }
@@ -111,6 +122,34 @@ export async function handleReassessRoadmap(rawParams, basePath) {
111
122
  return;
112
123
  }
113
124
  }
125
+ // Cross-milestone depends validation — effective slice ID set after this reassessment
126
+ const removedIds = new Set(params.sliceChanges.removed);
127
+ const effectiveSliceIds = new Set(existingSlices.map(s => s.id).filter(id => !removedIds.has(id)));
128
+ for (const added of params.sliceChanges.added) {
129
+ effectiveSliceIds.add(added.sliceId);
130
+ }
131
+ for (let i = 0; i < params.sliceChanges.modified.length; i++) {
132
+ const mod = params.sliceChanges.modified[i];
133
+ if (mod.depends !== undefined) {
134
+ for (const dep of mod.depends) {
135
+ if (!effectiveSliceIds.has(dep)) {
136
+ guardError = `sliceChanges.modified[${i}].depends references unknown slice "${dep}" — check that it is defined in this milestone`;
137
+ return;
138
+ }
139
+ }
140
+ }
141
+ }
142
+ for (let i = 0; i < params.sliceChanges.added.length; i++) {
143
+ const added = params.sliceChanges.added[i];
144
+ if (added.depends !== undefined) {
145
+ for (const dep of added.depends) {
146
+ if (!effectiveSliceIds.has(dep)) {
147
+ guardError = `sliceChanges.added[${i}].depends references unknown slice "${dep}" — check that it is defined in this milestone`;
148
+ return;
149
+ }
150
+ }
151
+ }
152
+ }
114
153
  // Record assessment
115
154
  insertAssessment({
116
155
  path: assessmentRelPath,
@@ -11,10 +11,11 @@
11
11
  * despite passing validation.
12
12
  */
13
13
  import { join } from "node:path";
14
- import { transaction, insertAssessment, getMilestoneSlices, getMilestone, getArtifact, } from "../gsd-db.js";
15
- import { gsdProjectionRoot, clearPathCache, resolveSliceFile } from "../paths.js";
14
+ import { transaction, insertAssessment, getMilestoneSlices, getMilestone, } from "../gsd-db.js";
15
+ import { gsdProjectionRoot, clearPathCache } from "../paths.js";
16
16
  import { resolveCanonicalMilestoneRoot } from "../worktree-manager.js";
17
- import { saveFile, clearParseCache, loadFile } from "../files.js";
17
+ import { resolveWorktreeProjectRoot } from "../worktree-root.js";
18
+ import { saveFile, clearParseCache } from "../files.js";
18
19
  import { invalidateStateCache } from "../state.js";
19
20
  import { VALIDATION_VERDICTS, isValidMilestoneVerdict } from "../verdict-parser.js";
20
21
  import { insertMilestoneValidationGates } from "../milestone-validation-gates.js";
@@ -22,7 +23,7 @@ import { logWarning } from "../workflow-logger.js";
22
23
  import { UokGateRunner } from "../uok/gate-runner.js";
23
24
  import { loadEffectiveGSDPreferences } from "../preferences.js";
24
25
  import { resolveUokFlags } from "../uok/flags.js";
25
- import { compactTextParts, hasBrowserEvidenceText, hasBrowserRequiredText } from "../browser-evidence.js";
26
+ import { applyBrowserEvidenceGate, browserEvidenceGateRequiresAttention, } from "../milestone-validation-evidence.js";
26
27
  function isVerificationNotApplicable(value) {
27
28
  const v = (value ?? "").toLowerCase().trim().replace(/[.\s]+$/, "");
28
29
  if (!v || v === "none")
@@ -44,80 +45,6 @@ function getRequiredVerificationClasses(milestoneId) {
44
45
  required.push("UAT");
45
46
  return required;
46
47
  }
47
- function hasRuntimeExecutableUatEvidenceText(text) {
48
- if (!/\buatType:\s*runtime-executable\b/i.test(text))
49
- return false;
50
- if (!/\bverdict:\s*PASS\b/i.test(text))
51
- return false;
52
- return /^\|\s*[^|\n]+\s*\|\s*runtime\s*\|\s*PASS\s*\|[^|\n]*\bgsd_uat_exec\b/mi.test(text);
53
- }
54
- async function browserEvidenceGateRequiresAttention(params, basePath) {
55
- if (params.verdict !== "pass")
56
- return false;
57
- const milestone = getMilestone(params.milestoneId);
58
- const slices = getMilestoneSlices(params.milestoneId);
59
- const requirementText = compactTextParts([
60
- milestone?.vision,
61
- milestone?.success_criteria,
62
- milestone?.verification_uat,
63
- params.successCriteriaChecklist,
64
- params.verificationClasses,
65
- ...slices.flatMap((slice) => [
66
- slice.demo,
67
- slice.goal,
68
- slice.success_criteria,
69
- ]),
70
- ]);
71
- if (!hasBrowserRequiredText(requirementText))
72
- return false;
73
- // Collect per-slice evidence so the runtime bypass is checked independently
74
- // for each slice. Concatenating all slices before checking would allow runtime
75
- // evidence from one slice to cover another slice's browser requirements.
76
- const sliceEvidencePairs = [];
77
- for (const slice of slices) {
78
- const chunks = [];
79
- const artifactPath = `milestones/${params.milestoneId}/slices/${slice.id}/${slice.id}-ASSESSMENT.md`;
80
- const artifact = getArtifact(artifactPath);
81
- if (artifact?.full_content)
82
- chunks.push(artifact.full_content);
83
- const assessmentPath = resolveSliceFile(basePath, params.milestoneId, slice.id, "ASSESSMENT");
84
- const assessmentContent = assessmentPath ? await loadFile(assessmentPath) : null;
85
- if (assessmentContent)
86
- chunks.push(assessmentContent);
87
- sliceEvidencePairs.push({
88
- sliceRequirementText: compactTextParts([slice.demo, slice.goal, slice.success_criteria]),
89
- evidenceText: chunks.join("\n\n"),
90
- });
91
- }
92
- const persistedEvidence = sliceEvidencePairs.map((s) => s.evidenceText).join("\n\n");
93
- // Runtime bypass: each slice whose own requirement text has browser-observable
94
- // criteria must have its own runtime-executable UAT evidence. When no individual
95
- // slice has slice-level browser requirements (e.g., they come from milestone-level
96
- // fields only), fall back to checking whether any slice has runtime evidence.
97
- const browserRequiringSlices = sliceEvidencePairs.filter((s) => hasBrowserRequiredText(s.sliceRequirementText));
98
- const runtimeBypasses = browserRequiringSlices.length > 0
99
- ? browserRequiringSlices.every((s) => hasRuntimeExecutableUatEvidenceText(s.evidenceText))
100
- : sliceEvidencePairs.some((s) => hasRuntimeExecutableUatEvidenceText(s.evidenceText));
101
- if (runtimeBypasses)
102
- return false;
103
- const validationEvidence = compactTextParts([
104
- params.successCriteriaChecklist,
105
- params.verificationClasses,
106
- params.verdictRationale,
107
- params.remediationPlan,
108
- ]);
109
- return !hasBrowserEvidenceText(`${persistedEvidence}\n\n${validationEvidence}`);
110
- }
111
- function applyBrowserEvidenceGate(params) {
112
- const note = "Browser evidence gate: Browser-observable acceptance criteria were detected, but no persisted ASSESSMENT or validation evidence recorded browser actions with assertions. Downgraded from pass to needs-attention.";
113
- return {
114
- ...params,
115
- verdict: "needs-attention",
116
- verdictRationale: params.verdictRationale.trim()
117
- ? `${params.verdictRationale.trim()}\n\n${note}`
118
- : note,
119
- };
120
- }
121
48
  function renderValidationMarkdown(params) {
122
49
  let md = `---
123
50
  verdict: ${params.verdict}
@@ -214,6 +141,16 @@ export async function handleValidateMilestone(params, basePath, opts) {
214
141
  let projectionStale = false;
215
142
  try {
216
143
  await saveFile(validationPath, validationMd);
144
+ const projectRoot = resolveWorktreeProjectRoot(basePath);
145
+ if (projectRoot !== artifactBasePath) {
146
+ const projectValidationPath = join(gsdProjectionRoot(projectRoot), "milestones", effectiveParams.milestoneId, `${effectiveParams.milestoneId}-VALIDATION.md`);
147
+ try {
148
+ await saveFile(projectValidationPath, validationMd);
149
+ }
150
+ catch (mirrorErr) {
151
+ logWarning("projection", `validate_milestone project-root VALIDATION mirror failed for ${effectiveParams.milestoneId}`, { error: mirrorErr.message });
152
+ }
153
+ }
217
154
  }
218
155
  catch (renderErr) {
219
156
  projectionStale = true;
@@ -2,6 +2,7 @@
2
2
  // File Purpose: Central UAT mode policy for dispatch, tool presentation, and result validation.
3
3
  import { extractUatType } from "./files.js";
4
4
  import { hasBrowserRequiredText } from "./browser-evidence.js";
5
+ import { parseMcpToolName } from "./mcp-tool-name.js";
5
6
  export const UAT_TYPES = [
6
7
  "artifact-driven",
7
8
  "browser-executable",
@@ -80,26 +81,31 @@ export function shouldDispatchUatForContent(content, prefs) {
80
81
  export function uatTypeIncludesBrowser(uatType) {
81
82
  return isUatType(uatType) && UAT_MODE_POLICIES[uatType].browserTools;
82
83
  }
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
84
  export function isUatBrowserToolName(toolName) {
90
- return canonicalPresentedToolName(toolName).startsWith("browser_");
85
+ const parsed = parseMcpToolName(toolName);
86
+ const canonicalName = parsed?.toolName ?? toolName;
87
+ if (canonicalName.startsWith("browser_"))
88
+ return true;
89
+ return parsed?.toolName === "*" && parsed.serverName.toLowerCase().includes("browser");
91
90
  }
92
91
  export function hasUatBrowserToolSurface(activeTools) {
93
92
  return Array.isArray(activeTools) && activeTools.some(isUatBrowserToolName);
94
93
  }
94
+ export function resolveUatBrowserToolSurface(options) {
95
+ const surfaces = [options.activeTools, options.registeredTools].filter(Array.isArray);
96
+ if (surfaces.length === 0)
97
+ return undefined;
98
+ return [...new Set(surfaces.flat())];
99
+ }
95
100
  export function getUatBrowserToolSupportError(options) {
96
101
  if (!uatTypeIncludesBrowser(options.uatType))
97
102
  return null;
98
- if (!Array.isArray(options.activeTools))
103
+ const toolSurface = resolveUatBrowserToolSurface(options);
104
+ if (!toolSurface)
99
105
  return null;
100
- if (hasUatBrowserToolSurface(options.activeTools))
106
+ if (hasUatBrowserToolSurface(toolSurface))
101
107
  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.`;
108
+ return `Cannot dispatch browser-backed run-uat for ${options.milestoneId}/${options.sliceId}: UAT mode "${options.uatType}" requires browser tools, but the run-uat tool surface has none. Enable browser tools or change the UAT to a runtime-executable Playwright command, then rerun /gsd auto.`;
103
109
  }
104
110
  export function isPartialEligibleUatType(uatType) {
105
111
  return !!uatType && UAT_MODE_POLICIES[uatType].partialEligible;
@@ -13,7 +13,7 @@ function isNonEmptyString(value) {
13
13
  function mergeBlockedTools(current, canonical) {
14
14
  const merged = new Map();
15
15
  for (const entry of [...(current ?? []), ...canonical]) {
16
- merged.set(canonicalWorkflowToolName(parseMcpToolName(entry.name)?.tool ?? entry.name), entry);
16
+ merged.set(canonicalWorkflowToolName(parseMcpToolName(entry.name)?.toolName ?? entry.name), entry);
17
17
  }
18
18
  return [...merged.values()];
19
19
  }
@@ -204,20 +204,15 @@ function quoteToolNames(toolNames) {
204
204
  return toolNames.map((toolName) => `"${toolName}"`).join(", ");
205
205
  }
206
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
207
  const errors = [];
214
208
  for (const toolName of params.presentation.presentedTools) {
215
- const baseName = parseMcpToolName(toolName)?.tool ?? toolName;
216
- const canonical = aliasHints[baseName];
217
- if (canonical)
209
+ const baseName = parseMcpToolName(toolName)?.toolName ?? toolName;
210
+ const canonical = canonicalWorkflowToolName(baseName);
211
+ if (canonical !== baseName) {
218
212
  errors.push(`presentation tool "${toolName}" uses an alias; use canonical "${canonical}"`);
213
+ }
219
214
  }
220
- const presentedCanonical = new Set(params.presentation.presentedTools.map((toolName) => canonicalWorkflowToolName(parseMcpToolName(toolName)?.tool ?? toolName)));
215
+ const presentedCanonical = new Set(params.presentation.presentedTools.map((toolName) => canonicalWorkflowToolName(parseMcpToolName(toolName)?.toolName ?? toolName)));
221
216
  const missingRequiredTools = RUN_UAT_WORKFLOW_TOOL_NAMES.filter((requiredTool) => !presentedCanonical.has(requiredTool));
222
217
  if (missingRequiredTools.length === 1) {
223
218
  errors.push(`presentation is missing required UAT tool "${missingRequiredTools[0]}"`);
@@ -227,10 +222,10 @@ function validateCanonicalPresentation(params) {
227
222
  }
228
223
  const forbiddenCanonical = new Set(RUN_UAT_FORBIDDEN_TOOL_NAMES
229
224
  .filter((toolName) => !toolName.includes("*"))
230
- .map((toolName) => canonicalWorkflowToolName(parseMcpToolName(toolName)?.tool ?? toolName)));
225
+ .map((toolName) => canonicalWorkflowToolName(parseMcpToolName(toolName)?.toolName ?? toolName)));
231
226
  const forbiddenPresentedTools = [];
232
227
  for (const toolName of params.presentation.presentedTools) {
233
- const canonical = canonicalWorkflowToolName(parseMcpToolName(toolName)?.tool ?? toolName);
228
+ const canonical = canonicalWorkflowToolName(parseMcpToolName(toolName)?.toolName ?? toolName);
234
229
  if (toolName === "mcp__gsd-workflow__*" || forbiddenCanonical.has(canonical)) {
235
230
  forbiddenPresentedTools.push(toolName);
236
231
  }
@@ -241,7 +236,7 @@ function validateCanonicalPresentation(params) {
241
236
  else if (forbiddenPresentedTools.length > 1) {
242
237
  errors.push(`presentation includes forbidden run-uat tools ${quoteToolNames(forbiddenPresentedTools)}`);
243
238
  }
244
- const blockedCanonical = new Set(params.presentation.blockedTools.map((entry) => canonicalWorkflowToolName(parseMcpToolName(entry.name)?.tool ?? entry.name)));
239
+ const blockedCanonical = new Set(params.presentation.blockedTools.map((entry) => canonicalWorkflowToolName(parseMcpToolName(entry.name)?.toolName ?? entry.name)));
245
240
  const missingBlockedTools = ["gsd_exec", "gsd_summary_save", "gsd_save_gate_result"].filter((blockedTool) => !blockedCanonical.has(blockedTool));
246
241
  if (missingBlockedTools.length === 1) {
247
242
  errors.push(`presentation must record "${missingBlockedTools[0]}" as blocked during run-uat`);