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

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 (530) 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/bg-shell/utilities.js +2 -2
  7. package/dist/resources/extensions/claude-code-cli/models.js +9 -0
  8. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +92 -230
  9. package/dist/resources/extensions/claude-code-cli/turn-assembler.js +224 -0
  10. package/dist/resources/extensions/github-sync/templates.js +3 -3
  11. package/dist/resources/extensions/gsd/artifact-projection.js +14 -0
  12. package/dist/resources/extensions/gsd/auto/loop.js +74 -56
  13. package/dist/resources/extensions/gsd/auto/orchestrator.js +142 -15
  14. package/dist/resources/extensions/gsd/auto/phases.js +34 -4
  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 +12 -9
  21. package/dist/resources/extensions/gsd/auto-prompts.js +81 -8
  22. package/dist/resources/extensions/gsd/auto-recovery.js +48 -49
  23. package/dist/resources/extensions/gsd/auto-runtime-state.js +14 -0
  24. package/dist/resources/extensions/gsd/auto-start.js +20 -36
  25. package/dist/resources/extensions/gsd/auto-timers.js +16 -2
  26. package/dist/resources/extensions/gsd/auto-tool-tracking.js +32 -0
  27. package/dist/resources/extensions/gsd/auto-unit-tool-scope.js +4 -29
  28. package/dist/resources/extensions/gsd/auto-verification.js +7 -7
  29. package/dist/resources/extensions/gsd/auto-worktree-repair.js +10 -2
  30. package/dist/resources/extensions/gsd/auto-worktree.js +34 -289
  31. package/dist/resources/extensions/gsd/auto.js +15 -14
  32. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +28 -37
  33. package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +20 -43
  34. package/dist/resources/extensions/gsd/bootstrap/query-tools.js +2 -2
  35. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +131 -140
  36. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +89 -8
  37. package/dist/resources/extensions/gsd/captures.js +5 -13
  38. package/dist/resources/extensions/gsd/closeout-consistency-gate.js +21 -4
  39. package/dist/resources/extensions/gsd/closeout-recovery.js +3 -2
  40. package/dist/resources/extensions/gsd/codebase-generator.js +8 -4
  41. package/dist/resources/extensions/gsd/commands/catalog.js +6 -62
  42. package/dist/resources/extensions/gsd/commands/handlers/auto.js +3 -0
  43. package/dist/resources/extensions/gsd/commands-handlers.js +20 -0
  44. package/dist/resources/extensions/gsd/commands-inspect.js +4 -8
  45. package/dist/resources/extensions/gsd/commands-maintenance.js +61 -41
  46. package/dist/resources/extensions/gsd/commands-ship.js +2 -2
  47. package/dist/resources/extensions/gsd/commands-verdict.js +12 -2
  48. package/dist/resources/extensions/gsd/db/engine.js +755 -0
  49. package/dist/resources/extensions/gsd/db/queries.js +372 -0
  50. package/dist/resources/extensions/gsd/db/sql-constants.js +11 -0
  51. package/dist/resources/extensions/gsd/db/writers/cascades.js +194 -0
  52. package/dist/resources/extensions/gsd/db/writers/import-restore.js +182 -0
  53. package/dist/resources/extensions/gsd/db/writers/memory.js +149 -0
  54. package/dist/resources/extensions/gsd/db/writers/reconcile.js +458 -0
  55. package/dist/resources/extensions/gsd/db/writers/status.js +70 -0
  56. package/dist/resources/extensions/gsd/db-workspace.js +103 -0
  57. package/dist/resources/extensions/gsd/delegation-policy.js +2 -10
  58. package/dist/resources/extensions/gsd/discussion-handoff.js +218 -0
  59. package/dist/resources/extensions/gsd/docs/preferences-reference.md +9 -0
  60. package/dist/resources/extensions/gsd/doctor-environment.js +8 -10
  61. package/dist/resources/extensions/gsd/doctor-git-checks.js +4 -3
  62. package/dist/resources/extensions/gsd/doctor-runtime-checks.js +9 -2
  63. package/dist/resources/extensions/gsd/doctor.js +16 -9
  64. package/dist/resources/extensions/gsd/error-classifier.js +1 -1
  65. package/dist/resources/extensions/gsd/git-conflict-state.js +16 -1
  66. package/dist/resources/extensions/gsd/git-service.js +1 -0
  67. package/dist/resources/extensions/gsd/gitignore.js +3 -0
  68. package/dist/resources/extensions/gsd/gsd-db.js +183 -2048
  69. package/dist/resources/extensions/gsd/guided-flow.js +68 -471
  70. package/dist/resources/extensions/gsd/guided-unit-completion.js +225 -0
  71. package/dist/resources/extensions/gsd/markdown-renderer.js +2 -1
  72. package/dist/resources/extensions/gsd/mcp-filter.js +2 -1
  73. package/dist/resources/extensions/gsd/mcp-tool-name.js +26 -0
  74. package/dist/resources/extensions/gsd/md-importer.js +4 -3
  75. package/dist/resources/extensions/gsd/migrate/safety.js +19 -11
  76. package/dist/resources/extensions/gsd/migration-auto-check.js +27 -5
  77. package/dist/resources/extensions/gsd/milestone-closeout-proof.js +72 -0
  78. package/dist/resources/extensions/gsd/milestone-closeout.js +12 -4
  79. package/dist/resources/extensions/gsd/milestone-merge-transaction.js +10 -0
  80. package/dist/resources/extensions/gsd/milestone-planning-persistence.js +156 -0
  81. package/dist/resources/extensions/gsd/milestone-readiness.js +77 -0
  82. package/dist/resources/extensions/gsd/milestone-settlement.js +50 -0
  83. package/dist/resources/extensions/gsd/milestone-validation-evidence.js +73 -0
  84. package/dist/resources/extensions/gsd/milestone-validation-verdict.js +57 -0
  85. package/dist/resources/extensions/gsd/model-cost-table.js +1 -0
  86. package/dist/resources/extensions/gsd/model-router.js +3 -0
  87. package/dist/resources/extensions/gsd/parallel-eligibility.js +3 -6
  88. package/dist/resources/extensions/gsd/parallel-merge.js +14 -11
  89. package/dist/resources/extensions/gsd/parallel-monitor-overlay.js +7 -5
  90. package/dist/resources/extensions/gsd/parallel-orchestrator.js +3 -2
  91. package/dist/resources/extensions/gsd/paths.js +10 -24
  92. package/dist/resources/extensions/gsd/preferences-diagnostics.js +67 -0
  93. package/dist/resources/extensions/gsd/preferences.js +161 -29
  94. package/dist/resources/extensions/gsd/prompts/complete-slice.md +1 -0
  95. package/dist/resources/extensions/gsd/prompts/execute-task.md +2 -0
  96. package/dist/resources/extensions/gsd/prompts/guided-discuss-project.md +3 -1
  97. package/dist/resources/extensions/gsd/prompts/plan-milestone.md +2 -0
  98. package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -0
  99. package/dist/resources/extensions/gsd/prompts/refine-slice.md +1 -0
  100. package/dist/resources/extensions/gsd/prompts/system.md +1 -1
  101. package/dist/resources/extensions/gsd/provider-payload-policy.js +83 -0
  102. package/dist/resources/extensions/gsd/pull-request-process.js +13 -0
  103. package/dist/resources/extensions/gsd/quality-gate-closure.js +109 -0
  104. package/dist/resources/extensions/gsd/question-transport.js +86 -0
  105. package/dist/resources/extensions/gsd/recovery-classification.js +12 -1
  106. package/dist/resources/extensions/gsd/roadmap-slices.js +8 -2
  107. package/dist/resources/extensions/gsd/safety/evidence-collector.js +37 -4
  108. package/dist/resources/extensions/gsd/safety/evidence-cross-ref.js +7 -2
  109. package/dist/resources/extensions/gsd/safety/file-change-validator.js +10 -0
  110. package/dist/resources/extensions/gsd/slice-parallel-orchestrator.js +3 -2
  111. package/dist/resources/extensions/gsd/state-transition-matrix.js +38 -0
  112. package/dist/resources/extensions/gsd/state.js +13 -5
  113. package/dist/resources/extensions/gsd/status-guards.js +56 -8
  114. package/dist/resources/extensions/gsd/templates/plan.md +7 -0
  115. package/dist/resources/extensions/gsd/templates/project.md +1 -0
  116. package/dist/resources/extensions/gsd/templates/roadmap.md +1 -1
  117. package/dist/resources/extensions/gsd/templates/uat.md +5 -1
  118. package/dist/resources/extensions/gsd/tool-contract.js +52 -8
  119. package/dist/resources/extensions/gsd/tool-presentation-plan.js +15 -34
  120. package/dist/resources/extensions/gsd/tool-surface-snapshot.js +17 -0
  121. package/dist/resources/extensions/gsd/tools/complete-slice.js +24 -43
  122. package/dist/resources/extensions/gsd/tools/exec-tool.js +5 -5
  123. package/dist/resources/extensions/gsd/tools/plan-milestone.js +15 -143
  124. package/dist/resources/extensions/gsd/tools/reassess-roadmap.js +39 -0
  125. package/dist/resources/extensions/gsd/tools/reopen-milestone.js +11 -29
  126. package/dist/resources/extensions/gsd/tools/reopen-slice.js +14 -33
  127. package/dist/resources/extensions/gsd/tools/skip-slice.js +18 -36
  128. package/dist/resources/extensions/gsd/tools/validate-milestone.js +15 -78
  129. package/dist/resources/extensions/gsd/uat-policy.js +16 -10
  130. package/dist/resources/extensions/gsd/uat-run.js +9 -14
  131. package/dist/resources/extensions/gsd/undo.js +8 -7
  132. package/dist/resources/extensions/gsd/unit-context-composer.js +40 -20
  133. package/dist/resources/extensions/gsd/unit-runtime.js +3 -2
  134. package/dist/resources/extensions/gsd/unit-tool-contracts.js +2 -1
  135. package/dist/resources/extensions/gsd/user-input-boundary.js +23 -0
  136. package/dist/resources/extensions/gsd/validation-block-guard.js +2 -0
  137. package/dist/resources/extensions/gsd/web-app-uat.js +80 -0
  138. package/dist/resources/extensions/gsd/workflow-mcp.js +15 -102
  139. package/dist/resources/extensions/gsd/workflow-reconcile.js +4 -3
  140. package/dist/resources/extensions/gsd/workflow-tool-surface.js +46 -0
  141. package/dist/resources/extensions/gsd/workspace-git-guard.js +2 -0
  142. package/dist/resources/extensions/gsd/worktree-git-recovery.js +287 -0
  143. package/dist/resources/extensions/gsd/worktree-lifecycle.js +9 -1
  144. package/dist/resources/extensions/gsd/worktree-manager.js +45 -28
  145. package/dist/resources/extensions/gsd/worktree-placement.js +59 -0
  146. package/dist/resources/extensions/gsd/worktree-reentry.js +12 -8
  147. package/dist/resources/extensions/gsd/worktree-root.js +17 -6
  148. package/dist/resources/extensions/gsd/worktree-safety.js +8 -5
  149. package/dist/resources/extensions/gsd/worktree-session-state.js +12 -10
  150. package/dist/resources/extensions/gsd/worktree-state-projection.js +33 -4
  151. package/dist/resources/extensions/gsd/worktree-telemetry.js +12 -0
  152. package/dist/resources/extensions/shared/interview-ui.js +2 -2
  153. package/dist/resources/shared/claude-runtime-floor.js +182 -0
  154. package/dist/resources/skills/gsd-browser/SKILL.md +1 -1
  155. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  156. package/dist/update-cmd.js +20 -0
  157. package/dist/web/standalone/.next/BUILD_ID +1 -1
  158. package/dist/web/standalone/.next/app-path-routes-manifest.json +12 -12
  159. package/dist/web/standalone/.next/build-manifest.json +3 -3
  160. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  161. package/dist/web/standalone/.next/react-loadable-manifest.json +8 -8
  162. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  163. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  164. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  165. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  166. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  167. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  168. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  169. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  170. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  171. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  172. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  173. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  174. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  175. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  176. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  177. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  178. package/dist/web/standalone/.next/server/app/api/boot/route.js.nft.json +1 -1
  179. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js.nft.json +1 -1
  180. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js.nft.json +1 -1
  181. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js.nft.json +1 -1
  182. package/dist/web/standalone/.next/server/app/api/captures/route.js.nft.json +1 -1
  183. package/dist/web/standalone/.next/server/app/api/cleanup/route.js.nft.json +1 -1
  184. package/dist/web/standalone/.next/server/app/api/doctor/route.js.nft.json +1 -1
  185. package/dist/web/standalone/.next/server/app/api/export-data/route.js.nft.json +1 -1
  186. package/dist/web/standalone/.next/server/app/api/files/route.js.nft.json +1 -1
  187. package/dist/web/standalone/.next/server/app/api/forensics/route.js.nft.json +1 -1
  188. package/dist/web/standalone/.next/server/app/api/git/route.js.nft.json +1 -1
  189. package/dist/web/standalone/.next/server/app/api/history/route.js.nft.json +1 -1
  190. package/dist/web/standalone/.next/server/app/api/hooks/route.js.nft.json +1 -1
  191. package/dist/web/standalone/.next/server/app/api/inspect/route.js.nft.json +1 -1
  192. package/dist/web/standalone/.next/server/app/api/knowledge/route.js.nft.json +1 -1
  193. package/dist/web/standalone/.next/server/app/api/live-state/route.js.nft.json +1 -1
  194. package/dist/web/standalone/.next/server/app/api/mcp-connections/route.js.nft.json +1 -1
  195. package/dist/web/standalone/.next/server/app/api/notifications/route.js.nft.json +1 -1
  196. package/dist/web/standalone/.next/server/app/api/onboarding/route.js.nft.json +1 -1
  197. package/dist/web/standalone/.next/server/app/api/projects/route.js.nft.json +1 -1
  198. package/dist/web/standalone/.next/server/app/api/recovery/route.js.nft.json +1 -1
  199. package/dist/web/standalone/.next/server/app/api/session/browser/route.js.nft.json +1 -1
  200. package/dist/web/standalone/.next/server/app/api/session/command/route.js.nft.json +1 -1
  201. package/dist/web/standalone/.next/server/app/api/session/events/route.js.nft.json +1 -1
  202. package/dist/web/standalone/.next/server/app/api/session/manage/route.js.nft.json +1 -1
  203. package/dist/web/standalone/.next/server/app/api/settings-data/route.js.nft.json +1 -1
  204. package/dist/web/standalone/.next/server/app/api/shutdown/route.js.nft.json +1 -1
  205. package/dist/web/standalone/.next/server/app/api/skill-health/route.js.nft.json +1 -1
  206. package/dist/web/standalone/.next/server/app/api/steer/route.js.nft.json +1 -1
  207. package/dist/web/standalone/.next/server/app/api/switch-root/route.js.nft.json +1 -1
  208. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js.nft.json +1 -1
  209. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js.nft.json +1 -1
  210. package/dist/web/standalone/.next/server/app/api/undo/route.js.nft.json +1 -1
  211. package/dist/web/standalone/.next/server/app/api/visualizer/route.js.nft.json +1 -1
  212. package/dist/web/standalone/.next/server/app/index.html +1 -1
  213. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  214. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  215. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  216. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  217. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  218. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  219. package/dist/web/standalone/.next/server/app-paths-manifest.json +12 -12
  220. package/dist/web/standalone/.next/server/chunks/{5047.js → 5942.js} +2 -2
  221. package/dist/web/standalone/.next/server/chunks/8357.js +1 -1
  222. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  223. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  224. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  225. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  226. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  227. package/dist/web/standalone/.next/static/chunks/2659.b7b129ee6a769448.js +1 -0
  228. package/dist/web/standalone/.next/static/chunks/2772.bfa657f49f955239.js +1 -0
  229. package/dist/web/standalone/.next/static/chunks/{3616.4113d484a994e411.js → 3616.3c60753b8ffcbd2e.js} +1 -1
  230. package/dist/web/standalone/.next/static/chunks/4283.e4873b058df143a1.js +2 -0
  231. package/dist/web/standalone/.next/static/chunks/5826.a46ecdd1cfe8dabc.js +1 -0
  232. package/dist/web/standalone/.next/static/chunks/796.cf859a427a2cb2ac.js +10 -0
  233. package/dist/web/standalone/.next/static/chunks/8785.2e5a118797fb2dd2.js +1 -0
  234. package/dist/web/standalone/.next/static/chunks/{webpack-dda80a1ef5587410.js → webpack-fbea77b5f9953368.js} +1 -1
  235. package/dist/web/standalone/node_modules/node-pty/build/Makefile +1 -1
  236. package/dist/web-mode.d.ts +2 -0
  237. package/dist/web-mode.js +20 -8
  238. package/dist/worktree-status-banner.js +7 -3
  239. package/package.json +2 -1
  240. package/packages/cloud-mcp-gateway/package.json +2 -2
  241. package/packages/contracts/package.json +1 -1
  242. package/packages/daemon/package.json +4 -4
  243. package/packages/gsd-agent-core/dist/session/agent-session-extensions.d.ts +2 -0
  244. package/packages/gsd-agent-core/dist/session/agent-session-extensions.d.ts.map +1 -1
  245. package/packages/gsd-agent-core/dist/session/agent-session-extensions.js +14 -0
  246. package/packages/gsd-agent-core/dist/session/agent-session-extensions.js.map +1 -1
  247. package/packages/gsd-agent-core/package.json +5 -5
  248. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  249. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js +3 -0
  250. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js.map +1 -1
  251. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  252. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js +106 -40
  253. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  254. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-extension-widgets.d.ts.map +1 -1
  255. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-extension-widgets.js +6 -0
  256. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-extension-widgets.js.map +1 -1
  257. package/packages/gsd-agent-modes/package.json +7 -7
  258. package/packages/mcp-server/dist/server.d.ts +10 -0
  259. package/packages/mcp-server/dist/server.d.ts.map +1 -1
  260. package/packages/mcp-server/dist/server.js +8 -0
  261. package/packages/mcp-server/dist/server.js.map +1 -1
  262. package/packages/mcp-server/dist/workflow-tools.d.ts +41 -0
  263. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  264. package/packages/mcp-server/dist/workflow-tools.js +32 -22
  265. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  266. package/packages/mcp-server/package.json +3 -3
  267. package/packages/native/package.json +1 -1
  268. package/packages/pi-agent-core/package.json +1 -1
  269. package/packages/pi-ai/dist/image-models.generated.d.ts +2 -2
  270. package/packages/pi-ai/dist/image-models.generated.js +6 -6
  271. package/packages/pi-ai/dist/image-models.generated.js.map +1 -1
  272. package/packages/pi-ai/dist/models.generated.d.ts +295 -98
  273. package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
  274. package/packages/pi-ai/dist/models.generated.js +309 -154
  275. package/packages/pi-ai/dist/models.generated.js.map +1 -1
  276. package/packages/pi-ai/package.json +1 -1
  277. package/packages/pi-coding-agent/dist/core/capability-patches.d.ts.map +1 -1
  278. package/packages/pi-coding-agent/dist/core/capability-patches.js +3 -1
  279. package/packages/pi-coding-agent/dist/core/capability-patches.js.map +1 -1
  280. package/packages/pi-coding-agent/package.json +7 -7
  281. package/packages/pi-tui/dist/components/input.js +1 -1
  282. package/packages/pi-tui/dist/components/input.js.map +1 -1
  283. package/packages/pi-tui/dist/keys.d.ts.map +1 -1
  284. package/packages/pi-tui/dist/keys.js +39 -30
  285. package/packages/pi-tui/dist/keys.js.map +1 -1
  286. package/packages/pi-tui/dist/stdin-buffer.d.ts.map +1 -1
  287. package/packages/pi-tui/dist/stdin-buffer.js +22 -0
  288. package/packages/pi-tui/dist/stdin-buffer.js.map +1 -1
  289. package/packages/pi-tui/package.json +2 -2
  290. package/packages/rpc-client/package.json +2 -2
  291. package/pkg/package.json +1 -1
  292. package/src/resources/extensions/ask-user-questions.ts +87 -24
  293. package/src/resources/extensions/bg-shell/utilities.ts +2 -2
  294. package/src/resources/extensions/claude-code-cli/models.ts +9 -0
  295. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +114 -281
  296. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +268 -0
  297. package/src/resources/extensions/claude-code-cli/turn-assembler.ts +287 -0
  298. package/src/resources/extensions/github-sync/templates.ts +3 -3
  299. package/src/resources/extensions/github-sync/tests/templates.test.ts +2 -2
  300. package/src/resources/extensions/gsd/artifact-projection.ts +31 -0
  301. package/src/resources/extensions/gsd/auto/contracts.ts +32 -2
  302. package/src/resources/extensions/gsd/auto/loop-deps.ts +3 -1
  303. package/src/resources/extensions/gsd/auto/loop.ts +83 -61
  304. package/src/resources/extensions/gsd/auto/orchestrator.ts +164 -17
  305. package/src/resources/extensions/gsd/auto/phases.ts +45 -4
  306. package/src/resources/extensions/gsd/auto/run-unit.ts +2 -1
  307. package/src/resources/extensions/gsd/auto/session.ts +4 -0
  308. package/src/resources/extensions/gsd/auto-dashboard.ts +18 -4
  309. package/src/resources/extensions/gsd/auto-dispatch.ts +20 -7
  310. package/src/resources/extensions/gsd/auto-model-selection.ts +8 -0
  311. package/src/resources/extensions/gsd/auto-post-unit.ts +16 -8
  312. package/src/resources/extensions/gsd/auto-prompts.ts +107 -9
  313. package/src/resources/extensions/gsd/auto-recovery.ts +50 -50
  314. package/src/resources/extensions/gsd/auto-runtime-state.ts +26 -0
  315. package/src/resources/extensions/gsd/auto-start.ts +25 -34
  316. package/src/resources/extensions/gsd/auto-timers.ts +16 -2
  317. package/src/resources/extensions/gsd/auto-tool-tracking.ts +35 -0
  318. package/src/resources/extensions/gsd/auto-unit-tool-scope.ts +9 -30
  319. package/src/resources/extensions/gsd/auto-verification.ts +7 -8
  320. package/src/resources/extensions/gsd/auto-worktree-repair.ts +13 -2
  321. package/src/resources/extensions/gsd/auto-worktree.ts +53 -306
  322. package/src/resources/extensions/gsd/auto.ts +27 -17
  323. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +29 -37
  324. package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +20 -43
  325. package/src/resources/extensions/gsd/bootstrap/query-tools.ts +2 -2
  326. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +147 -153
  327. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +132 -6
  328. package/src/resources/extensions/gsd/captures.ts +5 -14
  329. package/src/resources/extensions/gsd/closeout-consistency-gate.ts +27 -5
  330. package/src/resources/extensions/gsd/closeout-recovery.ts +2 -1
  331. package/src/resources/extensions/gsd/codebase-generator.ts +9 -5
  332. package/src/resources/extensions/gsd/commands/catalog.ts +6 -68
  333. package/src/resources/extensions/gsd/commands/handlers/auto.ts +3 -0
  334. package/src/resources/extensions/gsd/commands-handlers.ts +18 -0
  335. package/src/resources/extensions/gsd/commands-inspect.ts +7 -8
  336. package/src/resources/extensions/gsd/commands-maintenance.ts +74 -40
  337. package/src/resources/extensions/gsd/commands-ship.ts +2 -2
  338. package/src/resources/extensions/gsd/commands-verdict.ts +19 -2
  339. package/src/resources/extensions/gsd/db/engine.ts +809 -0
  340. package/src/resources/extensions/gsd/db/queries.ts +453 -0
  341. package/src/resources/extensions/gsd/db/sql-constants.ts +12 -0
  342. package/src/resources/extensions/gsd/db/writers/cascades.ts +237 -0
  343. package/src/resources/extensions/gsd/db/writers/import-restore.ts +310 -0
  344. package/src/resources/extensions/gsd/db/writers/memory.ts +220 -0
  345. package/src/resources/extensions/gsd/db/writers/reconcile.ts +500 -0
  346. package/src/resources/extensions/gsd/db/writers/status.ts +88 -0
  347. package/src/resources/extensions/gsd/db-workspace.ts +170 -0
  348. package/src/resources/extensions/gsd/delegation-policy.ts +3 -11
  349. package/src/resources/extensions/gsd/discussion-handoff.ts +276 -0
  350. package/src/resources/extensions/gsd/docs/preferences-reference.md +9 -0
  351. package/src/resources/extensions/gsd/doctor-environment.ts +8 -11
  352. package/src/resources/extensions/gsd/doctor-git-checks.ts +3 -3
  353. package/src/resources/extensions/gsd/doctor-runtime-checks.ts +10 -3
  354. package/src/resources/extensions/gsd/doctor.ts +15 -5
  355. package/src/resources/extensions/gsd/error-classifier.ts +1 -1
  356. package/src/resources/extensions/gsd/git-conflict-state.ts +17 -1
  357. package/src/resources/extensions/gsd/git-service.ts +1 -0
  358. package/src/resources/extensions/gsd/gitignore.ts +3 -0
  359. package/src/resources/extensions/gsd/gsd-db.ts +185 -2373
  360. package/src/resources/extensions/gsd/guided-flow.ts +81 -561
  361. package/src/resources/extensions/gsd/guided-unit-completion.ts +275 -0
  362. package/src/resources/extensions/gsd/markdown-renderer.ts +2 -1
  363. package/src/resources/extensions/gsd/mcp-filter.ts +2 -1
  364. package/src/resources/extensions/gsd/mcp-tool-name.ts +35 -0
  365. package/src/resources/extensions/gsd/md-importer.ts +3 -3
  366. package/src/resources/extensions/gsd/migrate/safety.ts +17 -9
  367. package/src/resources/extensions/gsd/migration-auto-check.ts +30 -5
  368. package/src/resources/extensions/gsd/milestone-closeout-proof.ts +131 -0
  369. package/src/resources/extensions/gsd/milestone-closeout.ts +12 -4
  370. package/src/resources/extensions/gsd/milestone-merge-transaction.ts +47 -0
  371. package/src/resources/extensions/gsd/milestone-planning-persistence.ts +224 -0
  372. package/src/resources/extensions/gsd/milestone-readiness.ts +125 -0
  373. package/src/resources/extensions/gsd/milestone-settlement.ts +81 -0
  374. package/src/resources/extensions/gsd/milestone-validation-evidence.ts +95 -0
  375. package/src/resources/extensions/gsd/milestone-validation-verdict.ts +80 -0
  376. package/src/resources/extensions/gsd/model-cost-table.ts +1 -0
  377. package/src/resources/extensions/gsd/model-router.ts +3 -0
  378. package/src/resources/extensions/gsd/parallel-eligibility.ts +4 -5
  379. package/src/resources/extensions/gsd/parallel-merge.ts +12 -9
  380. package/src/resources/extensions/gsd/parallel-monitor-overlay.ts +6 -5
  381. package/src/resources/extensions/gsd/parallel-orchestrator.ts +6 -2
  382. package/src/resources/extensions/gsd/paths.ts +9 -22
  383. package/src/resources/extensions/gsd/preferences-diagnostics.ts +98 -0
  384. package/src/resources/extensions/gsd/preferences-types.ts +16 -0
  385. package/src/resources/extensions/gsd/preferences.ts +191 -28
  386. package/src/resources/extensions/gsd/prompts/complete-slice.md +1 -0
  387. package/src/resources/extensions/gsd/prompts/execute-task.md +2 -0
  388. package/src/resources/extensions/gsd/prompts/guided-discuss-project.md +3 -1
  389. package/src/resources/extensions/gsd/prompts/plan-milestone.md +2 -0
  390. package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -0
  391. package/src/resources/extensions/gsd/prompts/refine-slice.md +1 -0
  392. package/src/resources/extensions/gsd/prompts/system.md +1 -1
  393. package/src/resources/extensions/gsd/provider-payload-policy.ts +140 -0
  394. package/src/resources/extensions/gsd/pull-request-process.ts +41 -0
  395. package/src/resources/extensions/gsd/quality-gate-closure.ts +140 -0
  396. package/src/resources/extensions/gsd/question-transport.ts +138 -0
  397. package/src/resources/extensions/gsd/recovery-classification.ts +14 -1
  398. package/src/resources/extensions/gsd/roadmap-slices.ts +8 -2
  399. package/src/resources/extensions/gsd/safety/evidence-collector.ts +36 -4
  400. package/src/resources/extensions/gsd/safety/evidence-cross-ref.ts +7 -2
  401. package/src/resources/extensions/gsd/safety/file-change-validator.ts +14 -0
  402. package/src/resources/extensions/gsd/slice-parallel-orchestrator.ts +6 -2
  403. package/src/resources/extensions/gsd/state-transition-matrix.ts +42 -0
  404. package/src/resources/extensions/gsd/state.ts +15 -5
  405. package/src/resources/extensions/gsd/status-guards.ts +59 -8
  406. package/src/resources/extensions/gsd/templates/plan.md +7 -0
  407. package/src/resources/extensions/gsd/templates/project.md +1 -0
  408. package/src/resources/extensions/gsd/templates/roadmap.md +1 -1
  409. package/src/resources/extensions/gsd/templates/uat.md +5 -1
  410. package/src/resources/extensions/gsd/tests/ask-user-questions-render.test.ts +92 -0
  411. package/src/resources/extensions/gsd/tests/auto-dashboard.test.ts +29 -1
  412. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +444 -5
  413. package/src/resources/extensions/gsd/tests/auto-milestone-target.test.ts +23 -0
  414. package/src/resources/extensions/gsd/tests/auto-model-selection-tool-poisoning.test.ts +18 -0
  415. package/src/resources/extensions/gsd/tests/auto-orchestrator.test.ts +133 -4
  416. package/src/resources/extensions/gsd/tests/auto-paused-ui-cleanup.test.ts +3 -1
  417. package/src/resources/extensions/gsd/tests/auto-post-unit-evidence-crossref-4909.test.ts +46 -0
  418. package/src/resources/extensions/gsd/tests/auto-runtime-state.test.ts +34 -0
  419. package/src/resources/extensions/gsd/tests/auto-worktree-registry.test.ts +2 -2
  420. package/src/resources/extensions/gsd/tests/auto-worktree-repair.test.ts +4 -2
  421. package/src/resources/extensions/gsd/tests/canonical-milestone-root.test.ts +20 -0
  422. package/src/resources/extensions/gsd/tests/clear-stale-autostart.test.ts +22 -0
  423. package/src/resources/extensions/gsd/tests/codebase-generator.test.ts +22 -0
  424. package/src/resources/extensions/gsd/tests/commands-dispatcher-workspace-git.test.ts +11 -0
  425. package/src/resources/extensions/gsd/tests/commands-verdict.test.ts +38 -1
  426. package/src/resources/extensions/gsd/tests/dispatch-complete-milestone-guard.test.ts +34 -3
  427. package/src/resources/extensions/gsd/tests/dispatch-run-uat-browser-tools.test.ts +88 -0
  428. package/src/resources/extensions/gsd/tests/doctor-scope-db-unavailable.test.ts +18 -0
  429. package/src/resources/extensions/gsd/tests/evidence-xref-gsd-exec.test.ts +157 -0
  430. package/src/resources/extensions/gsd/tests/execute-task-rendering.test.ts +1 -0
  431. package/src/resources/extensions/gsd/tests/file-change-validator.test.ts +33 -1
  432. package/src/resources/extensions/gsd/tests/fixtures/pr-body/swarm-lane-no-blockers.md +1 -5
  433. package/src/resources/extensions/gsd/tests/fixtures/pr-body/swarm-lane-with-blockers.md +1 -5
  434. package/src/resources/extensions/gsd/tests/gate-state-canonicalization.test.ts +48 -1
  435. package/src/resources/extensions/gsd/tests/integration/auto-worktree-milestone-merge.test.ts +5 -4
  436. package/src/resources/extensions/gsd/tests/integration/auto-worktree.test.ts +1 -1
  437. package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +3 -2
  438. package/src/resources/extensions/gsd/tests/mcp-tool-name.test.ts +34 -0
  439. package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +143 -1
  440. package/src/resources/extensions/gsd/tests/milestone-closeout-proof.test.ts +99 -0
  441. package/src/resources/extensions/gsd/tests/milestone-closeout.test.ts +25 -0
  442. package/src/resources/extensions/gsd/tests/milestone-merge-transaction.test.ts +46 -0
  443. package/src/resources/extensions/gsd/tests/milestone-readiness.test.ts +65 -0
  444. package/src/resources/extensions/gsd/tests/milestone-validation-evidence.test.ts +41 -0
  445. package/src/resources/extensions/gsd/tests/milestone-validation-verdict.test.ts +55 -0
  446. package/src/resources/extensions/gsd/tests/plan-milestone.test.ts +45 -0
  447. package/src/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +2 -0
  448. package/src/resources/extensions/gsd/tests/planning-crossval.test.ts +45 -0
  449. package/src/resources/extensions/gsd/tests/preferences-diagnostics.test.ts +67 -0
  450. package/src/resources/extensions/gsd/tests/preferences.test.ts +183 -0
  451. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +46 -0
  452. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +9 -0
  453. package/src/resources/extensions/gsd/tests/provider-payload-policy.test.ts +165 -0
  454. package/src/resources/extensions/gsd/tests/pull-request-process.test.ts +47 -0
  455. package/src/resources/extensions/gsd/tests/recovery-classification-illegal-transition.test.ts +30 -0
  456. package/src/resources/extensions/gsd/tests/register-hooks-depth-verification.test.ts +185 -1
  457. package/src/resources/extensions/gsd/tests/roadmap-parse-regression.test.ts +40 -0
  458. package/src/resources/extensions/gsd/tests/runtime-invariant-modules.test.ts +25 -1
  459. package/src/resources/extensions/gsd/tests/safety-harness-false-positives.test.ts +38 -0
  460. package/src/resources/extensions/gsd/tests/session-start-footer.test.ts +80 -0
  461. package/src/resources/extensions/gsd/tests/session-switch-clears-pending-autostart.test.ts +108 -0
  462. package/src/resources/extensions/gsd/tests/single-writer-invariant.test.ts +144 -7
  463. package/src/resources/extensions/gsd/tests/stale-queued-milestone.test.ts +27 -0
  464. package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +2 -1
  465. package/src/resources/extensions/gsd/tests/state-transition-matrix.test.ts +36 -0
  466. package/src/resources/extensions/gsd/tests/status-guards.test.ts +38 -0
  467. package/src/resources/extensions/gsd/tests/tool-availability-audit.test.ts +35 -0
  468. package/src/resources/extensions/gsd/tests/tool-naming.test.ts +35 -42
  469. package/src/resources/extensions/gsd/tests/uat-policy.test.ts +23 -0
  470. package/src/resources/extensions/gsd/tests/unit-context-composer.test.ts +47 -0
  471. package/src/resources/extensions/gsd/tests/user-input-boundary.test.ts +86 -1
  472. package/src/resources/extensions/gsd/tests/validate-milestone-stuck-guard.test.ts +39 -0
  473. package/src/resources/extensions/gsd/tests/web-app-uat.test.ts +150 -0
  474. package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +126 -9
  475. package/src/resources/extensions/gsd/tests/workspace-git-preflight.test.ts +15 -0
  476. package/src/resources/extensions/gsd/tests/worktree-lifecycle.test.ts +41 -4
  477. package/src/resources/extensions/gsd/tests/worktree-manager.test.ts +43 -1
  478. package/src/resources/extensions/gsd/tests/worktree-placement.test.ts +113 -0
  479. package/src/resources/extensions/gsd/tests/worktree-projection-writers.test.ts +1 -1
  480. package/src/resources/extensions/gsd/tests/worktree-reentry.test.ts +1 -1
  481. package/src/resources/extensions/gsd/tests/worktree-safety.test.ts +3 -1
  482. package/src/resources/extensions/gsd/tests/worktree-symlink-removal.test.ts +12 -6
  483. package/src/resources/extensions/gsd/tests/worktree-teardown-safety.test.ts +2 -2
  484. package/src/resources/extensions/gsd/tests/worktree-telemetry.test.ts +22 -0
  485. package/src/resources/extensions/gsd/tests/write-gate.test.ts +121 -0
  486. package/src/resources/extensions/gsd/tool-contract.ts +86 -8
  487. package/src/resources/extensions/gsd/tool-presentation-plan.ts +16 -33
  488. package/src/resources/extensions/gsd/tool-surface-snapshot.ts +47 -0
  489. package/src/resources/extensions/gsd/tools/complete-slice.ts +23 -58
  490. package/src/resources/extensions/gsd/tools/exec-tool.ts +5 -5
  491. package/src/resources/extensions/gsd/tools/plan-milestone.ts +19 -160
  492. package/src/resources/extensions/gsd/tools/reassess-roadmap.ts +43 -0
  493. package/src/resources/extensions/gsd/tools/reopen-milestone.ts +11 -38
  494. package/src/resources/extensions/gsd/tools/reopen-slice.ts +14 -42
  495. package/src/resources/extensions/gsd/tools/skip-slice.ts +18 -44
  496. package/src/resources/extensions/gsd/tools/validate-milestone.ts +25 -84
  497. package/src/resources/extensions/gsd/uat-policy.ts +19 -10
  498. package/src/resources/extensions/gsd/uat-run.ts +10 -14
  499. package/src/resources/extensions/gsd/undo.ts +9 -8
  500. package/src/resources/extensions/gsd/unit-context-composer.ts +85 -20
  501. package/src/resources/extensions/gsd/unit-runtime.ts +3 -2
  502. package/src/resources/extensions/gsd/unit-tool-contracts.ts +2 -1
  503. package/src/resources/extensions/gsd/user-input-boundary.ts +18 -0
  504. package/src/resources/extensions/gsd/validation-block-guard.ts +2 -0
  505. package/src/resources/extensions/gsd/web-app-uat.ts +101 -0
  506. package/src/resources/extensions/gsd/workflow-mcp.ts +22 -110
  507. package/src/resources/extensions/gsd/workflow-reconcile.ts +3 -3
  508. package/src/resources/extensions/gsd/workflow-tool-surface.ts +73 -0
  509. package/src/resources/extensions/gsd/workspace-git-guard.ts +1 -0
  510. package/src/resources/extensions/gsd/worktree-git-recovery.ts +308 -0
  511. package/src/resources/extensions/gsd/worktree-lifecycle.ts +17 -17
  512. package/src/resources/extensions/gsd/worktree-manager.ts +47 -28
  513. package/src/resources/extensions/gsd/worktree-placement.ts +63 -0
  514. package/src/resources/extensions/gsd/worktree-reentry.ts +10 -7
  515. package/src/resources/extensions/gsd/worktree-root.ts +17 -6
  516. package/src/resources/extensions/gsd/worktree-safety.ts +8 -5
  517. package/src/resources/extensions/gsd/worktree-session-state.ts +12 -10
  518. package/src/resources/extensions/gsd/worktree-state-projection.ts +55 -7
  519. package/src/resources/extensions/gsd/worktree-telemetry.ts +16 -0
  520. package/src/resources/extensions/shared/interview-ui.ts +15 -2
  521. package/src/resources/shared/claude-runtime-floor.ts +248 -0
  522. package/src/resources/skills/gsd-browser/SKILL.md +1 -1
  523. package/dist/web/standalone/.next/static/chunks/2659.feb6499ca863ebfc.js +0 -1
  524. package/dist/web/standalone/.next/static/chunks/2772.151789db0edea835.js +0 -1
  525. package/dist/web/standalone/.next/static/chunks/4283.10a065467b5340d8.js +0 -2
  526. package/dist/web/standalone/.next/static/chunks/5826.960dc4634cc9b0d3.js +0 -1
  527. package/dist/web/standalone/.next/static/chunks/796.46f811c0fac23aab.js +0 -10
  528. package/dist/web/standalone/.next/static/chunks/8785.d32f7a61f55c1600.js +0 -1
  529. /package/dist/web/standalone/.next/static/{Qbr81pQ-pbQXP4bq2VXLv → C24pqUd-aru-l0Dp0gLZP}/_buildManifest.js +0 -0
  530. /package/dist/web/standalone/.next/static/{Qbr81pQ-pbQXP4bq2VXLv → C24pqUd-aru-l0Dp0gLZP}/_ssgManifest.js +0 -0
