@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
@@ -21,6 +21,14 @@ function runGit(args: string[], cwd: string): void {
21
21
  });
22
22
  }
23
23
 
24
+ function renderOutcomeWidget(widget: unknown): string {
25
+ const component = (widget as any)(
26
+ { requestRender() {} },
27
+ { fg: (_color: string, text: string) => text, bold: (text: string) => text },
28
+ );
29
+ return component.render(100).join("\n");
30
+ }
31
+
24
32
  test("cleanupAfterLoopExit preserves paused auto badge after provider pause", async () => {
25
33
  const base = mkdtempSync(join(tmpdir(), "gsd-paused-cleanup-"));
26
34
  const previousCwd = process.cwd();
@@ -167,13 +175,17 @@ test("cleanupAfterLoopExit preserves completion closeout surface after stopAuto
167
175
  false,
168
176
  "completion cleanup must not replace the closeout surface with a generic outcome card",
169
177
  );
170
- assert.equal(autoSession.completionStopInProgress, false);
178
+ assert.equal(
179
+ autoSession.completionStopInProgress,
180
+ true,
181
+ "completion closeout preservation must survive post-loop cleanup until the next agent turn",
182
+ );
171
183
  } finally {
172
184
  autoSession.reset();
173
185
  }
174
186
  });
175
187
 
