@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
@@ -4,8 +4,10 @@
4
4
  import {
5
5
  resolveManifest,
6
6
  type ArtifactKey,
7
+ type ComputedArtifactId,
7
8
  type ContextModePolicy,
8
9
  type ToolsPolicy,
10
+ type UnitContextManifest,
9
11
  } from "./unit-context-manifest.js";
10
12
  import { getRequiredWorkflowToolsForAutoUnit } from "./workflow-mcp.js";
11
13
  import { getUnitToolSurfaceContract } from "./unit-tool-contracts.js";
@@ -21,6 +23,7 @@ export interface UnitToolContract {
21
23
  requiredWorkflowTools: readonly string[];
22
24
  forbiddenWorkflowTools: readonly { name: string; reason: string }[];
23
25
  promptObligations: readonly string[];
26
+ promptContext: UnitPromptContextContract;
24
27
  validationRules: readonly string[];
25
28
  closeoutTools: readonly string[];
26
29
  sourceObservations: UnitSourceObservationContract;
@@ -31,6 +34,22 @@ export interface UnitToolContract {
31
34
  };
32
35
  }
33
36
 
37
+ export interface UnitPromptContextContract {
38
+ unitType: string;
39
+ contextMode: ContextModePolicy;
40
+ toolsPolicy: ToolsPolicy;
41
+ obligations: readonly string[];
42
+ sourceObservations: UnitSourceObservationContract;
43
+ artifacts: {
44
+ inline: readonly ArtifactKey[];
45
+ excerpt: readonly ArtifactKey[];
46
+ onDemand: readonly ArtifactKey[];
47
+ computed: readonly ComputedArtifactId[];
48
+ prepend: readonly ComputedArtifactId[];
49
+ };
50
+ maxSystemPromptChars: number;
51
+ }
52
+
34
53
  export type UnitSourceObservationContract =
35
54
  | { mode: "none" }
