@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
@@ -19,11 +19,11 @@ import {
19
19
  insertAssessment,
20
20
  getMilestoneSlices,
21
21
  getMilestone,
22
- getArtifact,
23
22
  } from "../gsd-db.js";
24
- import { gsdProjectionRoot, clearPathCache, resolveSliceFile } from "../paths.js";
23
+ import { gsdProjectionRoot, clearPathCache } from "../paths.js";
25
24
  import { resolveCanonicalMilestoneRoot } from "../worktree-manager.js";
26
- import { saveFile, clearParseCache, loadFile } from "../files.js";
25
+ import { resolveWorktreeProjectRoot } from "../worktree-root.js";
26
+ import { saveFile, clearParseCache } from "../files.js";
27
27
  import { invalidateStateCache } from "../state.js";
28
28
  import { VALIDATION_VERDICTS, isValidMilestoneVerdict } from "../verdict-parser.js";
29
29
  import { insertMilestoneValidationGates } from "../milestone-validation-gates.js";
@@ -31,7 +31,10 @@ import { logWarning } from "../workflow-logger.js";
31
31
  import { UokGateRunner } from "../uok/gate-runner.js";
32
32
  import { loadEffectiveGSDPreferences } from "../preferences.js";
33
33
  import { resolveUokFlags } from "../uok/flags.js";
34
- import { compactTextParts, hasBrowserEvidenceText, hasBrowserRequiredText } from "../browser-evidence.js";
34
+ import {
35
+ applyBrowserEvidenceGate,
36
+ browserEvidenceGateRequiresAttention,
37
+ } from "../milestone-validation-evidence.js";
35
38
 
