@opengsd/gsd-pi 1.1.1-dev.b2556262 → 1.2.0-dev.4813ead6

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 (500) 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/project-sessions.js +4 -2
  5. package/dist/resources/.managed-resources-content-hash +1 -1
  6. package/dist/resources/extensions/ask-user-questions.js +78 -23
  7. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +101 -237
  8. package/dist/resources/extensions/claude-code-cli/turn-assembler.js +224 -0
  9. package/dist/resources/extensions/github-sync/templates.js +3 -3
  10. package/dist/resources/extensions/gsd/artifact-projection.js +14 -0
  11. package/dist/resources/extensions/gsd/auto/contracts.js +8 -1
  12. package/dist/resources/extensions/gsd/auto/loop.js +74 -56
  13. package/dist/resources/extensions/gsd/auto/orchestrator.js +763 -63
  14. package/dist/resources/extensions/gsd/auto/phases.js +28 -3
  15. package/dist/resources/extensions/gsd/auto/run-unit.js +2 -1
  16. package/dist/resources/extensions/gsd/auto/session.js +3 -0
  17. package/dist/resources/extensions/gsd/auto-dashboard.js +16 -4
  18. package/dist/resources/extensions/gsd/auto-dispatch.js +6 -5
  19. package/dist/resources/extensions/gsd/auto-model-selection.js +8 -0
  20. package/dist/resources/extensions/gsd/auto-post-unit.js +4 -3
  21. package/dist/resources/extensions/gsd/auto-prompts.js +191 -9
  22. package/dist/resources/extensions/gsd/auto-recovery.js +48 -49
  23. package/dist/resources/extensions/gsd/auto-runtime-state.js +17 -0
  24. package/dist/resources/extensions/gsd/auto-start.js +12 -23
  25. package/dist/resources/extensions/gsd/auto-timers.js +16 -2
  26. package/dist/resources/extensions/gsd/auto-tool-tracking.js +37 -0
  27. package/dist/resources/extensions/gsd/auto-unit-tool-scope.js +33 -29
  28. package/dist/resources/extensions/gsd/auto-verification.js +7 -7
  29. package/dist/resources/extensions/gsd/auto-worktree.js +45 -36
  30. package/dist/resources/extensions/gsd/auto.js +73 -471
  31. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +28 -37
  32. package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +11 -37
  33. package/dist/resources/extensions/gsd/bootstrap/query-tools.js +2 -2
  34. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +103 -138
  35. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +63 -4
  36. package/dist/resources/extensions/gsd/closeout-consistency-gate.js +21 -4
  37. package/dist/resources/extensions/gsd/codebase-generator.js +8 -4
  38. package/dist/resources/extensions/gsd/commands/handlers/auto.js +3 -0
  39. package/dist/resources/extensions/gsd/commands-handlers.js +20 -0
  40. package/dist/resources/extensions/gsd/commands-inspect.js +4 -8
  41. package/dist/resources/extensions/gsd/commands-maintenance.js +61 -41
  42. package/dist/resources/extensions/gsd/commands-ship.js +2 -2
  43. package/dist/resources/extensions/gsd/commands-verdict.js +12 -2
  44. package/dist/resources/extensions/gsd/db-workspace.js +103 -0
  45. package/dist/resources/extensions/gsd/debug-logger.js +10 -0
  46. package/dist/resources/extensions/gsd/delegation-policy.js +2 -10
  47. package/dist/resources/extensions/gsd/discussion-handoff.js +218 -0
  48. package/dist/resources/extensions/gsd/docs/preferences-reference.md +9 -0
  49. package/dist/resources/extensions/gsd/doctor-proactive.js +7 -2
  50. package/dist/resources/extensions/gsd/doctor.js +16 -9
  51. package/dist/resources/extensions/gsd/error-classifier.js +1 -1
  52. package/dist/resources/extensions/gsd/git-conflict-state.js +16 -1
  53. package/dist/resources/extensions/gsd/gsd-db.js +12 -0
  54. package/dist/resources/extensions/gsd/guided-flow.js +36 -470
  55. package/dist/resources/extensions/gsd/guided-unit-completion.js +225 -0
  56. package/dist/resources/extensions/gsd/markdown-renderer.js +33 -33
  57. package/dist/resources/extensions/gsd/mcp-filter.js +8 -1
  58. package/dist/resources/extensions/gsd/mcp-tool-name.js +26 -0
  59. package/dist/resources/extensions/gsd/md-importer.js +4 -3
  60. package/dist/resources/extensions/gsd/migrate/safety.js +2 -2
  61. package/dist/resources/extensions/gsd/migration-auto-check.js +3 -2
  62. package/dist/resources/extensions/gsd/milestone-closeout-proof.js +72 -0
  63. package/dist/resources/extensions/gsd/milestone-closeout.js +12 -4
  64. package/dist/resources/extensions/gsd/milestone-merge-transaction.js +10 -0
  65. package/dist/resources/extensions/gsd/milestone-planning-persistence.js +156 -0
  66. package/dist/resources/extensions/gsd/milestone-readiness.js +77 -0
  67. package/dist/resources/extensions/gsd/milestone-settlement.js +50 -0
  68. package/dist/resources/extensions/gsd/milestone-validation-evidence.js +73 -0
  69. package/dist/resources/extensions/gsd/milestone-validation-verdict.js +57 -0
  70. package/dist/resources/extensions/gsd/native-git-bridge.js +45 -0
  71. package/dist/resources/extensions/gsd/parallel-eligibility.js +3 -6
  72. package/dist/resources/extensions/gsd/parallel-orchestrator.js +3 -2
  73. package/dist/resources/extensions/gsd/preferences-diagnostics.js +67 -0
  74. package/dist/resources/extensions/gsd/preferences.js +147 -29
  75. package/dist/resources/extensions/gsd/prompts/complete-slice.md +1 -0
  76. package/dist/resources/extensions/gsd/prompts/discuss.md +6 -7
  77. package/dist/resources/extensions/gsd/prompts/execute-task.md +2 -0
  78. package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +5 -7
  79. package/dist/resources/extensions/gsd/prompts/guided-discuss-project.md +6 -6
  80. package/dist/resources/extensions/gsd/prompts/guided-discuss-requirements.md +1 -2
  81. package/dist/resources/extensions/gsd/prompts/guided-discuss-slice.md +5 -6
  82. package/dist/resources/extensions/gsd/prompts/plan-milestone.md +2 -0
  83. package/dist/resources/extensions/gsd/prompts/plan-slice.md +2 -1
  84. package/dist/resources/extensions/gsd/prompts/refine-slice.md +1 -0
  85. package/dist/resources/extensions/gsd/prompts/research-milestone.md +2 -2
  86. package/dist/resources/extensions/gsd/prompts/system.md +1 -1
  87. package/dist/resources/extensions/gsd/prompts/validate-milestone.md +5 -3
  88. package/dist/resources/extensions/gsd/provider-payload-policy.js +83 -0
  89. package/dist/resources/extensions/gsd/pull-request-process.js +13 -0
  90. package/dist/resources/extensions/gsd/quality-gate-closure.js +109 -0
  91. package/dist/resources/extensions/gsd/question-transport.js +86 -0
  92. package/dist/resources/extensions/gsd/roadmap-slices.js +8 -2
  93. package/dist/resources/extensions/gsd/schemas/parsers.js +6 -1
  94. package/dist/resources/extensions/gsd/slice-parallel-orchestrator.js +3 -2
  95. package/dist/resources/extensions/gsd/state-reconciliation/drift/artifact-db.js +21 -1
  96. package/dist/resources/extensions/gsd/state.js +13 -5
  97. package/dist/resources/extensions/gsd/templates/plan.md +7 -0
  98. package/dist/resources/extensions/gsd/templates/project.md +1 -0
  99. package/dist/resources/extensions/gsd/templates/roadmap.md +1 -1
  100. package/dist/resources/extensions/gsd/templates/uat.md +5 -1
  101. package/dist/resources/extensions/gsd/tool-contract.js +52 -8
  102. package/dist/resources/extensions/gsd/tool-presentation-plan.js +15 -34
  103. package/dist/resources/extensions/gsd/tool-surface-snapshot.js +17 -0
  104. package/dist/resources/extensions/gsd/tools/plan-milestone.js +15 -143
  105. package/dist/resources/extensions/gsd/tools/reassess-roadmap.js +39 -0
  106. package/dist/resources/extensions/gsd/tools/validate-milestone.js +15 -78
  107. package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +169 -20
  108. package/dist/resources/extensions/gsd/uat-policy.js +16 -10
  109. package/dist/resources/extensions/gsd/uat-run.js +9 -14
  110. package/dist/resources/extensions/gsd/unit-context-composer.js +40 -20
  111. package/dist/resources/extensions/gsd/unit-runtime.js +3 -2
  112. package/dist/resources/extensions/gsd/unit-tool-contracts.js +2 -1
  113. package/dist/resources/extensions/gsd/user-input-boundary.js +65 -4
  114. package/dist/resources/extensions/gsd/validation-block-guard.js +2 -0
  115. package/dist/resources/extensions/gsd/web-app-uat.js +80 -0
  116. package/dist/resources/extensions/gsd/workflow-mcp.js +15 -102
  117. package/dist/resources/extensions/gsd/workflow-reconcile.js +4 -3
  118. package/dist/resources/extensions/gsd/workflow-tool-surface.js +46 -0
  119. package/dist/resources/extensions/gsd/workspace-git-guard.js +2 -0
  120. package/dist/resources/extensions/gsd/worktree-state-projection.js +33 -4
  121. package/dist/resources/extensions/gsd/worktree-telemetry.js +12 -0
  122. package/dist/resources/extensions/shared/interview-ui.js +2 -2
  123. package/dist/resources/shared/claude-runtime-floor.js +182 -0
  124. package/dist/tsconfig.extensions.tsbuildinfo +1 -0
  125. package/dist/update-cmd.js +20 -0
  126. package/dist/web/standalone/.next/BUILD_ID +1 -1
  127. package/dist/web/standalone/.next/app-path-routes-manifest.json +5 -5
  128. package/dist/web/standalone/.next/build-manifest.json +3 -3
  129. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  130. package/dist/web/standalone/.next/react-loadable-manifest.json +8 -8
  131. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  132. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  133. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  134. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  135. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  136. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  137. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  138. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  139. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  140. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  141. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  142. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  143. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  144. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  145. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  146. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  147. package/dist/web/standalone/.next/server/app/api/boot/route.js.nft.json +1 -1
  148. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js.nft.json +1 -1
  149. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js.nft.json +1 -1
  150. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js.nft.json +1 -1
  151. package/dist/web/standalone/.next/server/app/api/captures/route.js.nft.json +1 -1
  152. package/dist/web/standalone/.next/server/app/api/cleanup/route.js.nft.json +1 -1
  153. package/dist/web/standalone/.next/server/app/api/doctor/route.js.nft.json +1 -1
  154. package/dist/web/standalone/.next/server/app/api/export-data/route.js.nft.json +1 -1
  155. package/dist/web/standalone/.next/server/app/api/files/route.js.nft.json +1 -1
  156. package/dist/web/standalone/.next/server/app/api/forensics/route.js.nft.json +1 -1
  157. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  158. package/dist/web/standalone/.next/server/app/api/git/route.js.nft.json +1 -1
  159. package/dist/web/standalone/.next/server/app/api/history/route.js.nft.json +1 -1
  160. package/dist/web/standalone/.next/server/app/api/hooks/route.js.nft.json +1 -1
  161. package/dist/web/standalone/.next/server/app/api/inspect/route.js.nft.json +1 -1
  162. package/dist/web/standalone/.next/server/app/api/knowledge/route.js.nft.json +1 -1
  163. package/dist/web/standalone/.next/server/app/api/live-state/route.js.nft.json +1 -1
  164. package/dist/web/standalone/.next/server/app/api/mcp-connections/route.js.nft.json +1 -1
  165. package/dist/web/standalone/.next/server/app/api/notifications/route.js.nft.json +1 -1
  166. package/dist/web/standalone/.next/server/app/api/onboarding/route.js.nft.json +1 -1
  167. package/dist/web/standalone/.next/server/app/api/projects/route.js.nft.json +1 -1
  168. package/dist/web/standalone/.next/server/app/api/recovery/route.js.nft.json +1 -1
  169. package/dist/web/standalone/.next/server/app/api/session/browser/route.js.nft.json +1 -1
  170. package/dist/web/standalone/.next/server/app/api/session/command/route.js.nft.json +1 -1
  171. package/dist/web/standalone/.next/server/app/api/session/events/route.js.nft.json +1 -1
  172. package/dist/web/standalone/.next/server/app/api/session/manage/route.js.nft.json +1 -1
  173. package/dist/web/standalone/.next/server/app/api/settings-data/route.js.nft.json +1 -1
  174. package/dist/web/standalone/.next/server/app/api/shutdown/route.js.nft.json +1 -1
  175. package/dist/web/standalone/.next/server/app/api/skill-health/route.js.nft.json +1 -1
  176. package/dist/web/standalone/.next/server/app/api/steer/route.js.nft.json +1 -1
  177. package/dist/web/standalone/.next/server/app/api/switch-root/route.js.nft.json +1 -1
  178. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js.nft.json +1 -1
  179. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js.nft.json +1 -1
  180. package/dist/web/standalone/.next/server/app/api/undo/route.js.nft.json +1 -1
  181. package/dist/web/standalone/.next/server/app/api/visualizer/route.js.nft.json +1 -1
  182. package/dist/web/standalone/.next/server/app/index.html +1 -1
  183. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  184. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  185. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  186. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  187. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  188. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  189. package/dist/web/standalone/.next/server/app-paths-manifest.json +5 -5
  190. package/dist/web/standalone/.next/server/chunks/5047.js +2 -0
  191. package/dist/web/standalone/.next/server/chunks/5124.js +1 -0
  192. package/dist/web/standalone/.next/server/chunks/8357.js +2 -2
  193. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  194. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  195. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  196. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  197. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  198. package/dist/web/standalone/.next/static/chunks/2659.b7b129ee6a769448.js +1 -0
  199. package/dist/web/standalone/.next/static/chunks/2772.bfa657f49f955239.js +1 -0
  200. package/dist/web/standalone/.next/static/chunks/{3616.4113d484a994e411.js → 3616.3c60753b8ffcbd2e.js} +1 -1
  201. package/dist/web/standalone/.next/static/chunks/4283.e4873b058df143a1.js +2 -0
  202. package/dist/web/standalone/.next/static/chunks/5826.a46ecdd1cfe8dabc.js +1 -0
  203. package/dist/web/standalone/.next/static/chunks/796.cf859a427a2cb2ac.js +10 -0
  204. package/dist/web/standalone/.next/static/chunks/8785.2e5a118797fb2dd2.js +1 -0
  205. package/dist/web/standalone/.next/static/chunks/{webpack-dda80a1ef5587410.js → webpack-fbea77b5f9953368.js} +1 -1
  206. package/dist/web/standalone/node_modules/@gsd/native/package.json +1 -1
  207. package/dist/web/standalone/node_modules/node-pty/build/Makefile +1 -1
  208. package/dist/web/standalone/node_modules/postcss/lib/container.js +26 -18
  209. package/dist/web/standalone/node_modules/postcss/lib/css-syntax-error.js +47 -14
  210. package/dist/web/standalone/node_modules/postcss/lib/declaration.js +4 -4
  211. package/dist/web/standalone/node_modules/postcss/lib/fromJSON.js +3 -3
  212. package/dist/web/standalone/node_modules/postcss/lib/input.js +54 -29
  213. package/dist/web/standalone/node_modules/postcss/lib/lazy-result.js +47 -37
  214. package/dist/web/standalone/node_modules/postcss/lib/map-generator.js +26 -9
  215. package/dist/web/standalone/node_modules/postcss/lib/no-work-result.js +57 -55
  216. package/dist/web/standalone/node_modules/postcss/lib/node.js +99 -31
  217. package/dist/web/standalone/node_modules/postcss/lib/parse.js +1 -1
  218. package/dist/web/standalone/node_modules/postcss/lib/parser.js +10 -9
  219. package/dist/web/standalone/node_modules/postcss/lib/postcss.js +12 -12
  220. package/dist/web/standalone/node_modules/postcss/lib/previous-map.js +30 -11
  221. package/dist/web/standalone/node_modules/postcss/lib/processor.js +7 -7
  222. package/dist/web/standalone/node_modules/postcss/lib/result.js +5 -5
  223. package/dist/web/standalone/node_modules/postcss/lib/rule.js +6 -6
  224. package/dist/web/standalone/node_modules/postcss/lib/stringifier.js +69 -28
  225. package/dist/web/standalone/node_modules/postcss/lib/tokenize.js +6 -2
  226. package/dist/web/standalone/node_modules/postcss/package.json +48 -48
  227. package/dist/web-mode.d.ts +2 -0
  228. package/dist/web-mode.js +20 -8
  229. package/package.json +17 -11
  230. package/packages/cloud-mcp-gateway/package.json +2 -2
  231. package/packages/contracts/package.json +1 -1
  232. package/packages/daemon/package.json +4 -4
  233. package/packages/gsd-agent-core/dist/session/agent-session-extensions.d.ts +2 -0
  234. package/packages/gsd-agent-core/dist/session/agent-session-extensions.d.ts.map +1 -1
  235. package/packages/gsd-agent-core/dist/session/agent-session-extensions.js +14 -0
  236. package/packages/gsd-agent-core/dist/session/agent-session-extensions.js.map +1 -1
  237. package/packages/gsd-agent-core/package.json +5 -5
  238. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  239. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js +3 -0
  240. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js.map +1 -1
  241. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js +1 -1
  242. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js.map +1 -1
  243. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  244. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js +106 -40
  245. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  246. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-extension-widgets.d.ts.map +1 -1
  247. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-extension-widgets.js +6 -0
  248. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-extension-widgets.js.map +1 -1
  249. package/packages/gsd-agent-modes/package.json +7 -7
  250. package/packages/mcp-server/dist/server.d.ts +10 -0
  251. package/packages/mcp-server/dist/server.d.ts.map +1 -1
  252. package/packages/mcp-server/dist/server.js +8 -0
  253. package/packages/mcp-server/dist/server.js.map +1 -1
  254. package/packages/mcp-server/dist/workflow-tools.d.ts +41 -0
  255. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  256. package/packages/mcp-server/dist/workflow-tools.js +2 -1
  257. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  258. package/packages/mcp-server/package.json +3 -3
  259. package/packages/native/package.json +1 -1
  260. package/packages/pi-agent-core/package.json +1 -1
  261. package/packages/pi-ai/dist/image-models.generated.d.ts +30 -0
  262. package/packages/pi-ai/dist/image-models.generated.d.ts.map +1 -1
  263. package/packages/pi-ai/dist/image-models.generated.js +30 -0
  264. package/packages/pi-ai/dist/image-models.generated.js.map +1 -1
  265. package/packages/pi-ai/dist/models.generated.d.ts +8 -127
  266. package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
  267. package/packages/pi-ai/dist/models.generated.js +47 -166
  268. package/packages/pi-ai/dist/models.generated.js.map +1 -1
  269. package/packages/pi-ai/package.json +1 -1
  270. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts.map +1 -1
  271. package/packages/pi-coding-agent/dist/core/auth-storage.js +11 -3
  272. package/packages/pi-coding-agent/dist/core/auth-storage.js.map +1 -1
  273. package/packages/pi-coding-agent/package.json +7 -7
  274. package/packages/pi-tui/dist/components/input.js +1 -1
  275. package/packages/pi-tui/dist/components/input.js.map +1 -1
  276. package/packages/pi-tui/dist/keys.d.ts.map +1 -1
  277. package/packages/pi-tui/dist/keys.js +39 -30
  278. package/packages/pi-tui/dist/keys.js.map +1 -1
  279. package/packages/pi-tui/dist/stdin-buffer.d.ts.map +1 -1
  280. package/packages/pi-tui/dist/stdin-buffer.js +22 -0
  281. package/packages/pi-tui/dist/stdin-buffer.js.map +1 -1
  282. package/packages/pi-tui/package.json +2 -2
  283. package/packages/rpc-client/package.json +2 -2
  284. package/pkg/package.json +1 -1
  285. package/scripts/install/deps.js +10 -0
  286. package/scripts/link-workspace-packages.cjs +7 -40
  287. package/src/resources/extensions/ask-user-questions.ts +87 -24
  288. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +126 -289
  289. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +242 -2
  290. package/src/resources/extensions/claude-code-cli/turn-assembler.ts +287 -0
  291. package/src/resources/extensions/github-sync/templates.ts +3 -3
  292. package/src/resources/extensions/github-sync/tests/templates.test.ts +2 -2
  293. package/src/resources/extensions/gsd/artifact-projection.ts +31 -0
  294. package/src/resources/extensions/gsd/auto/contracts.ts +40 -121
  295. package/src/resources/extensions/gsd/auto/loop-deps.ts +2 -0
  296. package/src/resources/extensions/gsd/auto/loop.ts +83 -61
  297. package/src/resources/extensions/gsd/auto/orchestrator.ts +913 -64
  298. package/src/resources/extensions/gsd/auto/phases.ts +35 -3
  299. package/src/resources/extensions/gsd/auto/run-unit.ts +2 -1
  300. package/src/resources/extensions/gsd/auto/session.ts +4 -0
  301. package/src/resources/extensions/gsd/auto-dashboard.ts +18 -4
  302. package/src/resources/extensions/gsd/auto-dispatch.ts +20 -7
  303. package/src/resources/extensions/gsd/auto-model-selection.ts +8 -0
  304. package/src/resources/extensions/gsd/auto-post-unit.ts +4 -3
  305. package/src/resources/extensions/gsd/auto-prompts.ts +220 -9
  306. package/src/resources/extensions/gsd/auto-recovery.ts +50 -50
  307. package/src/resources/extensions/gsd/auto-runtime-state.ts +30 -0
  308. package/src/resources/extensions/gsd/auto-start.ts +17 -20
  309. package/src/resources/extensions/gsd/auto-timers.ts +16 -2
  310. package/src/resources/extensions/gsd/auto-tool-tracking.ts +40 -0
  311. package/src/resources/extensions/gsd/auto-unit-tool-scope.ts +42 -30
  312. package/src/resources/extensions/gsd/auto-verification.ts +7 -8
  313. package/src/resources/extensions/gsd/auto-worktree.ts +57 -42
  314. package/src/resources/extensions/gsd/auto.ts +96 -508
  315. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +29 -37
  316. package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +10 -37
  317. package/src/resources/extensions/gsd/bootstrap/query-tools.ts +2 -2
  318. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +120 -151
  319. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +107 -3
  320. package/src/resources/extensions/gsd/closeout-consistency-gate.ts +27 -5
  321. package/src/resources/extensions/gsd/codebase-generator.ts +9 -5
  322. package/src/resources/extensions/gsd/commands/handlers/auto.ts +3 -0
  323. package/src/resources/extensions/gsd/commands-handlers.ts +18 -0
  324. package/src/resources/extensions/gsd/commands-inspect.ts +7 -8
  325. package/src/resources/extensions/gsd/commands-maintenance.ts +74 -40
  326. package/src/resources/extensions/gsd/commands-ship.ts +2 -2
  327. package/src/resources/extensions/gsd/commands-verdict.ts +19 -2
  328. package/src/resources/extensions/gsd/db-workspace.ts +170 -0
  329. package/src/resources/extensions/gsd/debug-logger.ts +11 -0
  330. package/src/resources/extensions/gsd/delegation-policy.ts +3 -11
  331. package/src/resources/extensions/gsd/discussion-handoff.ts +276 -0
  332. package/src/resources/extensions/gsd/docs/preferences-reference.md +9 -0
  333. package/src/resources/extensions/gsd/doctor-proactive.ts +8 -2
  334. package/src/resources/extensions/gsd/doctor.ts +15 -5
  335. package/src/resources/extensions/gsd/error-classifier.ts +1 -1
  336. package/src/resources/extensions/gsd/git-conflict-state.ts +17 -1
  337. package/src/resources/extensions/gsd/gsd-db.ts +12 -0
  338. package/src/resources/extensions/gsd/guided-flow.ts +49 -560
  339. package/src/resources/extensions/gsd/guided-unit-completion.ts +275 -0
  340. package/src/resources/extensions/gsd/markdown-renderer.ts +40 -20
  341. package/src/resources/extensions/gsd/mcp-filter.ts +9 -1
  342. package/src/resources/extensions/gsd/mcp-tool-name.ts +35 -0
  343. package/src/resources/extensions/gsd/md-importer.ts +3 -3
  344. package/src/resources/extensions/gsd/migrate/safety.ts +2 -2
  345. package/src/resources/extensions/gsd/migration-auto-check.ts +2 -2
  346. package/src/resources/extensions/gsd/milestone-closeout-proof.ts +131 -0
  347. package/src/resources/extensions/gsd/milestone-closeout.ts +12 -4
  348. package/src/resources/extensions/gsd/milestone-merge-transaction.ts +47 -0
  349. package/src/resources/extensions/gsd/milestone-planning-persistence.ts +224 -0
  350. package/src/resources/extensions/gsd/milestone-readiness.ts +125 -0
  351. package/src/resources/extensions/gsd/milestone-settlement.ts +81 -0
  352. package/src/resources/extensions/gsd/milestone-validation-evidence.ts +95 -0
  353. package/src/resources/extensions/gsd/milestone-validation-verdict.ts +80 -0
  354. package/src/resources/extensions/gsd/native-git-bridge.ts +48 -0
  355. package/src/resources/extensions/gsd/parallel-eligibility.ts +4 -5
  356. package/src/resources/extensions/gsd/parallel-orchestrator.ts +6 -2
  357. package/src/resources/extensions/gsd/preferences-diagnostics.ts +98 -0
  358. package/src/resources/extensions/gsd/preferences-types.ts +16 -0
  359. package/src/resources/extensions/gsd/preferences.ts +173 -28
  360. package/src/resources/extensions/gsd/prompts/complete-slice.md +1 -0
  361. package/src/resources/extensions/gsd/prompts/discuss.md +6 -7
  362. package/src/resources/extensions/gsd/prompts/execute-task.md +2 -0
  363. package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +5 -7
  364. package/src/resources/extensions/gsd/prompts/guided-discuss-project.md +6 -6
  365. package/src/resources/extensions/gsd/prompts/guided-discuss-requirements.md +1 -2
  366. package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +5 -6
  367. package/src/resources/extensions/gsd/prompts/plan-milestone.md +2 -0
  368. package/src/resources/extensions/gsd/prompts/plan-slice.md +2 -1
  369. package/src/resources/extensions/gsd/prompts/refine-slice.md +1 -0
  370. package/src/resources/extensions/gsd/prompts/research-milestone.md +2 -2
  371. package/src/resources/extensions/gsd/prompts/system.md +1 -1
  372. package/src/resources/extensions/gsd/prompts/validate-milestone.md +5 -3
  373. package/src/resources/extensions/gsd/provider-payload-policy.ts +140 -0
  374. package/src/resources/extensions/gsd/pull-request-process.ts +41 -0
  375. package/src/resources/extensions/gsd/quality-gate-closure.ts +140 -0
  376. package/src/resources/extensions/gsd/question-transport.ts +138 -0
  377. package/src/resources/extensions/gsd/roadmap-slices.ts +8 -2
  378. package/src/resources/extensions/gsd/schemas/parsers.ts +6 -1
  379. package/src/resources/extensions/gsd/slice-parallel-orchestrator.ts +6 -2
  380. package/src/resources/extensions/gsd/state-reconciliation/drift/artifact-db.ts +31 -10
  381. package/src/resources/extensions/gsd/state.ts +15 -5
  382. package/src/resources/extensions/gsd/templates/plan.md +7 -0
  383. package/src/resources/extensions/gsd/templates/project.md +1 -0
  384. package/src/resources/extensions/gsd/templates/roadmap.md +1 -1
  385. package/src/resources/extensions/gsd/templates/uat.md +5 -1
  386. package/src/resources/extensions/gsd/tests/artifact-db-drift-memo.test.ts +66 -0
  387. package/src/resources/extensions/gsd/tests/ask-user-questions-render.test.ts +92 -0
  388. package/src/resources/extensions/gsd/tests/auto-dashboard.test.ts +29 -1
  389. package/src/resources/extensions/gsd/tests/auto-dispatch-baseline-harness.test.ts +53 -0
  390. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +321 -5
  391. package/src/resources/extensions/gsd/tests/auto-milestone-target.test.ts +23 -0
  392. package/src/resources/extensions/gsd/tests/auto-model-selection-tool-poisoning.test.ts +18 -0
  393. package/src/resources/extensions/gsd/tests/auto-orchestrator.test.ts +709 -845
  394. package/src/resources/extensions/gsd/tests/auto-paused-ui-cleanup.test.ts +38 -10
  395. package/src/resources/extensions/gsd/tests/auto-runtime-state.test.ts +34 -0
  396. package/src/resources/extensions/gsd/tests/canonical-milestone-root.test.ts +20 -0
  397. package/src/resources/extensions/gsd/tests/codebase-generator.test.ts +22 -0
  398. package/src/resources/extensions/gsd/tests/commands-dispatcher-workspace-git.test.ts +11 -0
  399. package/src/resources/extensions/gsd/tests/commands-verdict.test.ts +38 -1
  400. package/src/resources/extensions/gsd/tests/debug-logger.test.ts +15 -0
  401. package/src/resources/extensions/gsd/tests/dispatch-complete-milestone-guard.test.ts +34 -3
  402. package/src/resources/extensions/gsd/tests/dispatch-run-uat-browser-tools.test.ts +88 -0
  403. package/src/resources/extensions/gsd/tests/doctor-scope-db-unavailable.test.ts +18 -0
  404. package/src/resources/extensions/gsd/tests/execute-summary-save-empty-project.test.ts +64 -1
  405. package/src/resources/extensions/gsd/tests/execute-task-rendering.test.ts +1 -0
  406. package/src/resources/extensions/gsd/tests/fixtures/pr-body/swarm-lane-no-blockers.md +1 -5
  407. package/src/resources/extensions/gsd/tests/fixtures/pr-body/swarm-lane-with-blockers.md +1 -5
  408. package/src/resources/extensions/gsd/tests/gate-state-canonicalization.test.ts +48 -1
  409. package/src/resources/extensions/gsd/tests/integration/merge-strategy-regular.test.ts +157 -0
  410. package/src/resources/extensions/gsd/tests/markdown-renderer-parse-cache.test.ts +75 -0
  411. package/src/resources/extensions/gsd/tests/mcp-tool-name.test.ts +34 -0
  412. package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +58 -0
  413. package/src/resources/extensions/gsd/tests/milestone-closeout-proof.test.ts +99 -0
  414. package/src/resources/extensions/gsd/tests/milestone-closeout.test.ts +25 -0
  415. package/src/resources/extensions/gsd/tests/milestone-merge-transaction.test.ts +46 -0
  416. package/src/resources/extensions/gsd/tests/milestone-readiness.test.ts +65 -0
  417. package/src/resources/extensions/gsd/tests/milestone-validation-evidence.test.ts +41 -0
  418. package/src/resources/extensions/gsd/tests/milestone-validation-verdict.test.ts +55 -0
  419. package/src/resources/extensions/gsd/tests/native-merge-regular.test.ts +139 -0
  420. package/src/resources/extensions/gsd/tests/orchestrator-legacy-parity.test.ts +127 -0
  421. package/src/resources/extensions/gsd/tests/parse-project-milestone-bridge.test.ts +77 -0
  422. package/src/resources/extensions/gsd/tests/plan-milestone.test.ts +45 -0
  423. package/src/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +6 -2
  424. package/src/resources/extensions/gsd/tests/planning-crossval.test.ts +45 -0
  425. package/src/resources/extensions/gsd/tests/preferences-diagnostics.test.ts +67 -0
  426. package/src/resources/extensions/gsd/tests/preferences.test.ts +183 -0
  427. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +75 -2
  428. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +9 -0
  429. package/src/resources/extensions/gsd/tests/provider-payload-policy.test.ts +165 -0
  430. package/src/resources/extensions/gsd/tests/pull-request-process.test.ts +47 -0
  431. package/src/resources/extensions/gsd/tests/register-hooks-depth-verification.test.ts +94 -0
  432. package/src/resources/extensions/gsd/tests/research-milestone-composer.test.ts +65 -0
  433. package/src/resources/extensions/gsd/tests/roadmap-parse-regression.test.ts +40 -0
  434. package/src/resources/extensions/gsd/tests/runtime-invariant-modules.test.ts +25 -1
  435. package/src/resources/extensions/gsd/tests/session-start-footer.test.ts +80 -0
  436. package/src/resources/extensions/gsd/tests/single-writer-invariant.test.ts +101 -1
  437. package/src/resources/extensions/gsd/tests/stale-queued-milestone.test.ts +27 -0
  438. package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +21 -6
  439. package/src/resources/extensions/gsd/tests/token-tool-gating.test.ts +38 -0
  440. package/src/resources/extensions/gsd/tests/tool-availability-audit.test.ts +35 -0
  441. package/src/resources/extensions/gsd/tests/tool-naming.test.ts +35 -42
  442. package/src/resources/extensions/gsd/tests/uat-policy.test.ts +23 -0
  443. package/src/resources/extensions/gsd/tests/unit-context-composer.test.ts +47 -0
  444. package/src/resources/extensions/gsd/tests/user-input-boundary.test.ts +147 -0
  445. package/src/resources/extensions/gsd/tests/validate-milestone-stuck-guard.test.ts +39 -0
  446. package/src/resources/extensions/gsd/tests/web-app-uat.test.ts +150 -0
  447. package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +126 -9
  448. package/src/resources/extensions/gsd/tests/workspace-git-preflight.test.ts +15 -0
  449. package/src/resources/extensions/gsd/tests/worktree-manager.test.ts +21 -0
  450. package/src/resources/extensions/gsd/tests/worktree-projection-writers.test.ts +1 -1
  451. package/src/resources/extensions/gsd/tests/worktree-safety.test.ts +24 -0
  452. package/src/resources/extensions/gsd/tests/worktree-telemetry.test.ts +22 -0
  453. package/src/resources/extensions/gsd/tests/write-gate-planning-unit.test.ts +15 -3
  454. package/src/resources/extensions/gsd/tests/write-gate.test.ts +79 -0
  455. package/src/resources/extensions/gsd/tool-contract.ts +86 -8
  456. package/src/resources/extensions/gsd/tool-presentation-plan.ts +16 -33
  457. package/src/resources/extensions/gsd/tool-surface-snapshot.ts +47 -0
  458. package/src/resources/extensions/gsd/tools/plan-milestone.ts +19 -160
  459. package/src/resources/extensions/gsd/tools/reassess-roadmap.ts +43 -0
  460. package/src/resources/extensions/gsd/tools/validate-milestone.ts +25 -84
  461. package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +183 -21
  462. package/src/resources/extensions/gsd/uat-policy.ts +19 -10
  463. package/src/resources/extensions/gsd/uat-run.ts +10 -14
  464. package/src/resources/extensions/gsd/unit-context-composer.ts +85 -20
  465. package/src/resources/extensions/gsd/unit-runtime.ts +3 -2
  466. package/src/resources/extensions/gsd/unit-tool-contracts.ts +2 -1
  467. package/src/resources/extensions/gsd/user-input-boundary.ts +55 -5
  468. package/src/resources/extensions/gsd/validation-block-guard.ts +2 -0
  469. package/src/resources/extensions/gsd/web-app-uat.ts +101 -0
  470. package/src/resources/extensions/gsd/workflow-mcp.ts +22 -110
  471. package/src/resources/extensions/gsd/workflow-reconcile.ts +3 -3
  472. package/src/resources/extensions/gsd/workflow-tool-surface.ts +73 -0
  473. package/src/resources/extensions/gsd/workspace-git-guard.ts +1 -0
  474. package/src/resources/extensions/gsd/worktree-lifecycle.ts +7 -16
  475. package/src/resources/extensions/gsd/worktree-state-projection.ts +55 -7
  476. package/src/resources/extensions/gsd/worktree-telemetry.ts +16 -0
  477. package/src/resources/extensions/shared/interview-ui.ts +15 -2
  478. package/src/resources/shared/claude-runtime-floor.ts +248 -0
  479. package/dist/web/standalone/.next/server/chunks/678.js +0 -2
  480. package/dist/web/standalone/.next/static/chunks/2659.feb6499ca863ebfc.js +0 -1
  481. package/dist/web/standalone/.next/static/chunks/2772.151789db0edea835.js +0 -1
  482. package/dist/web/standalone/.next/static/chunks/4283.10a065467b5340d8.js +0 -2
  483. package/dist/web/standalone/.next/static/chunks/5826.960dc4634cc9b0d3.js +0 -1
  484. package/dist/web/standalone/.next/static/chunks/796.46f811c0fac23aab.js +0 -10
  485. package/dist/web/standalone/.next/static/chunks/8785.d32f7a61f55c1600.js +0 -1
  486. package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/gsd-widget-prototype.d.ts +0 -21
  487. package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/gsd-widget-prototype.d.ts.map +0 -1
  488. package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/gsd-widget-prototype.js +0 -213
  489. package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/gsd-widget-prototype.js.map +0 -1
  490. package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/transcript-density-prototype.d.ts +0 -28
  491. package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/transcript-density-prototype.d.ts.map +0 -1
  492. package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/transcript-density-prototype.js +0 -249
  493. package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/transcript-density-prototype.js.map +0 -1
  494. package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/transcript-design-prototype.d.ts +0 -19
  495. package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/transcript-design-prototype.d.ts.map +0 -1
  496. package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/transcript-design-prototype.js +0 -797
  497. package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/transcript-design-prototype.js.map +0 -1
  498. package/scripts/ensure-workspace-builds.cjs +0 -129
  499. /package/dist/web/standalone/.next/static/{tJOKQbQRO-9MiFDO8DIDS → tkLHUSzPA2kMmWz4DmGwI}/_buildManifest.js +0 -0
  500. /package/dist/web/standalone/.next/static/{tJOKQbQRO-9MiFDO8DIDS → tkLHUSzPA2kMmWz4DmGwI}/_ssgManifest.js +0 -0