176
- test("cleanupAfterLoopExit clears completionStopInProgress even when preserveStepSurfaceAfterLoopExit is also set", async () => {
188
+ test("cleanupAfterLoopExit preserves completionStopInProgress even when preserveStepSurfaceAfterLoopExit is also set", async () => {
177
189
  const statusCalls: unknown[] = [];
178
190
  const widgetCalls: unknown[] = [];
179
191
 
@@ -196,8 +208,8 @@ test("cleanupAfterLoopExit clears completionStopInProgress even when preserveSte
196
208
 
197
209
  assert.equal(
198
210
  autoSession.completionStopInProgress,
199
- false,
200
- "completionStopInProgress must be cleared even when preserveStepSurfaceAfterLoopExit was also set",
211
+ true,
212
+ "completionStopInProgress must survive cleanup even when preserveStepSurfaceAfterLoopExit was also set",
201
213
  );
202
214
  assert.equal(autoSession.preserveStepSurfaceAfterLoopExit, false);
203
215
  } finally {
@@ -641,6 +653,10 @@ test("stopAuto foreground completion closeout reroots session and preserves the
641
653
  false,
642
654
  "foreground completion stop must not install a replacement roll-up widget over the transcript",
643
655
  );
656
+ assert.ok(
657
+ widgetCalls.some(([key, value]) => key === "gsd-progress" && value === undefined),
658
+ "foreground completion stop must clear stale progress controls in the rerooted session",
659
+ );
644
660
  assert.ok(
645
661
  notifications.every(message => !message.includes("/gsd auto to resume")),
646
662
  "completion stop notification must not tell users to resume a finished auto run",
@@ -649,10 +665,15 @@ test("stopAuto foreground completion closeout reroots session and preserves the
649
665
  notifications.every(message => !message.includes("Auto-mode stopped") && !message.includes("Session:") && !message.includes("Debug log written")),
650
666
  "completion stop must not append generic stop/session/debug notifications after the report",
651
667
  );
652
- assert.ok(
653
- widgetCalls.every(([key, value]) => key !== "gsd-outcome" || value === undefined),
654
- "foreground completion stop must not replace the transcript with a generic outcome card",
668
+ const outcome = widgetCalls.find(([key, value]) => key === "gsd-outcome" && typeof value === "function")?.[1];
669
+ assert.equal(
670
+ typeof outcome,
671
+ "function",
672
+ "foreground completion stop must install a durable closeout outcome in the rerooted session",
655
673
  );
674
+ const rendered = renderOutcomeWidget(outcome);
675
+ assert.match(rendered, /Milestone M003 complete/);
676
+ assert.match(rendered, /Review the closeout/);
656
677
 
657
678
  const widgetCallCountBeforePostLoopCleanup = widgetCalls.length;
658
679
  await cleanupAfterLoopExit(ctx);
@@ -822,7 +843,7 @@ test("stopAuto closeout-transcript preservation suppresses generic stop widgets"
822
843
  }
823
844
  });
824
845
 
825
- test("stopAuto foreground all-complete closeout preserves the transcript surface", async () => {
846
+ test("stopAuto foreground all-complete closeout installs a durable terminal outcome", async () => {
826
847
  const base = mkdtempSync(join(tmpdir(), "gsd-all-complete-closeout-"));
827
848
  const previousCwd = process.cwd();
828
849
  const widgetCalls: Array<[string, unknown]> = [];
@@ -875,8 +896,15 @@ test("stopAuto foreground all-complete closeout preserves the transcript surface
875
896
  false,
876
897
  "foreground all-complete closeout must not replace the visible transcript with a roll-up widget",
877
898
  );
878
- const finalOutcome = widgetCalls.filter(([key]) => key === "gsd-outcome").at(-1);
879
- assert.equal(finalOutcome?.[1], undefined, "foreground all-complete closeout must not add an outcome replacement");
899
+ const finalOutcome = widgetCalls.filter(([key]) => key === "gsd-outcome").at(-1)?.[1];
900
+ assert.equal(
901
+ typeof finalOutcome,
902
+ "function",
903
+ "foreground all-complete closeout must install a durable terminal outcome",
904
+ );
905
+ const rendered = renderOutcomeWidget(finalOutcome);
906
+ assert.match(rendered, /All milestones complete/);
907
+ assert.match(rendered, /start new work/);
880
908
  } finally {
881
909
  try { closeDatabase(); } catch { /* noop */ }
882
910
  autoSession.reset();
@@ -6,12 +6,15 @@ import assert from "node:assert/strict";
6
6
 
7
7
  import {
8
8
  autoSession,
9
+ clearAutoToolSurfaceSnapshot,
9
10
  clearToolInvocationError,
10
11
  getAutoRuntimeSnapshot,
12
+ recordAutoToolSurfaceSnapshot,
11
13
  } from "../auto-runtime-state.ts";
12
14
 
13
15
  test("getAutoRuntimeSnapshot includes orchestration phase when available", () => {
14
16
  autoSession.reset();
17
+ clearAutoToolSurfaceSnapshot();
15
18
  autoSession.active = true;
16
19
  autoSession.basePath = "/tmp/project";
17
20
  autoSession.orchestration = {
@@ -33,10 +36,40 @@ test("getAutoRuntimeSnapshot includes orchestration phase when available", () =>
33
36
  assert.equal(snap.orchestrationPhase, "running");
34
37
  assert.equal(snap.orchestrationTransitionCount, 3);
35
38
  assert.equal(snap.orchestrationLastTransitionAt, 123);
39
+ assert.equal(snap.toolSurface, null);
36
40
 
37
41
  autoSession.reset();
38
42
  });
39
43
 
44
+ test("getAutoRuntimeSnapshot includes the active typed tool-surface snapshot", () => {
45
+ autoSession.reset();
46
+ clearAutoToolSurfaceSnapshot();
47
+ autoSession.active = true;
48
+
49
+ recordAutoToolSurfaceSnapshot({
50
+ source: "dispatch-scope",
51
+ unitType: "run-uat",
52
+ modelFacingToolNames: ["read", "read", "gsd_uat_exec"],
53
+ registeredToolNames: ["read", "browser_navigate"],
54
+ scopedToolNames: ["read", "browser_navigate"],
55
+ presentedToolNames: ["gsd_uat_exec"],
56
+ capturedAt: 123,
57
+ });
58
+
59
+ const snap = getAutoRuntimeSnapshot();
60
+
61
+ assert.equal(snap.toolSurface?.source, "dispatch-scope");
62
+ assert.equal(snap.toolSurface?.unitType, "run-uat");
63
+ assert.deepEqual(snap.toolSurface?.modelFacingToolNames, ["read", "gsd_uat_exec"]);
64
+ assert.deepEqual(snap.toolSurface?.registeredToolNames, ["read", "browser_navigate"]);
65
+ assert.deepEqual(snap.toolSurface?.scopedToolNames, ["read", "browser_navigate"]);
66
+ assert.deepEqual(snap.toolSurface?.presentedToolNames, ["gsd_uat_exec"]);
67
+ assert.equal(snap.toolSurface?.capturedAt, 123);
68
+
69
+ autoSession.reset();
70
+ clearAutoToolSurfaceSnapshot();
71
+ });
72
+
40
73
  test("clearToolInvocationError clears stale tool error state for active auto sessions", () => {
41
74
  autoSession.reset();
42
75
  autoSession.active = true;
@@ -56,4 +89,5 @@ test("getAutoRuntimeSnapshot omits orchestration phase when seam not wired", ()
56
89
  assert.equal(snap.orchestrationPhase, undefined);
57
90
  assert.equal(snap.orchestrationTransitionCount, undefined);
58
91
  assert.equal(snap.orchestrationLastTransitionAt, undefined);
92
+ assert.equal(snap.toolSurface, null);
59
93
  });
@@ -17,6 +17,7 @@ import { tmpdir } from "node:os";
17
17
  import { randomUUID } from "node:crypto";
18
18
 
19
19
  import { resolveCanonicalMilestoneRoot } from "../worktree-manager.ts";
20
+ import { resolveCloseoutArtifactProjection } from "../artifact-projection.ts";
20
21
 
21
22
  function makeTmpBase(): string {
22
23
  const base = join(tmpdir(), `gsd-canon-test-${randomUUID()}`);
@@ -106,3 +107,22 @@ test("only returns the worktree for the requested milestone, not siblings", () =
106
107
  cleanup(base);
107
108
  }
108
109
  });
110
+
111
+ test("resolveCloseoutArtifactProjection names project and canonical artifact roots", () => {
112
+ const base = makeTmpBase();
113
+ try {
114
+ const wtPath = makeLiveWorktree(base, "M001");
115
+ const projection = resolveCloseoutArtifactProjection({
116
+ milestoneId: "M001",
117
+ basePath: wtPath,
118
+ originalBasePath: base,
119
+ });
120
+
121
+ assert.equal(projection.projectRoot, base);
122
+ assert.equal(projection.canonicalMilestoneRoot, wtPath);
123
+ assert.equal(projection.summaryArtifactBasePath, wtPath);
124
+ assert.equal(projection.gateEvidenceBasePath, wtPath);
125
+ } finally {
126
+ cleanup(base);
127
+ }
128
+ });
@@ -696,3 +696,25 @@ test("ensureCodebaseMapFresh: does not rewrite expired metadata when fingerprint
696
696
  cleanup(base);
697
697
  }
698
698
  });
699
+
700
+ test("ensureCodebaseMapFresh: detects file changes within the TTL window", () => {
701
+ const base = makeTmpRepo();
702
+ try {
703
+ addFile(base, "src/main.ts");
704
+ // Generate initial map with a long TTL so the cache is still active.
705
+ const initial = ensureCodebaseMapFresh(base, undefined, { ttlMs: 60_000 });
706
+ assert.equal(initial.status, "generated");
707
+
708
+ // Add a new tracked file while the TTL is still active.
709
+ addFile(base, "src/new.ts");
710
+
711
+ // Must detect the change even though the TTL has not expired.
712
+ const refreshed = ensureCodebaseMapFresh(base, undefined, { ttlMs: 60_000 });
713
+ assert.equal(refreshed.status, "updated");
714
+ assert.equal(refreshed.reason, "files-changed");
715
+ const written = readCodebaseMap(base);
716
+ assert.ok(written?.includes("`src/new.ts`"));
717
+ } finally {
718
+ cleanup(base);
719
+ }
720
+ });
@@ -64,3 +64,14 @@ test("getWorkspaceGitBlockMessageForBase allows doctor on product conflicts", as
64
64
  cleanup(base);
65
65
  }
66
66
  });
67
+
68
+ test("getWorkspaceGitBlockMessageForBase allows forensics on product conflicts", async () => {
69
+ const base = makeTempRepo("gsd-dispatch-ws-git-forensics-");
70
+ try {
71
+ seedProductConflict(base);
72
+ const blocked = await getWorkspaceGitBlockMessageForBase(base, "forensics");
73
+ assert.equal(blocked, null);
74
+ } finally {
75
+ cleanup(base);
76
+ }
77
+ });
@@ -10,7 +10,7 @@
10
10
  */
11
11
  import test from "node:test";
12
12
  import assert from "node:assert/strict";
13
- import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
13
+ import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, statSync, writeFileSync } from "node:fs";
14
14
  import { join } from "node:path";
15
15
  import { tmpdir } from "node:os";
16
16
  import { randomUUID } from "node:crypto";
@@ -486,3 +486,40 @@ test("state.ts needs-remediation blocker messages reference /gsd verdict", async
486
486
  `expected at least 2 references to /gsd verdict in state.ts blockers, found ${occurrences.length}`,
487
487
  );
488
488
  });
489
+
490
+ // ─── WAL checkpoint regression (#563) ───────────────────────────────────
491
+
492
+ test("handleVerdict pass checkpoints WAL so subsequent processes see the updated verdict (#563)", async () => {
493
+ // Regression: before the fix, executeValidateMilestone wrote the assessment
494
+ // only into the WAL file. The next /gsd auto invocation (a new process)
495
+ // opened gsd.db directly and saw stale pre-verdict data because the WAL
496
+ // had not been flushed. The fix adds checkpointDatabase() after a
497
+ // successful verdict so the WAL is truncated and gsd.db is self-contained.
498
+ const base = makeBase();
499
+ try {
500
+ openTestDb(base);
501
+ seedMilestone("M001", "Checkpoint Test Milestone");
502
+ seedSlice("M001", "S01", "complete");
503
+ writeValidation(base, "M001", "needs-attention");
504
+
505
+ const { ctx } = makeMockCtx();
506
+ await handleVerdict("pass --milestone M001", ctx, base);
507
+
508
+ // After handleVerdict succeeds, PRAGMA wal_checkpoint(TRUNCATE) must have
509
+ // run. The WAL file is truncated to zero bytes — a new process reading
510
+ // gsd.db directly sees the updated verdict without needing the WAL.
511
+ const walPath = join(base, ".gsd", "gsd.db-wal");
512
+ if (existsSync(walPath)) {
513
+ const { size } = statSync(walPath);
514
+ assert.equal(
515
+ size,
516
+ 0,
517
+ "WAL must be zero bytes after handleVerdict pass — checkpointDatabase() was not called (#563)",
518
+ );
519
+ }
520
+ } finally {
521
+ closeDatabase();
522
+ invalidateStateCache();
523
+ cleanup(base);
524
+ }
525
+ });
@@ -115,6 +115,21 @@ test('debugCount increments counters', () => {
115
115
  assert.strictEqual(summary.dispatches, 5);
116
116
  });
117
117
 
118
+ test('debugCount tracks gitInvocations and writeDebugSummary reports it', () => {
119
+ const tmp = createTempGsdDir();
120
+ enableDebug(tmp);
121
+
122
+ debugCount('gitInvocations');
123
+ debugCount('gitInvocations', 13);
124
+
125
+ const logPath = writeDebugSummary()!;
126
+ const lines = readLogLines(logPath);
127
+
128
+ const summary = lines.find(l => l.event === 'debug-summary') as any;
129
+ assert.ok(summary, 'should have debug-summary event');
130
+ assert.strictEqual(summary.gitInvocations, 14);
131
+ });
132
+
118
133
  test('debugPeak tracks max values', () => {
119
134
  const tmp = createTempGsdDir();
120
135
  enableDebug(tmp);
@@ -14,7 +14,15 @@ import { execFileSync } from "node:child_process";
14
14
 
15
15
  import { DISPATCH_RULES, resolveDispatch, type DispatchContext } from "../auto-dispatch.ts";
16
16
  import { AutoSession } from "../auto/session.ts";
17
- import { closeDatabase, insertAssessment, insertGateRow, insertMilestone, insertSlice, openDatabase } from "../gsd-db.ts";
17
+ import {
18
+ closeDatabase,
19
+ getPendingGates,
20
+ insertAssessment,
21
+ insertGateRow,
22
+ insertMilestone,
23
+ insertSlice,
24
+ openDatabase,
25
+ } from "../gsd-db.ts";
18
26
 
19
27
  function makeBase(): string {
20
28
  const base = mkdtempSync(join(tmpdir(), "gsd-complete-dispatch-"));
@@ -243,7 +251,7 @@ describe("complete phase dispatch guard (#5683)", () => {
243
251
  assert.equal(result?.reason, "All milestones complete.");
244
252
  });
245
253
 
246
- test("blocks terminal stop when closed milestone still has pending gates", async () => {
254
+ test("closes stale pending gates from milestone validation before terminal stop", async () => {
247
255
  base = makeBase();
248
256
  openDatabase(join(base, ".gsd", "gsd.db"));
249
257
  insertMilestone({ id: "M001", title: "Milestone One", status: "complete" });
@@ -268,10 +276,33 @@ describe("complete phase dispatch guard (#5683)", () => {
268
276
 
269
277
  const result = await rule.match(ctx);
270
278
 
279
+ assert.equal(result?.action, "stop");
280
+ assert.equal(result?.reason, "All milestones complete.");
281
+ assert.deepEqual(getPendingGates("M001", "S01"), []);
282
+ });
283
+
284
+ test("blocks terminal stop when pending gates have no closeout evidence", async () => {
285
+ base = makeBase();
286
+ openDatabase(join(base, ".gsd", "gsd.db"));
287
+ insertMilestone({ id: "M001", title: "Milestone One", status: "complete" });
288
+ insertSlice({ milestoneId: "M001", id: "S01", title: "Done", status: "complete" });
289
+ insertGateRow({
290
+ milestoneId: "M001",
291
+ sliceId: "S01",
292
+ gateId: "Q3",
293
+ scope: "slice",
294
+ status: "pending",
295
+ });
296
+
297
+ const ctx = buildDispatchCtx(base);
298
+ ctx.state.phase = "complete";
299
+
300
+ const result = await rule.match(ctx);
301
+
271
302
  assert.equal(result?.action, "stop");
272
303
  assert.equal(result?.level, "warning");
273
304
  assert.match(result?.reason ?? "", /closeout-consistency-blocked/);
274
- assert.match(result?.reason ?? "", /quality gate Q3 is still pending/);
305
+ assert.match(result?.reason ?? "", /latest milestone validation is "absent"/);
275
306
  });
276
307
  });
277
308
 
@@ -0,0 +1,88 @@
1
+ // Project/App: gsd-pi
2
+ // File Purpose: Regression coverage for run-uat browser tool availability checks.
3
+
4
+ import test from "node:test";
5
+ import assert from "node:assert/strict";
6
+ import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
7
+ import { tmpdir } from "node:os";
8
+ import { join } from "node:path";
9
+
10
+ import { DISPATCH_RULES, type DispatchContext } from "../auto-dispatch.ts";
11
+ import type { GSDState } from "../types.ts";
12
+
13
+ type DispatchRuleEntry = (typeof DISPATCH_RULES)[number];
14
+
15
+ function runUatRule(): DispatchRuleEntry {
16
+ const rule = DISPATCH_RULES.find((entry) => entry.name === "run-uat (post-completion)");
17
+ assert.ok(rule, "run-uat dispatch rule must exist");
18
+ return rule;
19
+ }
20
+
21
+ function makeState(): GSDState {
22
+ return {
23
+ activeMilestone: { id: "M001", title: "Browser UAT" },
24
+ activeSlice: { id: "S02", title: "Next Slice" },
25
+ activeTask: null,
26
+ phase: "verifying",
27
+ recentDecisions: [],
28
+ blockers: [],
29
+ nextAction: "",
30
+ registry: [],
31
+ };
32
+ }
33
+
34
+ function scaffoldRunUatProject(basePath: string): void {
35
+ const milestoneDir = join(basePath, ".gsd", "milestones", "M001");
36
+ const sliceDir = join(milestoneDir, "slices", "S01");
37
+ mkdirSync(sliceDir, { recursive: true });
38
+
39
+ writeFileSync(join(milestoneDir, "M001-ROADMAP.md"), [
40
+ "# M001: Browser UAT",
41
+ "",
42
+ "## Slices",
43
+ "",
44
+ "- [x] **S01: Completed browser slice** `risk:low` `depends:[]`",
45
+ "- [ ] **S02: Next slice** `risk:low` `depends:[S01]`",
46
+ "",
47
+ ].join("\n"), "utf-8");
48
+
49
+ writeFileSync(join(sliceDir, "S01-SUMMARY.md"), "# S01 Summary\n\nDone.\n", "utf-8");
50
+ writeFileSync(join(sliceDir, "S01-UAT.md"), [
51
+ "# S01 UAT",
52
+ "",
53
+ "## UAT Type",
54
+ "- UAT mode: human-experience",
55
+ "",
56
+ "Open the app in a browser and verify the completed user flow.",
57
+ "",
58
+ ].join("\n"), "utf-8");
59
+ }
60
+
61
+ function makeContext(basePath: string, overrides: Partial<DispatchContext> = {}): DispatchContext {
62
+ return {
63
+ basePath,
64
+ mid: "M001",
65
+ midTitle: "Browser UAT",
66
+ state: makeState(),
67
+ prefs: undefined,
68
+ activeTools: ["read", "gsd_uat_exec", "gsd_uat_result_save"],
69
+ ...overrides,
70
+ };
71
+ }
72
+
73
+ test("run-uat browser preflight uses registered tools when the active surface is scoped", async (t) => {
74
+ const basePath = mkdtempSync(join(tmpdir(), "gsd-run-uat-browser-tools-"));
75
+ t.after(() => rmSync(basePath, { recursive: true, force: true }));
76
+ scaffoldRunUatProject(basePath);
77
+
78
+ const blocked = await runUatRule().match(makeContext(basePath));
79
+ assert.equal(blocked?.action, "stop");
80
+ assert.match(blocked?.action === "stop" ? blocked.reason : "", /run-uat tool surface has none/);
81
+
82
+ const dispatched = await runUatRule().match(makeContext(basePath, {
83
+ registeredTools: ["browser_navigate"],
84
+ }));
85
+ assert.equal(dispatched?.action, "dispatch");
86
+ assert.equal(dispatched?.action === "dispatch" ? dispatched.unitType : undefined, "run-uat");
87
+ assert.equal(dispatched?.action === "dispatch" ? dispatched.unitId : undefined, "M001/S01");
88
+ });
@@ -26,6 +26,24 @@ test("filterDoctorIssues keeps project and environment issues in scoped reports"
26
26
  );
27
27
  });
28
28
 
29
+ test("filterDoctorIssues keeps invalid_preferences issues regardless of preferences file scope", () => {
30
+ // Both global and project preference diagnostics should survive scope filtering.
31
+ // doctor.ts uses unitId: "project" for all invalid_preferences issues so they
32
+ // pass through the scope filter the same way other project-level issues do.
33
+ const issues = [
34
+ { severity: "error", code: "invalid_preferences", scope: "project", unitId: "project", message: "global PREFERENCES.md parse error", fixable: false },
35
+ { severity: "error", code: "invalid_preferences", scope: "project", unitId: "project", message: "project PREFERENCES.md parse error", fixable: false },
36
+ { severity: "error", code: "invalid_preferences", scope: "project", unitId: "global", message: "stale unitId — should be filtered out", fixable: false },
37
+ ] as const;
38
+
39
+ const filtered = filterDoctorIssues([...issues], { scope: "M016", includeWarnings: true });
40
+ assert.deepEqual(
41
+ filtered.map((issue) => issue.message),
42
+ ["global PREFERENCES.md parse error", "project PREFERENCES.md parse error"],
43
+ "invalid_preferences issues with unitId: project survive scope filtering; unitId: global is dropped",
44
+ );
45
+ });
46
+
29
47
  test("checkEngineHealth reports db_unavailable when gsd.db exists but the DB is closed", async (t) => {
30
48
  const base = mkdtempSync(join(tmpdir(), "gsd-doctor-db-unavailable-"));
31
49
  t.after(() => rmSync(base, { recursive: true, force: true }));
@@ -6,7 +6,8 @@ import { join } from "node:path";
6
6
  import { tmpdir } from "node:os";
7
7
  import { randomUUID } from "node:crypto";
8
8
 
9
- import { openDatabase, closeDatabase, getAllMilestones } from "../gsd-db.ts";
9
+ import { openDatabase, closeDatabase, getAllMilestones, getArtifact } from "../gsd-db.ts";
10
+ import { parseProject } from "../schemas/parsers.ts";
10
11
  import { markApprovalGateVerified, clearDiscussionFlowState } from "../bootstrap/write-gate.ts";
11
12
  import { executeSummarySave } from "../tools/workflow-tool-executors.ts";
12
13
 
@@ -107,3 +108,65 @@ test("executeSummarySave registers milestones when PROJECT.md uses canonical em-
107
108
  assert.deepEqual(result.details.registeredMilestones, ["M001", "M002"]);
108
109
  assert.equal(getAllMilestones().length, 2);
109
110
  });
111
+
112
+ test("executeSummarySave self-heals the Milestone Sequence when DB already has milestones but content parses zero", async (t) => {
113
+ const base = setupBase(t);
114
+
115
+ // 1) First save registers M001 as complete; DB now holds the milestone with
116
+ // authoritative status.
117
+ const canonical = [
118
+ "# Project",
119
+ "",
120
+ "## Milestone Sequence",
121
+ "",
122
+ "<!-- Check off milestones as they complete. -->",
123
+ "",
124
+ "- [x] M001: Foo — bar",
125
+ "",
126
+ ].join("\n");
127
+ await inProjectDir(base, () => executeSummarySave({ artifact_type: "PROJECT", content: canonical }, base));
128
+ assert.equal(getAllMilestones().length, 1);
129
+
130
+ // 2) A completion re-save reflows the sequence with an en-dash separator the
131
+ // canonical parser rejects → zero parseable lines, but the DB is intact.
132
+ // (A trailing non-bullet section prevents MILESTONE_LINE_RE from bridging
133
+ // onto a later bullet.) The checkbox here is deliberately WRONG (unchecked)
134
+ // to prove the rebuild takes status from the DB, not the malformed content.
135
+ const malformed = [
136
+ "# Project",
137
+ "",
138
+ "## Milestone Sequence",
139
+ "",
140
+ "<!-- Check off milestones as they complete. -->",
141
+ "",
142
+ "- [ ] M001: Foo – shipped bar", // en-dash U+2013, not accepted by MILESTONE_LINE_RE
143
+ "",
144
+ "## Notes",
145
+ "",
146
+ "Trailing prose with no list bullets.",
147
+ "",
148
+ ].join("\n");
149
+
150
+ const result = await inProjectDir(base, () => executeSummarySave({ artifact_type: "PROJECT", content: malformed }, base));
151
+
152
+ // Non-fatal: the save succeeds and reports the self-heal.
153
+ assert.notEqual(result.isError, true);
154
+ assert.equal(result.details.milestoneSequenceSelfHealed, true);
155
+ assert.deepEqual(result.details.registeredMilestones, ["M001"]);
156
+ assert.equal(getAllMilestones().length, 1);
157
+
158
+ // The persisted PROJECT.md now parses canonically and preserves status + prose.
159
+ const persisted = getArtifact("PROJECT.md");
160
+ assert.ok(persisted);
161
+ const parsed = parseProject(persisted!.full_content);
162
+ assert.equal(parsed.milestones.length, 1);
163
+ const m001 = parsed.milestones.find(m => m.id === "M001");
164
+ assert.ok(m001);
165
+ // Status comes from the DB (M001 complete), not the unchecked box in the
166
+ // malformed content.
167
+ assert.equal(m001!.done, true);
168
+ assert.equal(m001!.title, "Foo");
169
+ assert.match(m001!.oneLiner, /shipped bar/);
170
+ // The HTML comment hint survives the rebuild.
171
+ assert.match(persisted!.full_content, /Check off milestones as they complete/);
172
+ });
@@ -38,6 +38,7 @@ test("execute-task prompt renders compact execution and completion gates", async
38
38
  planPath: ".gsd/milestones/M001/slices/S01/S01-PLAN.md",
39
39
  taskPlanPath: ".gsd/milestones/M001/slices/S01/tasks/T01-PLAN.md",
40
40
  priorTaskLines: "- None",
41
+ onDemandContext: "",
41
42
  skillActivation: "Load relevant skills.",
42
43
  inlinedTemplates: "### Output Template: Task Summary\nSource: `templates/task-summary.md`",
43
44
  templatesDir: join(fixtureRoot, "templates"),
@@ -48,8 +48,4 @@ Closes #123
48
48
 
49
49
  ## Rollback And Compatibility
50
50
 
51
- - Disable writer sequence enrichment
52
-
53
- ## AI Assistance Disclosure
54
-
55
- This PR was prepared with AI assistance.
51
+ - Disable writer sequence enrichment
@@ -53,8 +53,4 @@ Closes #123
53
53
 
54
54
  ## Rollback And Compatibility
55
55
 
56
- - Disable writer sequence enrichment
57
-
58
- ## AI Assistance Disclosure
59
-
60
- This PR was prepared with AI assistance.
56
+ - Disable writer sequence enrichment
@@ -3,7 +3,7 @@
3
3
 
4
4
  import { describe, test, beforeEach, afterEach } from "node:test";
5
5
  import assert from "node:assert/strict";
6
- import { mkdtempSync, rmSync } from "node:fs";
6
+ import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
7
7
  import { join } from "node:path";
8
8
  import { tmpdir } from "node:os";
9
9
 
@@ -18,6 +18,7 @@ import {
18
18
  insertSlice,
19
19
  } from "../gsd-db.ts";
20
20
  import type { GateVerdict } from "../types.ts";
21
+ import { closeQualityGatesFromEvidence } from "../quality-gate-closure.ts";
21
22
 
22
23
  describe("gate-state canonicalization (#4950)", () => {
23
24
  let tmpDir: string;
@@ -99,4 +100,50 @@ describe("gate-state canonicalization (#4950)", () => {
99
100
  assert.notEqual(g.verdict, "", `gate ${g.gate_id} verdict must not be empty string`);
100
101
  }
101
102
  });
103
+
104
+ test("closeQualityGatesFromEvidence repairs pending gate from durable section", () => {
105
+ insertGateRow({ milestoneId: "M001", sliceId: "S01", gateId: "Q3", scope: "slice" });
106
+ mkdirSync(join(tmpDir, ".gsd", "milestones", "M001", "slices", "S01"), { recursive: true });
107
+ writeFileSync(
108
+ join(tmpDir, ".gsd", "milestones", "M001", "slices", "S01", "S01-PLAN.md"),
109
+ [
110
+ "# S01",
111
+ "",
112
+ "## Threat Surface",
113
+ "",
114
+ "- Credential stuffing is rate-limited.",
115
+ ].join("\n"),
116
+ );
117
+
118
+ const result = closeQualityGatesFromEvidence("M001", { artifactBasePath: tmpDir });
119
+
120
+ assert.deepEqual(result.unresolved, []);
121
+ assert.deepEqual(result.repaired, [{ gateId: "Q3", sliceId: "S01", verdict: "pass" }]);
122
+ assert.equal(getPendingGates("M001", "S01").length, 0);
123
+ assert.equal(getGateResults("M001", "S01")[0].verdict, "pass");
124
+ });
125
+
126
+ test("closeQualityGatesFromEvidence omits stale pending gate after validation pass", () => {
127
+ insertGateRow({ milestoneId: "M001", sliceId: "S01", gateId: "Q4", scope: "slice" });
128
+
129
+ const result = closeQualityGatesFromEvidence("M001", {
130
+ artifactBasePath: tmpDir,
131
+ milestoneValidationPassed: true,
132
+ });
133
+
134
+ assert.deepEqual(result.unresolved, []);
135
+ assert.deepEqual(result.repaired, [{ gateId: "Q4", sliceId: "S01", verdict: "omitted" }]);
136
+ assert.equal(getGateResults("M001", "S01")[0].verdict, "omitted");
137
+ });
138
+
139
+ test("closeQualityGatesFromEvidence leaves pending gate unresolved without evidence", () => {
140
+ insertGateRow({ milestoneId: "M001", sliceId: "S01", gateId: "Q3", scope: "slice" });
141
+
142
+ const result = closeQualityGatesFromEvidence("M001", { artifactBasePath: tmpDir });
143
+
144
+ assert.deepEqual(result.repaired, []);
145
+ assert.equal(result.unresolved.length, 1);
146
+ assert.equal(result.unresolved[0].gate_id, "Q3");
147
+ assert.equal(getPendingGates("M001", "S01").length, 1);
148
+ });
102
149
  });