36
39
  export interface ValidateMilestoneParams {
37
40
  milestoneId: string;
@@ -78,86 +81,6 @@ function getRequiredVerificationClasses(milestoneId: string): string[] {
78
81
  return required;
79
82
  }
80
83
 
81
- function hasRuntimeExecutableUatEvidenceText(text: string): boolean {
82
- if (!/\buatType:\s*runtime-executable\b/i.test(text)) return false;
83
- if (!/\bverdict:\s*PASS\b/i.test(text)) return false;
84
- return /^\|\s*[^|\n]+\s*\|\s*runtime\s*\|\s*PASS\s*\|[^|\n]*\bgsd_uat_exec\b/mi.test(text);
85
- }
86
-
87
- async function browserEvidenceGateRequiresAttention(
88
- params: ValidateMilestoneParams,
89
- basePath: string,
90
- ): Promise<boolean> {
91
- if (params.verdict !== "pass") return false;
92
-
93
- const milestone = getMilestone(params.milestoneId);
94
- const slices = getMilestoneSlices(params.milestoneId);
95
- const requirementText = compactTextParts([
96
- milestone?.vision,
97
- milestone?.success_criteria,
98
- milestone?.verification_uat,
99
- params.successCriteriaChecklist,
100
- params.verificationClasses,
101
- ...slices.flatMap((slice) => [
102
- slice.demo,
103
- slice.goal,
104
- slice.success_criteria,
105
- ]),
106
- ]);
107
- if (!hasBrowserRequiredText(requirementText)) return false;
108
-
109
- // Collect per-slice evidence so the runtime bypass is checked independently
110
- // for each slice. Concatenating all slices before checking would allow runtime
111
- // evidence from one slice to cover another slice's browser requirements.
112
- const sliceEvidencePairs: Array<{ sliceRequirementText: string; evidenceText: string }> = [];
113
- for (const slice of slices) {
114
- const chunks: string[] = [];
115
- const artifactPath = `milestones/${params.milestoneId}/slices/${slice.id}/${slice.id}-ASSESSMENT.md`;
116
- const artifact = getArtifact(artifactPath);
117
- if (artifact?.full_content) chunks.push(artifact.full_content);
118
- const assessmentPath = resolveSliceFile(basePath, params.milestoneId, slice.id, "ASSESSMENT");
119
- const assessmentContent = assessmentPath ? await loadFile(assessmentPath) : null;
120
- if (assessmentContent) chunks.push(assessmentContent);
121
- sliceEvidencePairs.push({
122
- sliceRequirementText: compactTextParts([slice.demo, slice.goal, slice.success_criteria]),
123
- evidenceText: chunks.join("\n\n"),
124
- });
125
- }
126
- const persistedEvidence = sliceEvidencePairs.map((s) => s.evidenceText).join("\n\n");
127
-
128
- // Runtime bypass: each slice whose own requirement text has browser-observable
129
- // criteria must have its own runtime-executable UAT evidence. When no individual
130
- // slice has slice-level browser requirements (e.g., they come from milestone-level
131
- // fields only), fall back to checking whether any slice has runtime evidence.
132
- const browserRequiringSlices = sliceEvidencePairs.filter((s) =>
133
- hasBrowserRequiredText(s.sliceRequirementText),
134
- );
135
- const runtimeBypasses =
136
- browserRequiringSlices.length > 0
137
- ? browserRequiringSlices.every((s) => hasRuntimeExecutableUatEvidenceText(s.evidenceText))
138
- : sliceEvidencePairs.some((s) => hasRuntimeExecutableUatEvidenceText(s.evidenceText));
139
- if (runtimeBypasses) return false;
140
-
141
- const validationEvidence = compactTextParts([
142
- params.successCriteriaChecklist,
143
- params.verificationClasses,
144
- params.verdictRationale,
145
- params.remediationPlan,
146
- ]);
147
- return !hasBrowserEvidenceText(`${persistedEvidence}\n\n${validationEvidence}`);
148
- }
149
-
150
- function applyBrowserEvidenceGate(params: ValidateMilestoneParams): ValidateMilestoneParams {
151
- 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.";
152
- return {
153
- ...params,
154
- verdict: "needs-attention",
155
- verdictRationale: params.verdictRationale.trim()
156
- ? `${params.verdictRationale.trim()}\n\n${note}`
157
- : note,
158
- };
159
- }
160
-
161
84
  function renderValidationMarkdown(params: ValidateMilestoneParams): string {
162
85
  let md = `---
163
86
  verdict: ${params.verdict}
@@ -279,6 +202,24 @@ export async function handleValidateMilestone(
279
202
  let projectionStale = false;
280
203
  try {
281
204
  await saveFile(validationPath, validationMd);
205
+ const projectRoot = resolveWorktreeProjectRoot(basePath);
206
+ if (projectRoot !== artifactBasePath) {
207
+ const projectValidationPath = join(
208
+ gsdProjectionRoot(projectRoot),
209
+ "milestones",
210
+ effectiveParams.milestoneId,
211
+ `${effectiveParams.milestoneId}-VALIDATION.md`,
212
+ );
213
+ try {
214
+ await saveFile(projectValidationPath, validationMd);
215
+ } catch (mirrorErr) {
216
+ logWarning(
217
+ "projection",
218
+ `validate_milestone project-root VALIDATION mirror failed for ${effectiveParams.milestoneId}`,
219
+ { error: (mirrorErr as Error).message },
220
+ );
221
+ }
222
+ }
282
223
  } catch (renderErr) {
283
224
  projectionStale = true;
284
225
  logWarning("projection", `validate_milestone projection write failed for ${effectiveParams.milestoneId}; DB validation remains committed`, {
@@ -4,6 +4,7 @@
4
4
  import { extractUatType } from "./files.js";
5
5
  import type { UatType } from "./files.js";
6
6
  import { hasBrowserRequiredText } from "./browser-evidence.js";
7
+ import { parseMcpToolName } from "./mcp-tool-name.js";
7
8
 
8
9
  export type { UatType } from "./files.js";
9
10
 
@@ -122,31 +123,39 @@ export function uatTypeIncludesBrowser(uatType: string | undefined): boolean {
122
123
  return isUatType(uatType) && UAT_MODE_POLICIES[uatType].browserTools;
123
124
  }
124
125
 
125
- function canonicalPresentedToolName(toolName: string): string {
126
- if (!toolName.startsWith("mcp__")) return toolName;
127
- const toolSeparator = toolName.indexOf("__", "mcp__".length);
128
- return toolSeparator >= 0 ? toolName.slice(toolSeparator + 2) : toolName;
129
- }
130
-
131
126
  export function isUatBrowserToolName(toolName: string): boolean {
132
- return canonicalPresentedToolName(toolName).startsWith("browser_");
127
+ const parsed = parseMcpToolName(toolName);
128
+ const canonicalName = parsed?.toolName ?? toolName;
129
+ if (canonicalName.startsWith("browser_")) return true;
130
+ return parsed?.toolName === "*" && parsed.serverName.toLowerCase().includes("browser");
133
131
  }
134
132
 
135
133
  export function hasUatBrowserToolSurface(activeTools: readonly string[] | undefined): boolean {
136
134
  return Array.isArray(activeTools) && activeTools.some(isUatBrowserToolName);
137
135
  }
138
136
 
137
+ export function resolveUatBrowserToolSurface(options: {
138
+ activeTools: readonly string[] | undefined;
139
+ registeredTools?: readonly string[] | undefined;
140
+ }): readonly string[] | undefined {
141
+ const surfaces = [options.activeTools, options.registeredTools].filter(Array.isArray);
142
+ if (surfaces.length === 0) return undefined;
143
+ return [...new Set(surfaces.flat())];
144
+ }
145
+
139
146
  export function getUatBrowserToolSupportError(options: {
140
147
  uatType: UatType;
141
148
  activeTools: readonly string[] | undefined;
149
+ registeredTools?: readonly string[] | undefined;
142
150
  milestoneId: string;
143
151
  sliceId: string;
144
152
  }): string | null {
145
153
  if (!uatTypeIncludesBrowser(options.uatType)) return null;
146
- if (!Array.isArray(options.activeTools)) return null;
147
- if (hasUatBrowserToolSurface(options.activeTools)) return null;
154
+ const toolSurface = resolveUatBrowserToolSurface(options);
155
+ if (!toolSurface) return null;
156
+ if (hasUatBrowserToolSurface(toolSurface)) return null;
148
157
 
149
- 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.`;
158
+ 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.`;
150
159
  }
151
160
 
152
161
  export function isPartialEligibleUatType(uatType: UatType | undefined): boolean {
@@ -103,7 +103,7 @@ function mergeBlockedTools(
103
103
  ): UatPresentationInput["blockedTools"] {
104
104
  const merged = new Map<string, { name: string; reason: string }>();
105
105
  for (const entry of [...(current ?? []), ...canonical]) {
106
- merged.set(canonicalWorkflowToolName(parseMcpToolName(entry.name)?.tool ?? entry.name), entry);
106
+ merged.set(canonicalWorkflowToolName(parseMcpToolName(entry.name)?.toolName ?? entry.name), entry);
107
107
  }
108
108
  return [...merged.values()];
109
109
  }
@@ -299,22 +299,18 @@ function quoteToolNames(toolNames: readonly string[]): string {
299
299
  }
300
300
 
301
301
  function validateCanonicalPresentation(params: UatResultSaveParams): string | null {
302
- const aliasHints: Record<string, string> = {
303
- gsd_save_summary: "gsd_summary_save",
304
- gsd_complete_task: "gsd_task_complete",
305
- gsd_complete_slice: "gsd_slice_complete",
306
- gsd_milestone_complete: "gsd_complete_milestone",
307
- };
308
302
  const errors: string[] = [];
309
303
  for (const toolName of params.presentation.presentedTools) {
310
- const baseName = parseMcpToolName(toolName)?.tool ?? toolName;
311
- const canonical = aliasHints[baseName];
312
- if (canonical) errors.push(`presentation tool "${toolName}" uses an alias; use canonical "${canonical}"`);
304
+ const baseName = parseMcpToolName(toolName)?.toolName ?? toolName;
305
+ const canonical = canonicalWorkflowToolName(baseName);
306
+ if (canonical !== baseName) {
307
+ errors.push(`presentation tool "${toolName}" uses an alias; use canonical "${canonical}"`);
308
+ }
313
309
  }
314
310
 
315
311
  const presentedCanonical = new Set(
316
312
  params.presentation.presentedTools.map((toolName) =>
317
- canonicalWorkflowToolName(parseMcpToolName(toolName)?.tool ?? toolName)
313
+ canonicalWorkflowToolName(parseMcpToolName(toolName)?.toolName ?? toolName)
318
314
  ),
319
315
  );
320
316
  const missingRequiredTools = RUN_UAT_WORKFLOW_TOOL_NAMES.filter(
@@ -329,11 +325,11 @@ function validateCanonicalPresentation(params: UatResultSaveParams): string | nu
329
325
  const forbiddenCanonical = new Set(
330
326
  RUN_UAT_FORBIDDEN_TOOL_NAMES
331
327
  .filter((toolName) => !toolName.includes("*"))
332
- .map((toolName) => canonicalWorkflowToolName(parseMcpToolName(toolName)?.tool ?? toolName)),
328
+ .map((toolName) => canonicalWorkflowToolName(parseMcpToolName(toolName)?.toolName ?? toolName)),
333
329
  );
334
330
  const forbiddenPresentedTools: string[] = [];
335
331
  for (const toolName of params.presentation.presentedTools) {
336
- const canonical = canonicalWorkflowToolName(parseMcpToolName(toolName)?.tool ?? toolName);
332
+ const canonical = canonicalWorkflowToolName(parseMcpToolName(toolName)?.toolName ?? toolName);
337
333
  if (toolName === "mcp__gsd-workflow__*" || forbiddenCanonical.has(canonical)) {
338
334
  forbiddenPresentedTools.push(toolName);
339
335
  }
@@ -346,7 +342,7 @@ function validateCanonicalPresentation(params: UatResultSaveParams): string | nu
346
342
 
347
343
  const blockedCanonical = new Set(
348
344
  params.presentation.blockedTools.map((entry) =>
349
- canonicalWorkflowToolName(parseMcpToolName(entry.name)?.tool ?? entry.name)
345
+ canonicalWorkflowToolName(parseMcpToolName(entry.name)?.toolName ?? entry.name)
350
346
  ),
351
347
  );
352
348
  const missingBlockedTools = ["gsd_exec", "gsd_summary_save", "gsd_save_gate_result"].filter(
@@ -45,6 +45,7 @@ import {
45
45
  type ContextModePolicy,
46
46
  type UnitContextManifest,
47
47
  } from "./unit-context-manifest.js";
48
+ import type { UnitPromptContextContract } from "./tool-contract.js";
48
49
 
49
50
  /**
50
51
  * Async function mapping an artifact key to its inlined-content string,
@@ -195,8 +196,32 @@ export interface ComposedUnitContext {
195
196
  readonly inline: string;
196
197
  }
197
198
 
199
+ export type UnitContextBlockMode = "prepend" | "inline" | "excerpt" | "computed";
200
+
201
+ export interface ComposedUnitContextBlock {
202
+ readonly key: string;
203
+ readonly mode: UnitContextBlockMode;
204
+ readonly body: string;
205
+ }
206
+
207
+ export interface ComposedContractedUnitContext extends ComposedUnitContext {
208
+ readonly blocks: readonly ComposedUnitContextBlock[];
209
+ readonly onDemand: readonly ArtifactKey[];
210
+ }
211
+
198
212
  const SECTION_SEPARATOR = "\n\n---\n\n";
199
213
 
214
+ interface UnitContextCompositionContract {
215
+ readonly unitType: string;
216
+ readonly artifacts: {
217
+ readonly inline: readonly ArtifactKey[];
218
+ readonly excerpt: readonly ArtifactKey[];
219
+ readonly onDemand: readonly ArtifactKey[];
220
+ readonly computed: readonly ComputedArtifactId[];
221
+ readonly prepend: readonly ComputedArtifactId[];
222
+ };
223
+ }
224
+
200
225
  /**
201
226
  * Compose all manifest-declared context for a unit type using the v2
202
227
  * surface. Walks `prepend` first (computed-only), then the `inline` list
@@ -220,36 +245,73 @@ export async function composeUnitContext(
220
245
  const manifest: UnitContextManifest | null = resolveManifest(unitType);
221
246
  if (!manifest) return { prepend: "", inline: "" };
222
247
 
223
- // Single-source `unitType`: the manifest is resolved against the
248
+ const composed = await composeDeclaredUnitContext({
249
+ unitType,
250
+ artifacts: {
251
+ inline: manifest.artifacts.inline,
252
+ excerpt: manifest.artifacts.excerpt,
253
+ onDemand: manifest.artifacts.onDemand,
254
+ computed: manifest.artifacts.computed ?? [],
255
+ prepend: manifest.prepend ?? [],
256
+ },
257
+ }, opts);
258
+ return {
259
+ prepend: composed.prepend,
260
+ inline: composed.inline,
261
+ };
262
+ }
263
+
264
+ export async function composeContractedUnitContext(
265
+ contract: UnitPromptContextContract,
266
+ opts: ComposeUnitContextOptions,
267
+ ): Promise<ComposedContractedUnitContext> {
268
+ return composeDeclaredUnitContext(contract, opts);
269
+ }
270
+
271
+ async function composeDeclaredUnitContext(
272
+ contract: UnitContextCompositionContract,
273
+ opts: ComposeUnitContextOptions,
274
+ ): Promise<ComposedContractedUnitContext> {
275
+ // Single-source `unitType`: contract/manifest selection comes from the
224
276
  // function arg, but computed builders read it from `base.unitType`.
225
- // If those ever diverge (caller passes one type to composeUnitContext
226
- // but a different one in opts.base), the composer would silently
227
- // mix one unit's manifest with another unit's computed context.
228
- // Normalize here so the composer dispatches a consistent identity
229
- // through to every builder.
277
+ // Normalize here so every builder sees the same Unit identity.
230
278
  const normalizedOpts: ComposeUnitContextOptions = {
231
279
  ...opts,
232
- base: { ...opts.base, unitType },
280
+ base: { ...opts.base, unitType: contract.unitType },
233
281
  };
234
282
 
235
- const prependBlocks = await runComputed(manifest.prepend ?? [], normalizedOpts);
236
- const inlineBlocks: string[] = [];
283
+ const prependBlocks = await runComputedBlocks(
284
+ contract.artifacts.prepend,
285
+ normalizedOpts,
286
+ "prepend",
287
+ );
288
+ const inlineBlocks: ComposedUnitContextBlock[] = [];
237
289
 
238
- for (const key of manifest.artifacts.inline) {
290
+ for (const key of contract.artifacts.inline) {
239
291
  if (!normalizedOpts.resolveArtifact) break;
240
292
  const body = await normalizedOpts.resolveArtifact(key);
241
- if (body && body.length > 0) inlineBlocks.push(body);
293
+ if (body && body.length > 0) {
294
+ inlineBlocks.push({ key, mode: "inline", body });
295
+ }
242
296
  }
243
- for (const key of manifest.artifacts.excerpt) {
297
+ for (const key of contract.artifacts.excerpt) {
244
298
  if (!normalizedOpts.resolveExcerpt) break;
245
299
  const body = await normalizedOpts.resolveExcerpt(key);
246
- if (body && body.length > 0) inlineBlocks.push(body);
300
+ if (body && body.length > 0) {
301
+ inlineBlocks.push({ key, mode: "excerpt", body });
302
+ }
247
303
  }
248
- inlineBlocks.push(...await runComputed(manifest.artifacts.computed ?? [], normalizedOpts));
304
+ inlineBlocks.push(...await runComputedBlocks(
305
+ contract.artifacts.computed,
306
+ normalizedOpts,
307
+ "computed",
308
+ ));
249
309
 
250
310
  return {
251
- prepend: prependBlocks.join(SECTION_SEPARATOR),
252
- inline: inlineBlocks.join(SECTION_SEPARATOR),
311
+ prepend: prependBlocks.map((block) => block.body).join(SECTION_SEPARATOR),
312
+ inline: inlineBlocks.map((block) => block.body).join(SECTION_SEPARATOR),
313
+ blocks: [...prependBlocks, ...inlineBlocks],
314
+ onDemand: contract.artifacts.onDemand,
253
315
  };
254
316
  }
255
317
 
@@ -258,10 +320,11 @@ export async function composeUnitContext(
258
320
  * Missing registry entries (manifest declares the id but caller didn't
259
321
  * register it) are skipped silently — see composeUnitContext rationale.
260
322
  */
261
- async function runComputed(
323
+ async function runComputedBlocks(
262
324
  ids: readonly ComputedArtifactId[],
263
325
  opts: ComposeUnitContextOptions,
264
- ): Promise<string[]> {
326
+ mode: Extract<UnitContextBlockMode, "prepend" | "computed">,
327
+ ): Promise<ComposedUnitContextBlock[]> {
265
328
  if (ids.length === 0 || !opts.computed) return [];
266
329
  // Type safety lives at the registration boundary (caller-supplied
267
330
  // `computed` is typed against ComputedArtifactInputs[K] per id). Inside
@@ -274,12 +337,14 @@ async function runComputed(
274
337
  inputs: unknown;
275
338
  };
276
339
  const registry = opts.computed as Record<string, AnyEntry | undefined>;
277
- const out: string[] = [];
340
+ const out: ComposedUnitContextBlock[] = [];
278
341
  for (const id of ids) {
279
342
  const entry = registry[id];
280
343
  if (!entry) continue;
281
344
  const body = await entry.build(entry.inputs, opts.base);
282
- if (body && body.length > 0) out.push(body);
345
+ if (body && body.length > 0) {
346
+ out.push({ key: id, mode, body });
347
+ }
283
348
  }
284
349
  return out;
285
350
  }
@@ -10,7 +10,8 @@ import {
10
10
  } from "./paths.js";
11
11
  import { loadFile, parseTaskPlanMustHaves, countMustHavesMentionedInSummary } from "./files.js";
12
12
  import { parseUnitId } from "./unit-id.js";
13
- import { getTask, isDbAvailable, refreshOpenDatabaseFromDisk } from "./gsd-db.js";
13
+ import { getTask, isDbAvailable } from "./gsd-db.js";
14
+ import { refreshWorkflowDatabaseFromDisk } from "./db-workspace.js";
14
15
  import { isClosedStatus } from "./status-guards.js";
15
16
 
16
17
  // Per-record advisory lock — prevents read-modify-write races between
@@ -218,7 +219,7 @@ export async function inspectExecuteTaskDurability(
218
219
  const nextActionAdvanced = !new RegExp(`Execute ${tid}\\b`).test(stateContent);
219
220
  let dbComplete = false;
220
221
  if (isDbAvailable()) {
221
- refreshOpenDatabaseFromDisk();
222
+ refreshWorkflowDatabaseFromDisk();
222
223
  const task = getTask(mid, sid, tid);
223
224
  dbComplete = !!task && isClosedStatus(task.status);
224
225
  }
@@ -71,6 +71,7 @@ export const UNIT_TOOL_CONTRACTS: Record<string, UnitToolSurfaceContract> = {
71
71
  "gsd_milestone_generate_id",
72
72
  ],
73
73
  requiredWorkflowTools: [
74
+ "ask_user_questions",
74
75
  "gsd_summary_save",
75
76
  "gsd_requirement_save",
76
77
  "gsd_requirement_update",
@@ -80,7 +81,7 @@ export const UNIT_TOOL_CONTRACTS: Record<string, UnitToolSurfaceContract> = {
80
81
  },
81
82
  "discuss-slice": {
82
83
  allowedGsdTools: ["gsd_summary_save", "gsd_decision_save"],
83
- requiredWorkflowTools: ["gsd_summary_save"],
84
+ requiredWorkflowTools: ["ask_user_questions", "gsd_summary_save"],
84
85
  },
85
86
  "validate-milestone": {
86
87
  allowedGsdTools: ["gsd_milestone_status", "gsd_validate_milestone", "gsd_reassess_roadmap", "subagent"],
@@ -183,6 +183,24 @@ export function isAwaitingApprovalBoundary(messages: unknown[] | undefined): boo
183
183
  return hasApprovalQuestion(text);
184
184
  }
185
185
 
186
+ /** True when an assistant message already has an in-flight ask_user_questions tool call. */
187
+ export function messageHasPendingAskUserQuestionsTool(message: unknown): boolean {
188
+ if (!message || typeof message !== "object") return false;
189
+ const content = (message as { content?: unknown }).content;
190
+ if (!Array.isArray(content)) return false;
191
+ return content.some((block) => {
192
+ if (!block || typeof block !== "object") return false;
193
+ // Claude Code marks completion by attaching externalResult, not by setting state.
194
+ // Streaming blocks often carry no state; serverToolUse is the claude-code-cli MCP path.
195
+ const tool = block as { type?: string; name?: string; state?: string; externalResult?: unknown };
196
+ if (tool.type !== "toolCall" && tool.type !== "serverToolUse") return false;
197
+ const name = String(tool.name ?? "").toLowerCase();
198
+ if (!name.includes("ask_user_questions")) return false;
199
+ if (tool.externalResult !== undefined) return false;
200
+ return tool.state !== "completed" && tool.state !== "done";
201
+ });
202
+ }
203
+
186
204
  export function shouldPauseForUserApprovalQuestion(
187
205
  unitType: string | undefined,
188
206
  messages: unknown[] | undefined,
@@ -5,6 +5,7 @@ import { existsSync } from "node:fs";
5
5
 
6
6
  import { getAutoWorktreePath, isInAutoWorktree } from "./auto-worktree.js";
7
7
  import { ensureDbOpen } from "./bootstrap/dynamic-tools.js";
8
+ import { refreshWorkflowDatabaseFromDisk } from "./db-workspace.js";
8
9
  import { getIsolationMode } from "./preferences.js";
9
10
  import { deriveState } from "./state.js";
10
11
  import type { GSDState } from "./types.js";
@@ -91,6 +92,7 @@ export async function getValidationBlockMessageForBase(
91
92
  attemptedCommand = "",
92
93
  ): Promise<string | null> {
93
94
  await ensureDbOpen(base);
95
+ refreshWorkflowDatabaseFromDisk();
94
96
  let state = await deriveState(base);
95
97
 
96
98
  if (
@@ -0,0 +1,101 @@
1
+ // Project/App: gsd-pi
2
+ // File Purpose: Web-app detection and Playwright/UAT guidance for planning and slice closeout.
3
+
4
+ import { existsSync, readFileSync } from "node:fs";
5
+ import { resolve } from "node:path";
6
+
7
+ import { detectWebApp } from "../browser-tools/web-app-detect.js";
8
+
9
+ export { detectWebApp };
10
+
11
+ interface MinimalPackageJson {
12
+ dependencies?: Record<string, unknown>;
13
+ devDependencies?: Record<string, unknown>;
14
+ scripts?: Record<string, unknown>;
15
+ }
16
+
17
+ function readPackageJson(projectRoot: string): MinimalPackageJson | null {
18
+ const packageJsonPath = resolve(projectRoot, "package.json");
19
+ if (!existsSync(packageJsonPath)) return null;
20
+ try {
21
+ const parsed = JSON.parse(readFileSync(packageJsonPath, "utf-8")) as unknown;
22
+ return parsed && typeof parsed === "object" ? (parsed as MinimalPackageJson) : null;
23
+ } catch {
24
+ return null;
25
+ }
26
+ }
27
+
28
+ export function hasPlaywrightTestDependency(projectRoot: string): boolean {
29
+ const pkg = readPackageJson(projectRoot);
30
+ if (!pkg) return false;
31
+ const names = [
32
+ ...Object.keys(pkg.dependencies ?? {}),
33
+ ...Object.keys(pkg.devDependencies ?? {}),
34
+ ];
35
+ return names.some((name) => name === "playwright" || name === "@playwright/test");
36
+ }
37
+
38
+ export function findPlaywrightTestScript(projectRoot: string): string | null {
39
+ const pkg = readPackageJson(projectRoot);
40
+ if (!pkg?.scripts) return null;
41
+ for (const [name, value] of Object.entries(pkg.scripts)) {
42
+ if (typeof value !== "string") continue;
43
+ if (/\bplaywright\s+test\b/.test(value)) {
44
+ return `npm run ${name}`;
45
+ }
46
+ }
47
+ return null;
48
+ }
49
+
50
+ /**
51
+ * Markdown block injected into plan/complete-slice prompts when the project
52
+ * looks browser-facing. Returns null for CLI/library-only repos.
53
+ */
54
+ export function buildWebAppUatGuidanceBlock(projectRoot: string): string | null {
55
+ if (!detectWebApp(projectRoot)) return null;
56
+
57
+ const playwrightScript = findPlaywrightTestScript(projectRoot);
58
+ const hasPlaywright = hasPlaywrightTestDependency(projectRoot) || playwrightScript !== null;
59
+ const lines = [
60
+ "### Web App UAT (detected)",
61
+ "",
62
+ "This project looks browser-facing. GSD exposes Playwright-backed `browser_*` tools by default for run-uat.",
63
+ "",
64
+ "**UAT modes (pick one per slice — do not use `artifact-driven` for browser steps):**",
65
+ "- `browser-executable` — navigate to `http://localhost:…`, click, screenshot, assert via `browser_*` tools during run-uat",
66
+ "- `runtime-executable` — run an automated browser test command via `gsd_uat_exec` (for example `npx playwright test`)",
67
+ "- `mixed` / `live-runtime` — combine runtime startup checks with interactive browser verification",
68
+ "",
69
+ "**Planning / closeout rules:**",
70
+ "- Preconditions must name the dev-server command and URL (for example `npm run dev` → `http://localhost:3000`)",
71
+ "- Slice Verification and UAT test cases must not say \"open in browser\" under `artifact-driven` — complete-slice rejects that",
72
+ "- Milestone `Verification Classes` → UAT row must describe browser-observable acceptance, not \"manual spot check\" alone",
73
+ ];
74
+
75
+ if (hasPlaywright) {
76
+ lines.push(
77
+ "",
78
+ "**Playwright:** dependency detected.",
79
+ );
80
+ if (playwrightScript) {
81
+ lines.push(
82
+ `- Prefer slice verification and runtime-executable UAT referencing \`${playwrightScript}\` or a focused \`npx playwright test <spec>\` command`,
83
+ );
84
+ } else {
85
+ lines.push(
86
+ "- Prefer runtime-executable UAT with `npx playwright test` (or a focused spec path) when UI behavior is covered by specs",
87
+ );
88
+ }
89
+ lines.push("- Name concrete spec paths in slice Verification (for example `e2e/smoke.spec.ts`)");
90
+ } else {
91
+ lines.push(
92
+ "",
93
+ "**Playwright scaffolding (first UI slice):** no `playwright` / `@playwright/test` dependency yet.",
94
+ "- Add a planning task that installs Playwright, adds `playwright.config.ts`, and creates a minimal smoke spec (for example `e2e/smoke.spec.ts`)",
95
+ "- Task `verify` should run `npx playwright test` (or the focused spec) with a safe, simple command",
96
+ "- Until specs exist, use `browser-executable` UAT with localhost preconditions and interactive `browser_*` checks at slice closeout",
97
+ );
98
+ }
99
+
100
+ return lines.join("\n");
101
+ }