@@ -1,15 +1,7 @@
1
1
  // Project/App: gsd-pi
2
2
  // File Purpose: Plans milestone roadmap state through DB-backed workflow tools.
3
- import { clearParseCache } from "../files.js";
4
- import { isClosedStatus } from "../status-guards.js";
5
3
  import { isNonEmptyString, validateStringArray, validateTitle } from "../validation.js";
6
- import { transaction, getMilestone, getMilestoneSlices, getSlice, insertMilestone, insertSlice, upsertMilestonePlanning, upsertSlicePlanning, } from "../gsd-db.js";
7
- import { invalidateStateCache } from "../state.js";
8
- import { renderRoadmapFromDb } from "../markdown-renderer.js";
9
- import { renderAllProjections } from "../workflow-projections.js";
10
- import { writeManifest } from "../workflow-manifest.js";
11
- import { appendEvent } from "../workflow-events.js";
12
- import { logWarning } from "../workflow-logger.js";
4
+ import { persistMilestonePlan } from "../milestone-planning-persistence.js";
13
5
  function validateRiskEntries(value) {
14
6
  if (!Array.isArray(value)) {
15
7
  throw new Error("keyRisks must be an array");
@@ -43,10 +35,16 @@ function validateProofStrategy(value) {
43
35
  return { riskOrUnknown, retireIn, whatWillBeProven };
44
36
  });
45
37
  }