36
55
  | {
@@ -45,6 +64,22 @@ export type ToolContractResult =
45
64
  | { ok: true; contract: UnitToolContract }
46
65
  | { ok: false; reason: "unknown-unit-type" | "missing-closeout-tool"; detail: string };
47
66
 
67
+ export type UnitContextContractResult =
68
+ | { ok: true; contract: UnitPromptContextContract }
69
+ | { ok: false; reason: "unknown-unit-type"; detail: string };
70
+
71
+ export function compileUnitContextContract(unitType: string): UnitContextContractResult {
72
+ const manifest = resolveManifest(unitType);
73
+ if (!manifest) {
74
+ return {
75
+ ok: false,
76
+ reason: "unknown-unit-type",
77
+ detail: `No Unit manifest is registered for ${unitType}`,
78
+ };
79
+ }
80
+ return { ok: true, contract: buildPromptContextContract(unitType, manifest) };
81
+ }
82
+
48
83
  export function compileUnitToolContract(unitType: string): ToolContractResult {
49
84
  const manifest = resolveManifest(unitType);
50
85
  const surfaceContract = getUnitToolSurfaceContract(unitType);
@@ -71,6 +106,8 @@ export function compileUnitToolContract(unitType: string): ToolContractResult {
71
106
  };
72
107
  }
73
108
 
109
+ const promptContext = buildPromptContextContract(unitType, manifest);
110
+
74
111
  return {
75
112
  ok: true,
76
113
  contract: {
@@ -79,10 +116,8 @@ export function compileUnitToolContract(unitType: string): ToolContractResult {
79
116
  toolsPolicy: manifest.tools,
80
117
  requiredWorkflowTools,
81
118
  forbiddenWorkflowTools,
82
- promptObligations: [
83
- `context-mode:${manifest.contextMode}`,
84
- `tools-policy:${manifest.tools.mode}`,
85
- ],
119
+ promptObligations: promptContext.obligations,
120
+ promptContext,
86
121
  validationRules: [
87
122
  "unit-manifest-present",
88
123
  "workflow-tool-surface-present",
@@ -90,16 +125,59 @@ export function compileUnitToolContract(unitType: string): ToolContractResult {
90
125
  ...(unitType === "execute-task" ? ["source-observation-contract-present"] : []),
91
126
  ],
92
127
  closeoutTools,
93
- sourceObservations: sourceObservationContractForUnit(unitType),
128
+ sourceObservations: promptContext.sourceObservations,
94
129
  artifacts: {
95
- inline: manifest.artifacts.inline,
96
- excerpt: manifest.artifacts.excerpt,
97
- onDemand: manifest.artifacts.onDemand,
130
+ inline: promptContext.artifacts.inline,
131
+ excerpt: promptContext.artifacts.excerpt,
132
+ onDemand: promptContext.artifacts.onDemand,
98
133
  },
99
134
  },
100
135
  };
101
136
  }
102
137
 
138
+ function buildPromptContextContract(
139
+ unitType: string,
140
+ manifest: UnitContextManifest,
141
+ ): UnitPromptContextContract {
142
+ const sourceObservations = sourceObservationContractForUnit(unitType);
143
+ return {
144
+ unitType,
145
+ contextMode: manifest.contextMode,
146
+ toolsPolicy: manifest.tools,
147
+ obligations: promptContextObligations(manifest, sourceObservations),
148
+ sourceObservations,
149
+ artifacts: {
150
+ inline: manifest.artifacts.inline,
151
+ excerpt: manifest.artifacts.excerpt,
152
+ onDemand: manifest.artifacts.onDemand,
153
+ computed: manifest.artifacts.computed ?? [],
154
+ prepend: manifest.prepend ?? [],
155
+ },
156
+ maxSystemPromptChars: manifest.maxSystemPromptChars,
157
+ };
158
+ }
159
+
160
+ function promptContextObligations(
161
+ manifest: UnitContextManifest,
162
+ sourceObservations: UnitSourceObservationContract,
163
+ ): string[] {
164
+ const obligations = [
165
+ `context-mode:${manifest.contextMode}`,
166
+ `tools-policy:${manifest.tools.mode}`,
167
+ artifactObligation("context-inline", manifest.artifacts.inline),
168
+ artifactObligation("context-excerpt", manifest.artifacts.excerpt),
169
+ artifactObligation("context-on-demand", manifest.artifacts.onDemand),
170
+ ];
171
+ if (sourceObservations.mode !== "none") {
172
+ obligations.push(`source-observations:${sourceObservations.mode}`);
173
+ }
174
+ return obligations;
175
+ }
176
+
177
+ function artifactObligation(label: string, artifacts: readonly ArtifactKey[]): string {
178
+ return `${label}:${artifacts.length > 0 ? artifacts.join(",") : "none"}`;
179
+ }
180
+
103
181
  function sourceObservationContractForUnit(unitType: string): UnitSourceObservationContract {
104
182
  if (unitType !== "execute-task") return { mode: "none" };
105
183
  return {
@@ -7,7 +7,10 @@ import {
7
7
  RUN_UAT_TOOL_PRESENTATION_PLAN_ID,
8
8
  RUN_UAT_WORKFLOW_TOOL_NAMES,
9
9
  } from "./unit-tool-contracts.js";
10
+ import { parseMcpToolName, toMcpToolName } from "./mcp-tool-name.js";
11
+ import { createToolSurfaceSnapshot, type ToolSurfaceSnapshot } from "./tool-surface-snapshot.js";
10
12
  import { uatTypeIncludesBrowser } from "./uat-policy.js";
13
+ import { canonicalWorkflowSurfaceToolName } from "./workflow-tool-surface.js";
11
14
 
12
15
  export {
13
16
  RUN_UAT_BROWSER_TOOL_NAMES,
@@ -33,6 +36,7 @@ export interface ToolPresentationPlan {
33
36
  blockedToolNames: Array<{ name: string; reason: string }>;
34
37
  aliases: Array<{ requested: string; canonical: string }>;
35
38
  diagnostics: string[];
39
+ toolSurface: ToolSurfaceSnapshot;
36
40
  }
37
41
 
38
42
  export interface RunUatResultPresentation {
@@ -62,44 +66,14 @@ export const RUN_UAT_CLAUDE_NATIVE_TOOL_NAMES = [
62
66
  "Grep",
63
67
  ] as const;
64
68
 
65
- const WORKFLOW_ALIAS_TO_CANONICAL: Record<string, string> = {
66
- gsd_save_decision: "gsd_decision_save",
67
- gsd_update_requirement: "gsd_requirement_update",
68
- gsd_save_requirement: "gsd_requirement_save",
69
- gsd_save_summary: "gsd_summary_save",
70
- gsd_generate_milestone_id: "gsd_milestone_generate_id",
71
- gsd_milestone_plan: "gsd_plan_milestone",
72
- gsd_slice_plan: "gsd_plan_slice",
73
- gsd_task_plan: "gsd_plan_task",
74
- gsd_slice_replan: "gsd_replan_slice",
75
- gsd_complete_slice: "gsd_slice_complete",
76
- gsd_milestone_complete: "gsd_complete_milestone",
77
- gsd_milestone_validate: "gsd_validate_milestone",
78
- gsd_roadmap_reassess: "gsd_reassess_roadmap",
79
- gsd_complete_task: "gsd_task_complete",
80
- gsd_reopen_task: "gsd_task_reopen",
81
- gsd_reopen_slice: "gsd_slice_reopen",
82
- gsd_reopen_milestone: "gsd_milestone_reopen",
83
- };
84
-
85
69
  export function canonicalWorkflowToolName(toolName: string): string {
86
- const mcp = parseMcpToolName(toolName);
87
- const baseName = mcp?.tool ?? toolName;
88
- return WORKFLOW_ALIAS_TO_CANONICAL[baseName] ?? baseName;
70
+ return canonicalWorkflowSurfaceToolName(toolName);
89
71
  }
90
72
 
91
- export function parseMcpToolName(toolName: string): { server: string; tool: string } | null {
92
- if (!toolName.startsWith("mcp__")) return null;
93
- const toolSeparator = toolName.indexOf("__", "mcp__".length);
94
- if (toolSeparator < 0) return null;
95
- return {
96
- server: toolName.slice("mcp__".length, toolSeparator),
97
- tool: toolName.slice(toolSeparator + 2),
98
- };
99
- }
73
+ export { parseMcpToolName } from "./mcp-tool-name.js";
100
74
 
101
75
  export function toWorkflowMcpToolName(serverName: string, toolName: string): string {
102
- return `mcp__${serverName}__${canonicalWorkflowToolName(toolName)}`;
76
+ return toMcpToolName(serverName, canonicalWorkflowToolName(toolName));
103
77
  }
104
78
 
105
79
  function dedupe(values: readonly string[]): string[] {
@@ -212,6 +186,14 @@ export function resolveToolPresentationPlan(options: {
212
186
  : name
213
187
  )
214
188
  : allowedToolNames;
189
+ const toolSurface = createToolSurfaceSnapshot({
190
+ source: "presentation-plan",
191
+ phase: options.phase,
192
+ modelFacingToolNames: options.availableToolNames ?? requested,
193
+ registeredToolNames: options.availableToolNames ?? requested,
194
+ scopedToolNames: allowedToolNames,
195
+ presentedToolNames,
196
+ });
215
197
 
216
198
  if (options.phase === "run-uat") {
217
199
  for (const forbidden of RUN_UAT_FORBIDDEN_TOOL_NAMES) {
@@ -228,5 +210,6 @@ export function resolveToolPresentationPlan(options: {
228
210
  blockedToolNames,
229
211
  aliases,
230
212
  diagnostics: [],
213
+ toolSurface,
231
214
  };
232
215
  }
@@ -0,0 +1,47 @@
1
+ // Project/App: gsd-pi
2
+ // File Purpose: Typed snapshots for model-facing, registered, scoped, and presented tool surfaces.
3
+
4
+ export type ToolSurfaceSnapshotSource =
5
+ | "runtime-scope"
6
+ | "dispatch-scope"
7
+ | "provider-adjustment"
8
+ | "presentation-plan";
9
+
10
+ export interface ToolSurfaceSnapshot {
11
+ source: ToolSurfaceSnapshotSource;
12
+ unitType?: string;
13
+ phase?: string;
14
+ modelFacingToolNames: string[];
15
+ registeredToolNames: string[];
16
+ scopedToolNames: string[];
17
+ presentedToolNames: string[];
18
+ capturedAt: number;
19
+ }
20
+
21
+ export interface ToolSurfaceSnapshotInput {
22
+ source: ToolSurfaceSnapshotSource;
23
+ unitType?: string;
24
+ phase?: string;
25
+ modelFacingToolNames?: readonly string[];
26
+ registeredToolNames?: readonly string[];
27
+ scopedToolNames?: readonly string[];
28
+ presentedToolNames?: readonly string[];
29
+ capturedAt?: number;
30
+ }
31
+
32
+ function dedupeToolNames(toolNames: readonly string[] | undefined): string[] {
33
+ return [...new Set(toolNames ?? [])];
34
+ }
35
+
36
+ export function createToolSurfaceSnapshot(input: ToolSurfaceSnapshotInput): ToolSurfaceSnapshot {
37
+ return {
38
+ source: input.source,
39
+ unitType: input.unitType,
40
+ phase: input.phase,
41
+ modelFacingToolNames: dedupeToolNames(input.modelFacingToolNames),
42
+ registeredToolNames: dedupeToolNames(input.registeredToolNames),
43
+ scopedToolNames: dedupeToolNames(input.scopedToolNames),
44
+ presentedToolNames: dedupeToolNames(input.presentedToolNames),
45
+ capturedAt: input.capturedAt ?? Date.now(),
46
+ };
47
+ }
@@ -1,25 +1,8 @@
1
1
  // Project/App: gsd-pi
2
2
  // File Purpose: Plans milestone roadmap state through DB-backed workflow tools.
3
3
 
4
- import { clearParseCache } from "../files.js";
5
- import { isClosedStatus } from "../status-guards.js";
6
4
  import { isNonEmptyString, validateStringArray, validateTitle } from "../validation.js";
7
- import {
8
- transaction,
9
- getMilestone,
10
- getMilestoneSlices,
11
- getSlice,
12
- insertMilestone,
13
- insertSlice,
14
- upsertMilestonePlanning,
15
- upsertSlicePlanning,
16
- } from "../gsd-db.js";
17
- import { invalidateStateCache } from "../state.js";
18
- import { renderRoadmapFromDb } from "../markdown-renderer.js";
19
- import { renderAllProjections } from "../workflow-projections.js";
20
- import { writeManifest } from "../workflow-manifest.js";
21
- import { appendEvent } from "../workflow-events.js";
22
- import { logWarning } from "../workflow-logger.js";
5
+ import { persistMilestonePlan } from "../milestone-planning-persistence.js";
23
6
 
24
7
  export interface PlanMilestoneSliceInput {
25
8
  sliceId: string;
@@ -115,11 +98,21 @@ function validateProofStrategy(value: unknown): Array<{ riskOrUnknown: string; r
115
98
  });
116
99
  }
117
100
 
101
+ const SLICE_ID_RE = /^[A-Za-z0-9][A-Za-z0-9-]*$/;
102
+
118
103
  function validateSlices(value: unknown): PlanMilestoneSliceInput[] {
119
104
  if (!Array.isArray(value) || value.length === 0) {
120
105
  throw new Error("slices must be a non-empty array");
121
106
  }
122
107
 
108
+ // Pre-collect all slice IDs so depends cross-validation can reference the full set.
109
+ const allSliceIds = new Set<string>(
110
+ (value as unknown[])
111
+ .filter((e): e is Record<string, unknown> => !!e && typeof e === "object")
112
+ .map(e => e.sliceId)
113
+ .filter((id): id is string => isNonEmptyString(id)),
114
+ );
115
+
123
116
  const seen = new Set<string>();
124
117
  return value.map((entry, index) => {
125
118
  if (!entry || typeof entry !== "object") {
@@ -154,8 +147,13 @@ function validateSlices(value: unknown): PlanMilestoneSliceInput[] {
154
147
  const titleIssue = validateTitle(title);
155
148
  if (titleIssue) throw new Error(`slices[${index}].title is invalid: ${titleIssue}`);
156
149
  if (!isNonEmptyString(risk)) throw new Error(`slices[${index}].risk must be a non-empty string`);
157
- if (!Array.isArray(depends) || depends.some((item) => !isNonEmptyString(item))) {
158
- throw new Error(`slices[${index}].depends must be an array of non-empty strings`);
150
+ if (!Array.isArray(depends) || depends.some((item) => !isNonEmptyString(item) || !SLICE_ID_RE.test(item as string))) {
151
+ throw new Error(`slices[${index}].depends must be an array of valid slice IDs (e.g. "S01")`);
152
+ }
153
+ for (const dep of depends as string[]) {
154
+ if (!allSliceIds.has(dep)) {
155
+ throw new Error(`slices[${index}].depends references unknown slice "${dep}" — check that it is defined in the same milestone`);
156
+ }
159
157
  }
160
158
  if (!isNonEmptyString(demo)) throw new Error(`slices[${index}].demo must be a non-empty string`);
161
159
  if (!isNonEmptyString(goal)) throw new Error(`slices[${index}].goal must be a non-empty string`);
@@ -227,144 +225,5 @@ export async function handlePlanMilestone(
227
225
  return { error: `validation failed: ${(err as Error).message}` };
228
226
  }
229
227
 
230
- // ── Guards + DB writes inside a single transaction (prevents TOCTOU) ───
231
- // Guards must be inside the transaction so the state they check cannot
232
- // change between the read and the write (#2723).
233
- let guardError: string | null = null;
234
-
235
- try {
236
- transaction(() => {
237
- const existingMilestone = getMilestone(params.milestoneId);
238
- if (existingMilestone && isClosedStatus(existingMilestone.status)) {
239
- guardError = `cannot re-plan milestone ${params.milestoneId}: it is already complete`;
240
- return;
241
- }
242
-
243
- // Guard: refuse to re-plan a milestone that would drop completed slices (#2960).
244
- // Allow re-planning when all completed slices are still present in the
245
- // incoming plan — their status is preserved below (#2558). Block only when
246
- // the new plan omits a completed slice, which could shadow completed work.
247
- const existingSlices = getMilestoneSlices(params.milestoneId);
248
- const completedSlices = existingSlices.filter(s => isClosedStatus(s.status));
249
- if (completedSlices.length > 0) {
250
- const incomingSliceIds = new Set(params.slices.map(s => s.sliceId));
251
- const droppedCompleted = completedSlices.filter(s => !incomingSliceIds.has(s.id));
252
- if (droppedCompleted.length > 0) {
253
- 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.`;
254
- return;
255
- }
256
- }
257
-
258
- // Validate depends_on: all dependencies must exist and be complete
259
- if (params.dependsOn && params.dependsOn.length > 0) {
260
- for (const depId of params.dependsOn) {
261
- const dep = getMilestone(depId);
262
- if (!dep) {
263
- guardError = `depends_on references unknown milestone: ${depId}`;
264
- return;
265
- }
266
- if (!isClosedStatus(dep.status)) {
267
- guardError = `depends_on milestone ${depId} is not yet complete (status: ${dep.status})`;
268
- return;
269
- }
270
- }
271
- }
272
-
273
- insertMilestone({
274
- id: params.milestoneId,
275
- title: params.title,
276
- status: params.status ?? "active",
277
- depends_on: params.dependsOn ?? [],
278
- });
279
-
280
- upsertMilestonePlanning(params.milestoneId, {
281
- title: params.title,
282
- status: params.status ?? "active",
283
- depends_on: params.dependsOn ?? [],
284
- vision: params.vision,
285
- successCriteria: params.successCriteria,
286
- keyRisks: params.keyRisks,
287
- proofStrategy: params.proofStrategy,
288
- verificationContract: params.verificationContract,
289
- verificationIntegration: params.verificationIntegration,
290
- verificationOperational: params.verificationOperational,
291
- verificationUat: params.verificationUat,
292
- definitionOfDone: params.definitionOfDone,
293
- requirementCoverage: params.requirementCoverage,
294
- boundaryMapMarkdown: params.boundaryMapMarkdown,
295
- });
296
-
297
- for (let i = 0; i < params.slices.length; i++) {
298
- const slice = params.slices[i]!;
299
- // Preserve completed/done status on re-plan (#2558).
300
- // Without this, a re-plan after milestone transition would reset
301
- // already-completed slices back to "pending".
302
- const existing = getSlice(params.milestoneId, slice.sliceId);
303
- const status = existing && (existing.status === "complete" || existing.status === "done")
304
- ? existing.status
305
- : "pending";
306
- insertSlice({
307
- id: slice.sliceId,
308
- milestoneId: params.milestoneId,
309
- title: slice.title,
310
- status,
311
- risk: slice.risk,
312
- depends: slice.depends,
313
- demo: slice.demo,
314
- sequence: i + 1, // Preserve agent-ordered sequence (#3356)
315
- // ADR-011: pass undefined through so ON CONFLICT preserves existing values
316
- // when the caller omitted the fields on a re-plan.
317
- isSketch: slice.isSketch,
318
- sketchScope: slice.sketchScope,
319
- });
320
- upsertSlicePlanning(params.milestoneId, slice.sliceId, {
321
- goal: slice.goal,
322
- successCriteria: slice.successCriteria,
323
- proofLevel: slice.proofLevel,
324
- integrationClosure: slice.integrationClosure,
325
- observabilityImpact: slice.observabilityImpact,
326
- });
327
- }
328
- });
329
- } catch (err) {
330
- return { error: `db write failed: ${(err as Error).message}` };
331
- }
332
-
333
- if (guardError) {
334
- return { error: guardError };
335
- }
336
-
337
- let roadmapPath: string;
338
- try {
339
- const renderResult = await renderRoadmapFromDb(basePath, params.milestoneId);
340
- roadmapPath = renderResult.roadmapPath;
341
- } catch (renderErr) {
342
- logWarning("tool", `plan_milestone — render failed (DB rows preserved for debugging): ${(renderErr as Error).message}`);
343
- invalidateStateCache();
344
- return { error: `render failed: ${(renderErr as Error).message}` };
345
- }
346
-
347
- invalidateStateCache();
348
- clearParseCache();
349
-
350
- // ── Post-mutation hook: projections, manifest, event log ───────────────
351
- try {
352
- await renderAllProjections(basePath, params.milestoneId);
353
- writeManifest(basePath);
354
- appendEvent(basePath, {
355
- cmd: "plan-milestone",
356
- params: { milestoneId: params.milestoneId },
357
- ts: new Date().toISOString(),
358
- actor: "agent",
359
- actor_name: params.actorName,
360
- trigger_reason: params.triggerReason,
361
- });
362
- } catch (hookErr) {
363
- logWarning("tool", `plan-milestone post-mutation hook warning: ${(hookErr as Error).message}`);
364
- }
365
-
366
- return {
367
- milestoneId: params.milestoneId,
368
- roadmapPath,
369
- };
228
+ return persistMilestonePlan(params, basePath);
370
229
  }
@@ -75,12 +75,19 @@ function validateParams(params: ReassessRoadmapParams): ReassessRoadmapParams {
75
75
  throw new Error("sliceChanges.removed must be an array");
76
76
  }
77
77
 
78
+ const SLICE_ID_RE = /^[A-Za-z0-9][A-Za-z0-9-]*$/;
79
+
78
80
  // Validate each modified slice
79
81
  for (let i = 0; i < params.sliceChanges.modified.length; i++) {
80
82
  const s = params.sliceChanges.modified[i];
81
83
  if (!s || typeof s !== "object") throw new Error(`sliceChanges.modified[${i}] must be an object`);
82
84
  if (!isNonEmptyString(s.sliceId)) throw new Error(`sliceChanges.modified[${i}].sliceId is required`);
83
85
  if (!isNonEmptyString(s.title)) throw new Error(`sliceChanges.modified[${i}].title is required`);
86
+ if (s.depends !== undefined) {
87
+ if (!Array.isArray(s.depends) || s.depends.some((item: unknown) => !isNonEmptyString(item) || !SLICE_ID_RE.test(item as string))) {
88
+ throw new Error(`sliceChanges.modified[${i}].depends must be an array of valid slice IDs (e.g. "S01")`);
89
+ }
90
+ }
84
91
  }
85
92
 
86
93
  // Validate each added slice
@@ -89,6 +96,11 @@ function validateParams(params: ReassessRoadmapParams): ReassessRoadmapParams {
89
96
  if (!s || typeof s !== "object") throw new Error(`sliceChanges.added[${i}] must be an object`);
90
97
  if (!isNonEmptyString(s.sliceId)) throw new Error(`sliceChanges.added[${i}].sliceId is required`);
91
98
  if (!isNonEmptyString(s.title)) throw new Error(`sliceChanges.added[${i}].title is required`);
99
+ if (s.depends !== undefined) {
100
+ if (!Array.isArray(s.depends) || s.depends.some((item: unknown) => !isNonEmptyString(item) || !SLICE_ID_RE.test(item as string))) {
101
+ throw new Error(`sliceChanges.added[${i}].depends must be an array of valid slice IDs (e.g. "S01")`);
102
+ }
103
+ }
92
104
  }
93
105
 
94
106
  return params;
@@ -166,6 +178,37 @@ export async function handleReassessRoadmap(
166
178
  }
167
179
  }
168
180
 
181
+ // Cross-milestone depends validation — effective slice ID set after this reassessment
182
+ const removedIds = new Set<string>(params.sliceChanges.removed);
183
+ const effectiveSliceIds = new Set<string>(
184
+ existingSlices.map(s => s.id).filter(id => !removedIds.has(id)),
185
+ );
186
+ for (const added of params.sliceChanges.added) {
187
+ effectiveSliceIds.add(added.sliceId);
188
+ }
189
+ for (let i = 0; i < params.sliceChanges.modified.length; i++) {
190
+ const mod = params.sliceChanges.modified[i]!;
191
+ if (mod.depends !== undefined) {
192
+ for (const dep of mod.depends) {
193
+ if (!effectiveSliceIds.has(dep)) {
194
+ guardError = `sliceChanges.modified[${i}].depends references unknown slice "${dep}" — check that it is defined in this milestone`;
195
+ return;
196
+ }
197
+ }
198
+ }
199
+ }
200
+ for (let i = 0; i < params.sliceChanges.added.length; i++) {
201
+ const added = params.sliceChanges.added[i]!;
202
+ if (added.depends !== undefined) {
203
+ for (const dep of added.depends) {
204
+ if (!effectiveSliceIds.has(dep)) {
205
+ guardError = `sliceChanges.added[${i}].depends references unknown slice "${dep}" — check that it is defined in this milestone`;
206
+ return;
207
+ }
208
+ }
209
+ }
210
+ }
211
+
169
212
  // Record assessment
170
213
  insertAssessment({
171
214
  path: assessmentRelPath,