@@ -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,
@@ -10,16 +10,11 @@
10
10
  */
11
11
 
12
12
  import {
13
- getMilestone,
14
13
  getMilestoneSlices,
15
14
  getSliceTasks,
16
- reopenMilestoneStatus,
17
- updateSliceStatus,
18
- updateTaskStatus,
19
- transaction,
15
+ reopenMilestoneCascade,
20
16
  } from "../gsd-db.js";
21
17
  import { invalidateStateCache } from "../state.js";
22
- import { isClosedStatus } from "../status-guards.js";
23
18
  import { renderAllProjections } from "../workflow-projections.js";
24
19
  import { writeManifest } from "../workflow-manifest.js";
25
20
  import { appendEvent } from "../workflow-events.js";
@@ -53,40 +48,18 @@ export async function handleReopenMilestone(
53
48
  return { error: "milestoneId is required and must be a non-empty string" };
54
49
  }
55
50
 
56
- // ── Guards + DB writes inside a single transaction (prevents TOCTOU) ───
57
- let guardError: string | null = null;
58
- let slicesResetCount = 0;
59
- let tasksResetCount = 0;
60
-
61
- transaction(() => {
62
- const milestone = getMilestone(params.milestoneId);
63
- if (!milestone) {
64
- guardError = `milestone not found: ${params.milestoneId}`;
65
- return;
66
- }
67
- if (!isClosedStatus(milestone.status)) {
68
- guardError = `milestone ${params.milestoneId} is not closed (status: ${milestone.status}) — nothing to reopen`;
69
- return;
70
- }
71
-
72
- reopenMilestoneStatus(params.milestoneId);
73
-
74
- const slices = getMilestoneSlices(params.milestoneId);
75
- slicesResetCount = slices.length;
76
-
77
- for (const slice of slices) {
78
- updateSliceStatus(params.milestoneId, slice.id, "in_progress");
79
- const tasks = getSliceTasks(params.milestoneId, slice.id);
80
- tasksResetCount += tasks.length;
81
- for (const task of tasks) {
82
- updateTaskStatus(params.milestoneId, slice.id, task.id, "pending");
83
- }
51
+ // ── Atomic reopen cascade (guards + writes in one transaction) ───────────
52
+ const outcome = reopenMilestoneCascade(params.milestoneId);
53
+ if (!outcome.ok) {
54
+ switch (outcome.reason) {
55
+ case "milestone-not-found":
56
+ return { error: `milestone not found: ${params.milestoneId}` };
57
+ case "milestone-not-closed":
58
+ return { error: `milestone ${params.milestoneId} is not closed (status: ${outcome.status}) — nothing to reopen` };
84
59
  }
85
- });
86
-
87
- if (guardError) {
88
- return { error: guardError };
89
60
  }
61
+ const slicesResetCount = outcome.slicesReset;
62
+ const tasksResetCount = outcome.tasksReset;
90
63
 
91
64
  // ── Invalidate caches ────────────────────────────────────────────────────
92
65
  invalidateStateCache();
@@ -12,15 +12,10 @@
12
12
  // Copyright (c) 2026 Jeremy McSpadden <jeremy@fluxlabs.net>
13
13
 
14
14
  import {
15
- getMilestone,
16
- getSlice,
17
15
  getSliceTasks,
18
- updateSliceStatus,
19
- updateTaskStatus,
20
- transaction,
16
+ reopenSliceCascade,
21
17
  } from "../gsd-db.js";
22
18
  import { invalidateStateCache } from "../state.js";
23
- import { isClosedStatus } from "../status-guards.js";
24
19
  import { renderAllProjections } from "../workflow-projections.js";
25
20
  import { writeManifest } from "../workflow-manifest.js";
26
21
  import { appendEvent } from "../workflow-events.js";
@@ -57,44 +52,21 @@ export async function handleReopenSlice(
57
52
  return { error: "milestoneId is required and must be a non-empty string" };
58
53
  }
59
54
 
60
- // ── Guards + DB writes inside a single transaction (prevents TOCTOU) ───
61
- let guardError: string | null = null;
62
- let tasksResetCount = 0;
63
-
64
- transaction(() => {
65
- const milestone = getMilestone(params.milestoneId);
66
- if (!milestone) {
67
- guardError = `milestone not found: ${params.milestoneId}`;
68
- return;
69
- }
70
- if (isClosedStatus(milestone.status)) {
71
- guardError = `cannot reopen slice in a closed milestone: ${params.milestoneId} (status: ${milestone.status})`;
72
- return;
73
- }
74
-
75
- const slice = getSlice(params.milestoneId, params.sliceId);
76
- if (!slice) {
77
- guardError = `slice not found: ${params.milestoneId}/${params.sliceId}`;
78
- return;
79
- }
80
- if (!isClosedStatus(slice.status)) {
81
- guardError = `slice ${params.sliceId} is not complete (status: ${slice.status}) — nothing to reopen`;
82
- return;
55
+ // ── Atomic reopen cascade (guards + writes in one transaction) ───────────
56
+ const outcome = reopenSliceCascade(params.milestoneId, params.sliceId);
57
+ if (!outcome.ok) {
58
+ switch (outcome.reason) {
59
+ case "milestone-not-found":
60
+ return { error: `milestone not found: ${params.milestoneId}` };
61
+ case "milestone-closed":
62
+ return { error: `cannot reopen slice in a closed milestone: ${params.milestoneId} (status: ${outcome.status})` };
63
+ case "slice-not-found":
64
+ return { error: `slice not found: ${params.milestoneId}/${params.sliceId}` };
65
+ case "slice-not-complete":
66
+ return { error: `slice ${params.sliceId} is not complete (status: ${outcome.status}) — nothing to reopen` };
83
67
  }
84
-
85
- // Fetch tasks inside txn so the list is consistent with the slice status check
86
- const tasks = getSliceTasks(params.milestoneId, params.sliceId);
87
- tasksResetCount = tasks.length;
88
-
89
- updateSliceStatus(params.milestoneId, params.sliceId, "in_progress");
90
- for (const task of tasks) {
91
- updateTaskStatus(params.milestoneId, params.sliceId, task.id, "pending");
92
- }
93
- });
94
-
95
- if (guardError) {
96
- return { error: guardError };
97
68
  }
69
+ const tasksResetCount = outcome.tasksReset;
98
70
 
99
71
  // ── Invalidate caches ────────────────────────────────────────────────────
100
72
  invalidateStateCache();
@@ -11,14 +11,9 @@
11
11
  */
12
12
 
13
13
  import {
14
- getSlice,
15
- getSliceTasks,
16
14
  isDbAvailable,
17
- transaction,
18
- updateSliceStatus,
19
- updateTaskStatus,
15
+ skipSliceCascade,
20
16
  } from "../gsd-db.js";
21
- import { isClosedStatus } from "../status-guards.js";
22
17
 
23
18
  /**
24
19
  * Input parameters for {@link handleSkipSlice}.
@@ -90,44 +85,23 @@ export function handleSkipSlice(params: SkipSliceParams): SkipSliceResult {
90
85
  throw new Error("handleSkipSlice: GSD database is not available");
91
86
  }
92
87
 
93
- // ── Guards + DB writes inside a single transaction (prevents TOCTOU) ────
94
- let guardError: string | null = null;
95
- let guardCode: SkipSliceErrorCode | null = null;
96
- let wasAlreadySkipped = false;
97
- let tasksSkipped = 0;
98
-
99
- transaction(() => {
100
- const slice = getSlice(params.milestoneId, params.sliceId);
101
- if (!slice) {
102
- guardError = `Slice ${params.sliceId} not found in milestone ${params.milestoneId}`;
103
- guardCode = "slice_not_found";
104
- return;
105
- }
106
- if (slice.status === "complete" || slice.status === "done") {
107
- guardError = `Slice ${params.sliceId} is already complete — cannot skip.`;
108
- guardCode = "already_complete";
109
- return;
110
- }
111
-
112
- wasAlreadySkipped = slice.status === "skipped";
113
- if (!wasAlreadySkipped) {
114
- updateSliceStatus(params.milestoneId, params.sliceId, "skipped");
88
+ // ── Atomic skip cascade (guards + writes in one transaction) ────────────
89
+ const outcome = skipSliceCascade(params.milestoneId, params.sliceId);
90
+ if (!outcome.ok) {
91
+ switch (outcome.reason) {
92
+ case "slice-not-found":
93
+ return {
94
+ ...base,
95
+ error: `Slice ${params.sliceId} not found in milestone ${params.milestoneId}`,
96
+ errorCode: "slice_not_found" as SkipSliceErrorCode,
97
+ };
98
+ case "slice-already-complete":
99
+ return {
100
+ ...base,
101
+ error: `Slice ${params.sliceId} is already complete cannot skip.`,
102
+ errorCode: "already_complete" as SkipSliceErrorCode,
103
+ };
115
104
  }
116
-
117
- // Cascade: mark every non-closed task as skipped so milestone completion
118
- // doesn't trip the deep-task guard (#4375). Closed tasks (complete/done/
119
- // skipped) are left untouched — we never downgrade.
120
- const tasks = getSliceTasks(params.milestoneId, params.sliceId);
121
- for (const task of tasks) {
122
- if (!isClosedStatus(task.status)) {
123
- updateTaskStatus(params.milestoneId, params.sliceId, task.id, "skipped");
124
- tasksSkipped++;
125
- }
126
- }
127
- });
128
-
129
- if (guardError) {
130
- return { ...base, error: guardError, errorCode: guardCode ?? undefined };
131
105
  }
132
- return { ...base, tasksSkipped, wasAlreadySkipped };
106
+ return { ...base, tasksSkipped: outcome.tasksSkipped, wasAlreadySkipped: outcome.wasAlreadySkipped };
133
107
  }
@@ -19,11 +19,11 @@ import {
19
19
  insertAssessment,
20
20
  getMilestoneSlices,
21
21
  getMilestone,
22
- getArtifact,
23
22
  } from "../gsd-db.js";
24
- import { gsdProjectionRoot, clearPathCache, resolveSliceFile } from "../paths.js";
23
+ import { gsdProjectionRoot, clearPathCache } from "../paths.js";
25
24
  import { resolveCanonicalMilestoneRoot } from "../worktree-manager.js";
26
- import { saveFile, clearParseCache, loadFile } from "../files.js";
25
+ import { resolveWorktreeProjectRoot } from "../worktree-root.js";
26
+ import { saveFile, clearParseCache } from "../files.js";
27
27
  import { invalidateStateCache } from "../state.js";
28
28
  import { VALIDATION_VERDICTS, isValidMilestoneVerdict } from "../verdict-parser.js";
29
29
  import { insertMilestoneValidationGates } from "../milestone-validation-gates.js";
@@ -31,7 +31,10 @@ import { logWarning } from "../workflow-logger.js";
31
31
  import { UokGateRunner } from "../uok/gate-runner.js";
32
32
  import { loadEffectiveGSDPreferences } from "../preferences.js";
33
33
  import { resolveUokFlags } from "../uok/flags.js";
34
- import { compactTextParts, hasBrowserEvidenceText, hasBrowserRequiredText } from "../browser-evidence.js";
34
+ import {
35
+ applyBrowserEvidenceGate,
36
+ browserEvidenceGateRequiresAttention,
37
+ } from "../milestone-validation-evidence.js";
35
38
 
36
39
  export interface ValidateMilestoneParams {
37
40
  milestoneId: string;
@@ -78,86 +81,6 @@ function getRequiredVerificationClasses(milestoneId: string): string[] {
78
81
  return required;
79
82
  }
80
83
 
81
- function hasRuntimeExecutableUatEvidenceText(text: string): boolean {
82
- if (!/\buatType:\s*runtime-executable\b/i.test(text)) return false;
83
- if (!/\bverdict:\s*PASS\b/i.test(text)) return false;
84
- return /^\|\s*[^|\n]+\s*\|\s*runtime\s*\|\s*PASS\s*\|[^|\n]*\bgsd_uat_exec\b/mi.test(text);
85
- }
86
-
87
- async function browserEvidenceGateRequiresAttention(
88
- params: ValidateMilestoneParams,
89
- basePath: string,
90
- ): Promise<boolean> {
91
- if (params.verdict !== "pass") return false;
92
-
93
- const milestone = getMilestone(params.milestoneId);
94
- const slices = getMilestoneSlices(params.milestoneId);
95
- const requirementText = compactTextParts([
96
- milestone?.vision,
97
- milestone?.success_criteria,
98
- milestone?.verification_uat,
99
- params.successCriteriaChecklist,
100
- params.verificationClasses,
101
- ...slices.flatMap((slice) => [
102
- slice.demo,
103
- slice.goal,
104
- slice.success_criteria,
105
- ]),
106
- ]);
107
- if (!hasBrowserRequiredText(requirementText)) return false;
108
-
109
- // Collect per-slice evidence so the runtime bypass is checked independently
110
- // for each slice. Concatenating all slices before checking would allow runtime
111
- // evidence from one slice to cover another slice's browser requirements.
112
- const sliceEvidencePairs: Array<{ sliceRequirementText: string; evidenceText: string }> = [];
113
- for (const slice of slices) {
114
- const chunks: string[] = [];
115
- const artifactPath = `milestones/${params.milestoneId}/slices/${slice.id}/${slice.id}-ASSESSMENT.md`;
116
- const artifact = getArtifact(artifactPath);
117
- if (artifact?.full_content) chunks.push(artifact.full_content);
118
- const assessmentPath = resolveSliceFile(basePath, params.milestoneId, slice.id, "ASSESSMENT");
119
- const assessmentContent = assessmentPath ? await loadFile(assessmentPath) : null;
120
- if (assessmentContent) chunks.push(assessmentContent);
121
- sliceEvidencePairs.push({
122
- sliceRequirementText: compactTextParts([slice.demo, slice.goal, slice.success_criteria]),
123
- evidenceText: chunks.join("\n\n"),
124
- });
125
- }
126
- const persistedEvidence = sliceEvidencePairs.map((s) => s.evidenceText).join("\n\n");
127
-
128
- // Runtime bypass: each slice whose own requirement text has browser-observable
129
- // criteria must have its own runtime-executable UAT evidence. When no individual
130
- // slice has slice-level browser requirements (e.g., they come from milestone-level
131
- // fields only), fall back to checking whether any slice has runtime evidence.
132
- const browserRequiringSlices = sliceEvidencePairs.filter((s) =>
133
- hasBrowserRequiredText(s.sliceRequirementText),
134
- );
135
- const runtimeBypasses =
136
- browserRequiringSlices.length > 0
137
- ? browserRequiringSlices.every((s) => hasRuntimeExecutableUatEvidenceText(s.evidenceText))
138
- : sliceEvidencePairs.some((s) => hasRuntimeExecutableUatEvidenceText(s.evidenceText));
139
- if (runtimeBypasses) return false;
140
-
141
- const validationEvidence = compactTextParts([
142
- params.successCriteriaChecklist,
143
- params.verificationClasses,
144
- params.verdictRationale,
145
- params.remediationPlan,
146
- ]);
147
- return !hasBrowserEvidenceText(`${persistedEvidence}\n\n${validationEvidence}`);
148
- }
149
-
150
- function applyBrowserEvidenceGate(params: ValidateMilestoneParams): ValidateMilestoneParams {
151
- const note = "Browser evidence gate: Browser-observable acceptance criteria were detected, but no persisted ASSESSMENT or validation evidence recorded browser actions with assertions. Downgraded from pass to needs-attention.";
152
- return {
153
- ...params,
154
- verdict: "needs-attention",
155
- verdictRationale: params.verdictRationale.trim()
156
- ? `${params.verdictRationale.trim()}\n\n${note}`
157
- : note,
158
- };
159
- }
160
-
161
84
  function renderValidationMarkdown(params: ValidateMilestoneParams): string {
162
85
  let md = `---
163
86
  verdict: ${params.verdict}
@@ -279,6 +202,24 @@ export async function handleValidateMilestone(
279
202
  let projectionStale = false;
280
203
  try {
281
204
  await saveFile(validationPath, validationMd);
205
+ const projectRoot = resolveWorktreeProjectRoot(basePath);
206
+ if (projectRoot !== artifactBasePath) {
207
+ const projectValidationPath = join(
208
+ gsdProjectionRoot(projectRoot),
209
+ "milestones",
210
+ effectiveParams.milestoneId,
211
+ `${effectiveParams.milestoneId}-VALIDATION.md`,
212
+ );
213
+ try {
214
+ await saveFile(projectValidationPath, validationMd);
215
+ } catch (mirrorErr) {
216
+ logWarning(
217
+ "projection",
218
+ `validate_milestone project-root VALIDATION mirror failed for ${effectiveParams.milestoneId}`,
219
+ { error: (mirrorErr as Error).message },
220
+ );
221
+ }
222
+ }
282
223
  } catch (renderErr) {
283
224
  projectionStale = true;
284
225
  logWarning("projection", `validate_milestone projection write failed for ${effectiveParams.milestoneId}; DB validation remains committed`, {