38
+ const SLICE_ID_RE = /^[A-Za-z0-9][A-Za-z0-9-]*$/;
46
39
  function validateSlices(value) {
47
40
  if (!Array.isArray(value) || value.length === 0) {
48
41
  throw new Error("slices must be a non-empty array");
49
42
  }
43
+ // Pre-collect all slice IDs so depends cross-validation can reference the full set.
44
+ const allSliceIds = new Set(value
45
+ .filter((e) => !!e && typeof e === "object")
46
+ .map(e => e.sliceId)
47
+ .filter((id) => isNonEmptyString(id)));
50
48
  const seen = new Set();
51
49
  return value.map((entry, index) => {
52
50
  if (!entry || typeof entry !== "object") {
@@ -84,8 +82,13 @@ function validateSlices(value) {
84
82
  throw new Error(`slices[${index}].title is invalid: ${titleIssue}`);
85
83
  if (!isNonEmptyString(risk))
86
84
  throw new Error(`slices[${index}].risk must be a non-empty string`);
87
- if (!Array.isArray(depends) || depends.some((item) => !isNonEmptyString(item))) {
88
- throw new Error(`slices[${index}].depends must be an array of non-empty strings`);
85
+ if (!Array.isArray(depends) || depends.some((item) => !isNonEmptyString(item) || !SLICE_ID_RE.test(item))) {
86
+ throw new Error(`slices[${index}].depends must be an array of valid slice IDs (e.g. "S01")`);
87
+ }
88
+ for (const dep of depends) {
89
+ if (!allSliceIds.has(dep)) {
90
+ throw new Error(`slices[${index}].depends references unknown slice "${dep}" — check that it is defined in the same milestone`);
91
+ }
89
92
  }
90
93
  if (!isNonEmptyString(demo))
91
94
  throw new Error(`slices[${index}].demo must be a non-empty string`);
@@ -160,136 +163,5 @@ export async function handlePlanMilestone(rawParams, basePath) {
160
163
  catch (err) {
161
164
  return { error: `validation failed: ${err.message}` };
162
165
  }
163
- // ── Guards + DB writes inside a single transaction (prevents TOCTOU) ───
164
- // Guards must be inside the transaction so the state they check cannot
165
- // change between the read and the write (#2723).
166
- let guardError = null;
167
- try {
168
- transaction(() => {
169
- const existingMilestone = getMilestone(params.milestoneId);
170
- if (existingMilestone && isClosedStatus(existingMilestone.status)) {
171
- guardError = `cannot re-plan milestone ${params.milestoneId}: it is already complete`;
172
- return;
173
- }
174
- // Guard: refuse to re-plan a milestone that would drop completed slices (#2960).
175
- // Allow re-planning when all completed slices are still present in the
176
- // incoming plan — their status is preserved below (#2558). Block only when
177
- // the new plan omits a completed slice, which could shadow completed work.
178
- const existingSlices = getMilestoneSlices(params.milestoneId);
179
- const completedSlices = existingSlices.filter(s => isClosedStatus(s.status));
180
- if (completedSlices.length > 0) {
181
- const incomingSliceIds = new Set(params.slices.map(s => s.sliceId));
182
- const droppedCompleted = completedSlices.filter(s => !incomingSliceIds.has(s.id));
183
- if (droppedCompleted.length > 0) {
184
- guardError = `cannot re-plan milestone ${params.milestoneId}: ${droppedCompleted.length} completed slice(s) would be dropped (${droppedCompleted.map(s => s.id).join(", ")}). Use gsd_reassess_roadmap to modify the roadmap.`;
185
- return;
186
- }
187
- }
188
- // Validate depends_on: all dependencies must exist and be complete
189
- if (params.dependsOn && params.dependsOn.length > 0) {
190
- for (const depId of params.dependsOn) {
191
- const dep = getMilestone(depId);
192
- if (!dep) {
193
- guardError = `depends_on references unknown milestone: ${depId}`;
194
- return;
195
- }
196
- if (!isClosedStatus(dep.status)) {
197
- guardError = `depends_on milestone ${depId} is not yet complete (status: ${dep.status})`;
198
- return;
199
- }
200
- }
201
- }
202
- insertMilestone({
203
- id: params.milestoneId,
204
- title: params.title,
205
- status: params.status ?? "active",
206
- depends_on: params.dependsOn ?? [],
207
- });
208
- upsertMilestonePlanning(params.milestoneId, {
209
- title: params.title,
210
- status: params.status ?? "active",
211
- depends_on: params.dependsOn ?? [],
212
- vision: params.vision,
213
- successCriteria: params.successCriteria,
214
- keyRisks: params.keyRisks,
215
- proofStrategy: params.proofStrategy,
216
- verificationContract: params.verificationContract,
217
- verificationIntegration: params.verificationIntegration,
218
- verificationOperational: params.verificationOperational,
219
- verificationUat: params.verificationUat,
220
- definitionOfDone: params.definitionOfDone,
221
- requirementCoverage: params.requirementCoverage,
222
- boundaryMapMarkdown: params.boundaryMapMarkdown,
223
- });
224
- for (let i = 0; i < params.slices.length; i++) {
225
- const slice = params.slices[i];
226
- // Preserve completed/done status on re-plan (#2558).
227
- // Without this, a re-plan after milestone transition would reset
228
- // already-completed slices back to "pending".
229
- const existing = getSlice(params.milestoneId, slice.sliceId);
230
- const status = existing && (existing.status === "complete" || existing.status === "done")
231
- ? existing.status
232
- : "pending";
233
- insertSlice({
234
- id: slice.sliceId,
235
- milestoneId: params.milestoneId,
236
- title: slice.title,
237
- status,
238
- risk: slice.risk,
239
- depends: slice.depends,
240
- demo: slice.demo,
241
- sequence: i + 1, // Preserve agent-ordered sequence (#3356)
242
- // ADR-011: pass undefined through so ON CONFLICT preserves existing values
243
- // when the caller omitted the fields on a re-plan.
244
- isSketch: slice.isSketch,
245
- sketchScope: slice.sketchScope,
246
- });
247
- upsertSlicePlanning(params.milestoneId, slice.sliceId, {
248
- goal: slice.goal,
249
- successCriteria: slice.successCriteria,
250
- proofLevel: slice.proofLevel,
251
- integrationClosure: slice.integrationClosure,
252
- observabilityImpact: slice.observabilityImpact,
253
- });
254
- }
255
- });
256
- }
257
- catch (err) {
258
- return { error: `db write failed: ${err.message}` };
259
- }
260
- if (guardError) {
261
- return { error: guardError };
262
- }
263
- let roadmapPath;
264
- try {
265
- const renderResult = await renderRoadmapFromDb(basePath, params.milestoneId);
266
- roadmapPath = renderResult.roadmapPath;
267
- }
268
- catch (renderErr) {
269
- logWarning("tool", `plan_milestone — render failed (DB rows preserved for debugging): ${renderErr.message}`);
270
- invalidateStateCache();
271
- return { error: `render failed: ${renderErr.message}` };
272
- }
273
- invalidateStateCache();
274
- clearParseCache();
275
- // ── Post-mutation hook: projections, manifest, event log ───────────────
276
- try {
277
- await renderAllProjections(basePath, params.milestoneId);
278
- writeManifest(basePath);
279
- appendEvent(basePath, {
280
- cmd: "plan-milestone",
281
- params: { milestoneId: params.milestoneId },
282
- ts: new Date().toISOString(),
283
- actor: "agent",
284
- actor_name: params.actorName,
285
- trigger_reason: params.triggerReason,
286
- });
287
- }
288
- catch (hookErr) {
289
- logWarning("tool", `plan-milestone post-mutation hook warning: ${hookErr.message}`);
290
- }
291
- return {
292
- milestoneId: params.milestoneId,
293
- roadmapPath,
294
- };
166
+ return persistMilestonePlan(params, basePath);
295
167
  }
@@ -31,6 +31,7 @@ function validateParams(params) {
31
31
  if (!Array.isArray(params.sliceChanges.removed)) {
32
32
  throw new Error("sliceChanges.removed must be an array");
33
33
  }
34
+ const SLICE_ID_RE = /^[A-Za-z0-9][A-Za-z0-9-]*$/;
34
35
  // Validate each modified slice
35
36
  for (let i = 0; i < params.sliceChanges.modified.length; i++) {
36
37
  const s = params.sliceChanges.modified[i];
@@ -40,6 +41,11 @@ function validateParams(params) {
40
41
  throw new Error(`sliceChanges.modified[${i}].sliceId is required`);
41
42
  if (!isNonEmptyString(s.title))
42
43
  throw new Error(`sliceChanges.modified[${i}].title is required`);
44
+ if (s.depends !== undefined) {
45
+ if (!Array.isArray(s.depends) || s.depends.some((item) => !isNonEmptyString(item) || !SLICE_ID_RE.test(item))) {
46
+ throw new Error(`sliceChanges.modified[${i}].depends must be an array of valid slice IDs (e.g. "S01")`);
47
+ }
48
+ }
43
49
  }
44
50
  // Validate each added slice
45
51
  for (let i = 0; i < params.sliceChanges.added.length; i++) {
@@ -50,6 +56,11 @@ function validateParams(params) {
50
56
  throw new Error(`sliceChanges.added[${i}].sliceId is required`);
51
57
  if (!isNonEmptyString(s.title))
52
58
  throw new Error(`sliceChanges.added[${i}].title is required`);
59
+ if (s.depends !== undefined) {
60
+ if (!Array.isArray(s.depends) || s.depends.some((item) => !isNonEmptyString(item) || !SLICE_ID_RE.test(item))) {
61
+ throw new Error(`sliceChanges.added[${i}].depends must be an array of valid slice IDs (e.g. "S01")`);
62
+ }
63
+ }
53
64
  }
54
65
  return params;
55
66
  }
@@ -111,6 +122,34 @@ export async function handleReassessRoadmap(rawParams, basePath) {
111
122
  return;
112
123
  }
113
124
  }
125
+ // Cross-milestone depends validation — effective slice ID set after this reassessment
126
+ const removedIds = new Set(params.sliceChanges.removed);
127
+ const effectiveSliceIds = new Set(existingSlices.map(s => s.id).filter(id => !removedIds.has(id)));
128
+ for (const added of params.sliceChanges.added) {
129
+ effectiveSliceIds.add(added.sliceId);
130
+ }
131
+ for (let i = 0; i < params.sliceChanges.modified.length; i++) {
132
+ const mod = params.sliceChanges.modified[i];
133
+ if (mod.depends !== undefined) {
134
+ for (const dep of mod.depends) {
135
+ if (!effectiveSliceIds.has(dep)) {
136
+ guardError = `sliceChanges.modified[${i}].depends references unknown slice "${dep}" — check that it is defined in this milestone`;
137
+ return;
138
+ }
139
+ }
140
+ }
141
+ }
142
+ for (let i = 0; i < params.sliceChanges.added.length; i++) {
143
+ const added = params.sliceChanges.added[i];
144
+ if (added.depends !== undefined) {
145
+ for (const dep of added.depends) {
146
+ if (!effectiveSliceIds.has(dep)) {
147
+ guardError = `sliceChanges.added[${i}].depends references unknown slice "${dep}" — check that it is defined in this milestone`;
148
+ return;
149
+ }
150
+ }
151
+ }
152
+ }
114
153
  // Record assessment
115
154
  insertAssessment({
116
155
  path: assessmentRelPath,
@@ -11,10 +11,11 @@
11
11
  * despite passing validation.
12
12
  */
13
13
  import { join } from "node:path";
14
- import { transaction, insertAssessment, getMilestoneSlices, getMilestone, getArtifact, } from "../gsd-db.js";
15
- import { gsdProjectionRoot, clearPathCache, resolveSliceFile } from "../paths.js";
14
+ import { transaction, insertAssessment, getMilestoneSlices, getMilestone, } from "../gsd-db.js";
15
+ import { gsdProjectionRoot, clearPathCache } from "../paths.js";
16
16
  import { resolveCanonicalMilestoneRoot } from "../worktree-manager.js";
17
- import { saveFile, clearParseCache, loadFile } from "../files.js";
17
+ import { resolveWorktreeProjectRoot } from "../worktree-root.js";
18
+ import { saveFile, clearParseCache } from "../files.js";
18
19
  import { invalidateStateCache } from "../state.js";
19
20
  import { VALIDATION_VERDICTS, isValidMilestoneVerdict } from "../verdict-parser.js";
20
21
  import { insertMilestoneValidationGates } from "../milestone-validation-gates.js";
@@ -22,7 +23,7 @@ import { logWarning } from "../workflow-logger.js";
22
23
  import { UokGateRunner } from "../uok/gate-runner.js";
23
24
  import { loadEffectiveGSDPreferences } from "../preferences.js";
24
25
  import { resolveUokFlags } from "../uok/flags.js";
25
- import { compactTextParts, hasBrowserEvidenceText, hasBrowserRequiredText } from "../browser-evidence.js";
26
+ import { applyBrowserEvidenceGate, browserEvidenceGateRequiresAttention, } from "../milestone-validation-evidence.js";
26
27
  function isVerificationNotApplicable(value) {
27
28
  const v = (value ?? "").toLowerCase().trim().replace(/[.\s]+$/, "");
28
29
  if (!v || v === "none")
@@ -44,80 +45,6 @@ function getRequiredVerificationClasses(milestoneId) {
44
45
  required.push("UAT");
45
46
  return required;
46
47
  }
47
- function hasRuntimeExecutableUatEvidenceText(text) {
48
- if (!/\buatType:\s*runtime-executable\b/i.test(text))
49
- return false;
50
- if (!/\bverdict:\s*PASS\b/i.test(text))
51
- return false;
52
- return /^\|\s*[^|\n]+\s*\|\s*runtime\s*\|\s*PASS\s*\|[^|\n]*\bgsd_uat_exec\b/mi.test(text);
53
- }
54
- async function browserEvidenceGateRequiresAttention(params, basePath) {
55
- if (params.verdict !== "pass")
56
- return false;
57
- const milestone = getMilestone(params.milestoneId);
58
- const slices = getMilestoneSlices(params.milestoneId);
59
- const requirementText = compactTextParts([
60
- milestone?.vision,
61
- milestone?.success_criteria,
62
- milestone?.verification_uat,
63
- params.successCriteriaChecklist,
64
- params.verificationClasses,
65
- ...slices.flatMap((slice) => [
66
- slice.demo,
67
- slice.goal,
68
- slice.success_criteria,
69
- ]),
70
- ]);
71
- if (!hasBrowserRequiredText(requirementText))
72
- return false;
73
- // Collect per-slice evidence so the runtime bypass is checked independently
74
- // for each slice. Concatenating all slices before checking would allow runtime
75
- // evidence from one slice to cover another slice's browser requirements.
76
- const sliceEvidencePairs = [];
77
- for (const slice of slices) {
78
- const chunks = [];
79
- const artifactPath = `milestones/${params.milestoneId}/slices/${slice.id}/${slice.id}-ASSESSMENT.md`;
80
- const artifact = getArtifact(artifactPath);
81
- if (artifact?.full_content)
82
- chunks.push(artifact.full_content);
83
- const assessmentPath = resolveSliceFile(basePath, params.milestoneId, slice.id, "ASSESSMENT");
84
- const assessmentContent = assessmentPath ? await loadFile(assessmentPath) : null;
85
- if (assessmentContent)
86
- chunks.push(assessmentContent);
87
- sliceEvidencePairs.push({
88
- sliceRequirementText: compactTextParts([slice.demo, slice.goal, slice.success_criteria]),
89
- evidenceText: chunks.join("\n\n"),
90
- });
91
- }
92
- const persistedEvidence = sliceEvidencePairs.map((s) => s.evidenceText).join("\n\n");
93
- // Runtime bypass: each slice whose own requirement text has browser-observable
94
- // criteria must have its own runtime-executable UAT evidence. When no individual
95
- // slice has slice-level browser requirements (e.g., they come from milestone-level
96
- // fields only), fall back to checking whether any slice has runtime evidence.
97
- const browserRequiringSlices = sliceEvidencePairs.filter((s) => hasBrowserRequiredText(s.sliceRequirementText));
98
- const runtimeBypasses = browserRequiringSlices.length > 0
99
- ? browserRequiringSlices.every((s) => hasRuntimeExecutableUatEvidenceText(s.evidenceText))
100
- : sliceEvidencePairs.some((s) => hasRuntimeExecutableUatEvidenceText(s.evidenceText));
101
- if (runtimeBypasses)
102
- return false;
103
- const validationEvidence = compactTextParts([
104
- params.successCriteriaChecklist,
105
- params.verificationClasses,
106
- params.verdictRationale,
107
- params.remediationPlan,
108
- ]);
109
- return !hasBrowserEvidenceText(`${persistedEvidence}\n\n${validationEvidence}`);
110
- }
111
- function applyBrowserEvidenceGate(params) {
112
- const note = "Browser evidence gate: Browser-observable acceptance criteria were detected, but no persisted ASSESSMENT or validation evidence recorded browser actions with assertions. Downgraded from pass to needs-attention.";
113
- return {
114
- ...params,
115
- verdict: "needs-attention",
116
- verdictRationale: params.verdictRationale.trim()
117
- ? `${params.verdictRationale.trim()}\n\n${note}`
118
- : note,
119
- };
120
- }
121
48
  function renderValidationMarkdown(params) {
122
49
  let md = `---
123
50
  verdict: ${params.verdict}
@@ -214,6 +141,16 @@ export async function handleValidateMilestone(params, basePath, opts) {
214
141
  let projectionStale = false;
215
142
  try {
216
143
  await saveFile(validationPath, validationMd);
144
+ const projectRoot = resolveWorktreeProjectRoot(basePath);
145
+ if (projectRoot !== artifactBasePath) {
146
+ const projectValidationPath = join(gsdProjectionRoot(projectRoot), "milestones", effectiveParams.milestoneId, `${effectiveParams.milestoneId}-VALIDATION.md`);
147
+ try {
148
+ await saveFile(projectValidationPath, validationMd);
149
+ }
150
+ catch (mirrorErr) {
151
+ logWarning("projection", `validate_milestone project-root VALIDATION mirror failed for ${effectiveParams.milestoneId}`, { error: mirrorErr.message });
152
+ }
153
+ }
217
154
  }
218
155
  catch (renderErr) {
219
156
  projectionStale = true;
@@ -3,7 +3,7 @@
3
3
  import { ensureDbOpen } from "../bootstrap/dynamic-tools.js";
4
4
  import { sanitizeCompleteMilestoneParams } from "../bootstrap/sanitize-complete-milestone.js";
5
5
  import { loadWriteGateSnapshot, shouldBlockContextArtifactSaveInSnapshot, shouldBlockRootArtifactSaveInSnapshot } from "../bootstrap/write-gate.js";
6
- import { getActiveRequirements, insertMilestone, getMilestone, getSliceStatusSummary, getSliceTaskCounts, insertAssessment, insertGateRun, readTransaction, saveGateResult, upsertQualityGate, } from "../gsd-db.js";
6
+ import { getActiveRequirements, getAllMilestones, getMilestone, getSliceStatusSummary, getSliceTaskCounts, insertMilestone, insertAssessment, insertGateRun, readTransaction, saveGateResult, upsertQualityGate, } from "../gsd-db.js";
7
7
  import { GATE_REGISTRY } from "../gate-registry.js";
8
8
  import { generateRequirementsMd, saveArtifactToDb } from "../db-writer.js";
9
9
  import { clearPathCache, relSliceFile, resolveGsdPathContract, resolveMilestoneFile, resolveSliceFile } from "../paths.js";
@@ -74,6 +74,103 @@ function registerProjectMilestoneSequence(content) {
74
74
  }
75
75
  return registered;
76
76
  }
77
+ /**
78
+ * Best-effort recovery of the human one-liner for each milestone id from a
79
+ * (possibly malformed) Milestone Sequence body. Deliberately lenient: tolerates
80
+ * any separator the canonical MILESTONE_LINE_RE rejects (en-dash, " : ", a
81
+ * missing checkbox, etc.) so a model formatting slip does not discard the prose.
82
+ */
83
+ function recoverMilestoneTails(sequenceBody) {
84
+ const out = new Map();
85
+ const lenient = /^\s*(?:-\s*)?(?:\[[ xX]\]\s*)?(M\d{3})\b\s*[:.\-–—]*\s*(.*)$/;
86
+ for (const rawLine of sequenceBody.split("\n")) {
87
+ const m = rawLine.match(lenient);
88
+ if (m)
89
+ out.set(m[1], m[2].trim());
90
+ }
91
+ return out;
92
+ }
93
+ function firstSentence(text) {
94
+ const trimmed = text.trim();
95
+ if (!trimmed)
96
+ return "";
97
+ const idx = trimmed.search(/[.!?](\s|$)/);
98
+ return (idx >= 0 ? trimmed.slice(0, idx + 1) : trimmed).trim();
99
+ }
100
+ /** Render one canonical, parseable milestone line for the given DB row. */
101
+ function renderMilestoneLine(m, recoveredTail) {
102
+ const done = m.status === "complete";
103
+ let oneLiner = recoveredTail;
104
+ // The recovered tail often still carries the title (e.g. "Foo — bar" or
105
+ // "Foo : bar"). Strip a leading repetition of the title, then any separator.
106
+ if (oneLiner.toLowerCase().startsWith(m.title.toLowerCase())) {
107
+ oneLiner = oneLiner.slice(m.title.length).replace(/^\s*[:.\-–—]+\s*/, "").trim();
108
+ }
109
+ else {
110
+ const sep = oneLiner.match(/\s+(?:—|–|--|-|:)\s+/);
111
+ if (sep && sep.index !== undefined)
112
+ oneLiner = oneLiner.slice(sep.index + sep[0].length).trim();
113
+ }
114
+ // MILESTONE_LINE_RE requires non-empty prose after the separator.
115
+ if (!oneLiner)
116
+ oneLiner = firstSentence(m.vision) || (done ? "Completed." : "Planned.");
117
+ return `- [${done ? "x" : " "}] ${m.id}: ${m.title} — ${oneLiner}`;
118
+ }
119
+ /**
120
+ * Rebuild the "## Milestone Sequence" section from authoritative DB rows when a
121
+ * model-authored PROJECT.md projection parsed to zero milestone lines but the DB
122
+ * already holds milestones. The DB is the source of truth (markdown is a
123
+ * projection), so this repairs the projection rather than failing the save.
124
+ * Preserves a leading HTML comment in the section and recovers one-liners
125
+ * best-effort. The returned content parses cleanly under MILESTONE_LINE_RE.
126
+ */
127
+ function rebuildMilestoneSequenceSection(content, milestones) {
128
+ const lines = content.split("\n");
129
+ const headerIdx = lines.findIndex(l => /^##\s+Milestone Sequence\s*$/.test(l));
130
+ const canonicalLines = (() => {
131
+ // Recover tails from the existing (malformed) body when the section exists.
132
+ let body = "";
133
+ if (headerIdx !== -1) {
134
+ let end = headerIdx + 1;
135
+ while (end < lines.length && !/^##\s+/.test(lines[end]))
136
+ end++;
137
+ body = lines.slice(headerIdx + 1, end).join("\n");
138
+ }
139
+ const tails = recoverMilestoneTails(body);
140
+ return milestones.map(m => renderMilestoneLine(m, tails.get(m.id) ?? ""));
141
+ })();
142
+ if (headerIdx === -1) {
143
+ // No section at all — append a fresh, canonical one.
144
+ const sep = content.endsWith("\n") ? "" : "\n";
145
+ return `${content}${sep}\n## Milestone Sequence\n\n${canonicalLines.join("\n")}\n`;
146
+ }
147
+ let bodyEnd = headerIdx + 1;
148
+ while (bodyEnd < lines.length && !/^##\s+/.test(lines[bodyEnd]))
149
+ bodyEnd++;
150
+ const existingBody = lines.slice(headerIdx + 1, bodyEnd);
151
+ // Preserve a contiguous leading HTML comment block (the "Check off…" hint).
152
+ let i = 0;
153
+ while (i < existingBody.length && existingBody[i].trim() === "")
154
+ i++;
155
+ const preserved = [];
156
+ if (i < existingBody.length && existingBody[i].trim().startsWith("<!--")) {
157
+ while (i < existingBody.length) {
158
+ preserved.push(existingBody[i]);
159
+ const closed = existingBody[i].includes("-->");
160
+ i++;
161
+ if (closed)
162
+ break;
163
+ }
164
+ }
165
+ return [
166
+ ...lines.slice(0, headerIdx + 1),
167
+ "",
168
+ ...(preserved.length ? [...preserved, ""] : []),
169
+ ...canonicalLines,
170
+ "",
171
+ ...lines.slice(bodyEnd),
172
+ ].join("\n");
173
+ }
77
174
  async function mirrorArtifactToActiveWorktreeProjection(basePath, relativePath, content) {
78
175
  const contract = resolveGsdPathContract(basePath);
79
176
  if (!contract.worktreeGsd)
@@ -192,6 +289,7 @@ export async function executeSummarySave(params, basePath = process.cwd()) {
192
289
  }, basePath);
193
290
  await mirrorArtifactToActiveWorktreeProjection(basePath, relativePath, contentToSave);
194
291
  let registeredMilestones = [];
292
+ let milestoneSequenceSelfHealed = false;
195
293
  if (params.artifact_type === "PROJECT") {
196
294
  try {
197
295
  registeredMilestones = registerProjectMilestoneSequence(contentToSave);
@@ -227,28 +325,78 @@ export async function executeSummarySave(params, basePath = process.cwd()) {
227
325
  };
228
326
  }
229
327
  if (registeredMilestones.length === 0) {
230
- logError("tool", `gsd_summary_save: PROJECT.md saved to ${relativePath} but parsed zero milestones — registration produced no DB rows`, {
328
+ const existingMilestones = getAllMilestones();
329
+ if (existingMilestones.length === 0) {
330
+ // Genuine first-save failure: no milestones parsed AND none in the DB.
331
+ // /gsd really would report "No Active Milestone" — hard-fail so the
332
+ // caller rewrites the sequence before proceeding.
333
+ logError("tool", `gsd_summary_save: PROJECT.md saved to ${relativePath} but parsed zero milestones — registration produced no DB rows`, {
334
+ tool: "gsd_summary_save",
335
+ });
336
+ // PROJECT.md was persisted; invalidate so subsequent reads see the new
337
+ // artifacts row even though no milestones registered.
338
+ invalidateStateCache();
339
+ return {
340
+ content: [{
341
+ type: "text",
342
+ text: `Error: PROJECT.md was saved to ${relativePath} but contains zero parseable milestone lines, ` +
343
+ `so no milestones were registered in the DB. /gsd will report "No Active Milestone". ` +
344
+ `Rewrite PROJECT.md so the "Milestone Sequence" section uses canonical lines: ` +
345
+ `\`- [ ] M001: <Title> — <One-liner>\` (em-dash, double-dash \`--\`, or single-dash \`-\` separator), then re-call gsd_summary_save(PROJECT).`,
346
+ }],
347
+ details: {
348
+ operation: "save_summary",
349
+ path: relativePath,
350
+ artifact_type: params.artifact_type,
351
+ error: "milestone_registration_empty_parse",
352
+ },
353
+ isError: true,
354
+ };
355
+ }
356
+ // Existing DB rows mean this is projection drift, not data loss. Rebuild
357
+ // the section from DB state and re-persist a parseable projection.
358
+ logWarning("tool", `gsd_summary_save: PROJECT.md parsed zero milestone lines but DB has ${existingMilestones.length} — rebuilding Milestone Sequence from DB (projection self-heal)`, {
231
359
  tool: "gsd_summary_save",
360
+ path: relativePath,
232
361
  });
233
- // PROJECT.md was persisted; invalidate so subsequent reads see the new
234
- // artifacts row even though no milestones registered.
235
- invalidateStateCache();
236
- return {
237
- content: [{
238
- type: "text",
239
- text: `Error: PROJECT.md was saved to ${relativePath} but contains zero parseable milestone lines, ` +
240
- `so no milestones were registered in the DB. /gsd will report "No Active Milestone". ` +
241
- `Rewrite PROJECT.md so the "Milestone Sequence" section uses canonical lines: ` +
242
- `\`- [ ] M001: <Title> — <One-liner>\` (em-dash, double-dash \`--\`, or single-dash \`-\` separator), then re-call gsd_summary_save(PROJECT).`,
243
- }],
244
- details: {
245
- operation: "save_summary",
362
+ try {
363
+ const healed = rebuildMilestoneSequenceSection(contentToSave, existingMilestones);
364
+ await saveArtifactToDb({ path: relativePath, artifact_type: params.artifact_type, content: healed }, basePath);
365
+ await mirrorArtifactToActiveWorktreeProjection(basePath, relativePath, healed);
366
+ const healedRegisteredMilestones = registerProjectMilestoneSequence(healed);
367
+ if (healedRegisteredMilestones.length === 0) {
368
+ throw new Error("self-healed PROJECT.md still parsed zero milestone lines");
369
+ }
370
+ registeredMilestones = healedRegisteredMilestones;
371
+ milestoneSequenceSelfHealed = true;
372
+ }
373
+ catch (healErr) {
374
+ const msg = healErr instanceof Error ? healErr.message : String(healErr);
375
+ logError("tool", `gsd_summary_save: Milestone Sequence self-heal failed: ${msg}`, {
376
+ tool: "gsd_summary_save",
246
377
  path: relativePath,
247
- artifact_type: params.artifact_type,
248
- error: "milestone_registration_empty_parse",
249
- },
250
- isError: true,
251
- };
378
+ error: msg,
379
+ });
380
+ invalidateStateCache();
381
+ return {
382
+ content: [{
383
+ type: "text",
384
+ text: `Error: PROJECT.md was saved to ${relativePath} but contains zero parseable milestone lines, ` +
385
+ `and automatic DB-backed Milestone Sequence repair failed: ${msg}. ` +
386
+ `Rewrite PROJECT.md so the "Milestone Sequence" section uses canonical lines: ` +
387
+ `\`- [ ] M001: <Title> — <One-liner>\`, then re-call gsd_summary_save(PROJECT).`,
388
+ }],
389
+ details: {
390
+ operation: "save_summary",
391
+ path: relativePath,
392
+ artifact_type: params.artifact_type,
393
+ error: "milestone_sequence_self_heal_failed",
394
+ self_heal_error: msg,
395
+ },
396
+ isError: true,
397
+ };
398
+ }
399
+ invalidateStateCache();
252
400
  }
253
401
  }
254
402
  if (params.artifact_type === "CONTEXT" && !params.task_id) {
@@ -271,6 +419,7 @@ export async function executeSummarySave(params, basePath = process.cwd()) {
271
419
  artifact_type: params.artifact_type,
272
420
  content_source: contentSource,
273
421
  ...(registeredMilestones.length > 0 ? { registeredMilestones } : {}),
422
+ ...(milestoneSequenceSelfHealed ? { milestoneSequenceSelfHealed: true } : {}),
274
423
  },
275
424
  };
276
425
  }
@@ -2,6 +2,7 @@
2
2
  // File Purpose: Central UAT mode policy for dispatch, tool presentation, and result validation.
3
3
  import { extractUatType } from "./files.js";
4
4
  import { hasBrowserRequiredText } from "./browser-evidence.js";
5
+ import { parseMcpToolName } from "./mcp-tool-name.js";
5
6
  export const UAT_TYPES = [
6
7
  "artifact-driven",
7
8
  "browser-executable",
@@ -80,26 +81,31 @@ export function shouldDispatchUatForContent(content, prefs) {
80
81
  export function uatTypeIncludesBrowser(uatType) {
81
82
  return isUatType(uatType) && UAT_MODE_POLICIES[uatType].browserTools;
82
83
  }
83
- function canonicalPresentedToolName(toolName) {
84
- if (!toolName.startsWith("mcp__"))
85
- return toolName;
86
- const toolSeparator = toolName.indexOf("__", "mcp__".length);
87
- return toolSeparator >= 0 ? toolName.slice(toolSeparator + 2) : toolName;
88
- }
89
84
  export function isUatBrowserToolName(toolName) {
90
- return canonicalPresentedToolName(toolName).startsWith("browser_");
85
+ const parsed = parseMcpToolName(toolName);
86
+ const canonicalName = parsed?.toolName ?? toolName;
87
+ if (canonicalName.startsWith("browser_"))
88
+ return true;
89
+ return parsed?.toolName === "*" && parsed.serverName.toLowerCase().includes("browser");
91
90
  }
92
91
  export function hasUatBrowserToolSurface(activeTools) {
93
92
  return Array.isArray(activeTools) && activeTools.some(isUatBrowserToolName);
94
93
  }
94
+ export function resolveUatBrowserToolSurface(options) {
95
+ const surfaces = [options.activeTools, options.registeredTools].filter(Array.isArray);
96
+ if (surfaces.length === 0)
97
+ return undefined;
98
+ return [...new Set(surfaces.flat())];
99
+ }
95
100
  export function getUatBrowserToolSupportError(options) {
96
101
  if (!uatTypeIncludesBrowser(options.uatType))
97
102
  return null;
98
- if (!Array.isArray(options.activeTools))
103
+ const toolSurface = resolveUatBrowserToolSurface(options);
104
+ if (!toolSurface)
99
105
  return null;
100
- if (hasUatBrowserToolSurface(options.activeTools))
106
+ if (hasUatBrowserToolSurface(toolSurface))
101
107
  return null;
102
- return `Cannot dispatch browser-backed run-uat for ${options.milestoneId}/${options.sliceId}: UAT mode "${options.uatType}" requires browser tools, but the active tool surface has none. Enable browser tools or change the UAT to a runtime-executable Playwright command, then rerun /gsd auto.`;
108
+ return `Cannot dispatch browser-backed run-uat for ${options.milestoneId}/${options.sliceId}: UAT mode "${options.uatType}" requires browser tools, but the run-uat tool surface has none. Enable browser tools or change the UAT to a runtime-executable Playwright command, then rerun /gsd auto.`;
103
109
  }
104
110
  export function isPartialEligibleUatType(uatType) {
105
111
  return !!uatType && UAT_MODE_POLICIES[uatType].partialEligible;