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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (375) hide show
  1. package/dist/cli-web-branch.d.ts +2 -0
  2. package/dist/cli-web-branch.js +9 -2
  3. package/dist/help-text.js +5 -0
  4. package/dist/resources/.managed-resources-content-hash +1 -1
  5. package/dist/resources/extensions/ask-user-questions.js +78 -23
  6. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +84 -228
  7. package/dist/resources/extensions/claude-code-cli/turn-assembler.js +224 -0
  8. package/dist/resources/extensions/github-sync/templates.js +3 -3
  9. package/dist/resources/extensions/gsd/artifact-projection.js +14 -0
  10. package/dist/resources/extensions/gsd/auto/loop.js +74 -56
  11. package/dist/resources/extensions/gsd/auto/orchestrator.js +109 -11
  12. package/dist/resources/extensions/gsd/auto/phases.js +28 -3
  13. package/dist/resources/extensions/gsd/auto/run-unit.js +2 -1
  14. package/dist/resources/extensions/gsd/auto/session.js +3 -0
  15. package/dist/resources/extensions/gsd/auto-dashboard.js +16 -4
  16. package/dist/resources/extensions/gsd/auto-dispatch.js +6 -5
  17. package/dist/resources/extensions/gsd/auto-model-selection.js +8 -0
  18. package/dist/resources/extensions/gsd/auto-post-unit.js +4 -3
  19. package/dist/resources/extensions/gsd/auto-prompts.js +81 -8
  20. package/dist/resources/extensions/gsd/auto-recovery.js +48 -49
  21. package/dist/resources/extensions/gsd/auto-runtime-state.js +14 -0
  22. package/dist/resources/extensions/gsd/auto-start.js +12 -23
  23. package/dist/resources/extensions/gsd/auto-timers.js +16 -2
  24. package/dist/resources/extensions/gsd/auto-tool-tracking.js +32 -0
  25. package/dist/resources/extensions/gsd/auto-unit-tool-scope.js +4 -29
  26. package/dist/resources/extensions/gsd/auto-verification.js +7 -7
  27. package/dist/resources/extensions/gsd/auto-worktree.js +21 -19
  28. package/dist/resources/extensions/gsd/auto.js +11 -7
  29. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +28 -37
  30. package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +11 -37
  31. package/dist/resources/extensions/gsd/bootstrap/query-tools.js +2 -2
  32. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +100 -138
  33. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +63 -4
  34. package/dist/resources/extensions/gsd/closeout-consistency-gate.js +21 -4
  35. package/dist/resources/extensions/gsd/codebase-generator.js +8 -4
  36. package/dist/resources/extensions/gsd/commands/handlers/auto.js +3 -0
  37. package/dist/resources/extensions/gsd/commands-handlers.js +20 -0
  38. package/dist/resources/extensions/gsd/commands-inspect.js +4 -8
  39. package/dist/resources/extensions/gsd/commands-maintenance.js +61 -41
  40. package/dist/resources/extensions/gsd/commands-ship.js +2 -2
  41. package/dist/resources/extensions/gsd/commands-verdict.js +12 -2
  42. package/dist/resources/extensions/gsd/db-workspace.js +103 -0
  43. package/dist/resources/extensions/gsd/delegation-policy.js +2 -10
  44. package/dist/resources/extensions/gsd/discussion-handoff.js +218 -0
  45. package/dist/resources/extensions/gsd/docs/preferences-reference.md +9 -0
  46. package/dist/resources/extensions/gsd/doctor.js +16 -9
  47. package/dist/resources/extensions/gsd/error-classifier.js +1 -1
  48. package/dist/resources/extensions/gsd/git-conflict-state.js +16 -1
  49. package/dist/resources/extensions/gsd/gsd-db.js +12 -0
  50. package/dist/resources/extensions/gsd/guided-flow.js +34 -468
  51. package/dist/resources/extensions/gsd/guided-unit-completion.js +225 -0
  52. package/dist/resources/extensions/gsd/markdown-renderer.js +2 -1
  53. package/dist/resources/extensions/gsd/mcp-filter.js +2 -1
  54. package/dist/resources/extensions/gsd/mcp-tool-name.js +26 -0
  55. package/dist/resources/extensions/gsd/md-importer.js +4 -3
  56. package/dist/resources/extensions/gsd/migrate/safety.js +2 -2
  57. package/dist/resources/extensions/gsd/migration-auto-check.js +3 -2
  58. package/dist/resources/extensions/gsd/milestone-closeout-proof.js +72 -0
  59. package/dist/resources/extensions/gsd/milestone-closeout.js +12 -4
  60. package/dist/resources/extensions/gsd/milestone-merge-transaction.js +10 -0
  61. package/dist/resources/extensions/gsd/milestone-planning-persistence.js +156 -0
  62. package/dist/resources/extensions/gsd/milestone-readiness.js +77 -0
  63. package/dist/resources/extensions/gsd/milestone-settlement.js +50 -0
  64. package/dist/resources/extensions/gsd/milestone-validation-evidence.js +73 -0
  65. package/dist/resources/extensions/gsd/milestone-validation-verdict.js +57 -0
  66. package/dist/resources/extensions/gsd/parallel-eligibility.js +3 -6
  67. package/dist/resources/extensions/gsd/parallel-orchestrator.js +3 -2
  68. package/dist/resources/extensions/gsd/preferences-diagnostics.js +67 -0
  69. package/dist/resources/extensions/gsd/preferences.js +147 -29
  70. package/dist/resources/extensions/gsd/prompts/complete-slice.md +1 -0
  71. package/dist/resources/extensions/gsd/prompts/execute-task.md +2 -0
  72. package/dist/resources/extensions/gsd/prompts/guided-discuss-project.md +3 -1
  73. package/dist/resources/extensions/gsd/prompts/plan-milestone.md +2 -0
  74. package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -0
  75. package/dist/resources/extensions/gsd/prompts/refine-slice.md +1 -0
  76. package/dist/resources/extensions/gsd/prompts/system.md +1 -1
  77. package/dist/resources/extensions/gsd/provider-payload-policy.js +83 -0
  78. package/dist/resources/extensions/gsd/pull-request-process.js +13 -0
  79. package/dist/resources/extensions/gsd/quality-gate-closure.js +109 -0
  80. package/dist/resources/extensions/gsd/question-transport.js +86 -0
  81. package/dist/resources/extensions/gsd/roadmap-slices.js +8 -2
  82. package/dist/resources/extensions/gsd/slice-parallel-orchestrator.js +3 -2
  83. package/dist/resources/extensions/gsd/state.js +13 -5
  84. package/dist/resources/extensions/gsd/templates/plan.md +7 -0
  85. package/dist/resources/extensions/gsd/templates/project.md +1 -0
  86. package/dist/resources/extensions/gsd/templates/roadmap.md +1 -1
  87. package/dist/resources/extensions/gsd/templates/uat.md +5 -1
  88. package/dist/resources/extensions/gsd/tool-contract.js +52 -8
  89. package/dist/resources/extensions/gsd/tool-presentation-plan.js +15 -34
  90. package/dist/resources/extensions/gsd/tool-surface-snapshot.js +17 -0
  91. package/dist/resources/extensions/gsd/tools/plan-milestone.js +15 -143
  92. package/dist/resources/extensions/gsd/tools/reassess-roadmap.js +39 -0
  93. package/dist/resources/extensions/gsd/tools/validate-milestone.js +15 -78
  94. package/dist/resources/extensions/gsd/uat-policy.js +16 -10
  95. package/dist/resources/extensions/gsd/uat-run.js +9 -14
  96. package/dist/resources/extensions/gsd/unit-context-composer.js +40 -20
  97. package/dist/resources/extensions/gsd/unit-runtime.js +3 -2
  98. package/dist/resources/extensions/gsd/unit-tool-contracts.js +2 -1
  99. package/dist/resources/extensions/gsd/user-input-boundary.js +23 -0
  100. package/dist/resources/extensions/gsd/validation-block-guard.js +2 -0
  101. package/dist/resources/extensions/gsd/web-app-uat.js +80 -0
  102. package/dist/resources/extensions/gsd/workflow-mcp.js +15 -102
  103. package/dist/resources/extensions/gsd/workflow-reconcile.js +4 -3
  104. package/dist/resources/extensions/gsd/workflow-tool-surface.js +46 -0
  105. package/dist/resources/extensions/gsd/workspace-git-guard.js +2 -0
  106. package/dist/resources/extensions/gsd/worktree-state-projection.js +33 -4
  107. package/dist/resources/extensions/gsd/worktree-telemetry.js +12 -0
  108. package/dist/resources/extensions/shared/interview-ui.js +2 -2
  109. package/dist/resources/shared/claude-runtime-floor.js +182 -0
  110. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  111. package/dist/update-cmd.js +20 -0
  112. package/dist/web/standalone/.next/BUILD_ID +1 -1
  113. package/dist/web/standalone/.next/app-path-routes-manifest.json +7 -7
  114. package/dist/web/standalone/.next/build-manifest.json +3 -3
  115. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  116. package/dist/web/standalone/.next/react-loadable-manifest.json +8 -8
  117. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  118. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  119. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  120. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  121. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  122. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  123. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  124. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  125. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  126. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  127. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  128. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  129. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  130. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  131. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  132. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  133. package/dist/web/standalone/.next/server/app/index.html +1 -1
  134. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  135. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  136. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  137. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  138. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  139. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  140. package/dist/web/standalone/.next/server/app-paths-manifest.json +7 -7
  141. package/dist/web/standalone/.next/server/chunks/8357.js +1 -1
  142. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  143. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  144. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  145. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  146. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  147. package/dist/web/standalone/.next/static/chunks/2659.b7b129ee6a769448.js +1 -0
  148. package/dist/web/standalone/.next/static/chunks/2772.bfa657f49f955239.js +1 -0
  149. package/dist/web/standalone/.next/static/chunks/{3616.4113d484a994e411.js → 3616.3c60753b8ffcbd2e.js} +1 -1
  150. package/dist/web/standalone/.next/static/chunks/4283.e4873b058df143a1.js +2 -0
  151. package/dist/web/standalone/.next/static/chunks/5826.a46ecdd1cfe8dabc.js +1 -0
  152. package/dist/web/standalone/.next/static/chunks/796.cf859a427a2cb2ac.js +10 -0
  153. package/dist/web/standalone/.next/static/chunks/8785.2e5a118797fb2dd2.js +1 -0
  154. package/dist/web/standalone/.next/static/chunks/{webpack-dda80a1ef5587410.js → webpack-fbea77b5f9953368.js} +1 -1
  155. package/dist/web/standalone/node_modules/node-pty/build/Makefile +1 -1
  156. package/dist/web-mode.d.ts +2 -0
  157. package/dist/web-mode.js +20 -8
  158. package/package.json +2 -1
  159. package/packages/cloud-mcp-gateway/package.json +2 -2
  160. package/packages/contracts/package.json +1 -1
  161. package/packages/daemon/package.json +4 -4
  162. package/packages/gsd-agent-core/dist/session/agent-session-extensions.d.ts +2 -0
  163. package/packages/gsd-agent-core/dist/session/agent-session-extensions.d.ts.map +1 -1
  164. package/packages/gsd-agent-core/dist/session/agent-session-extensions.js +14 -0
  165. package/packages/gsd-agent-core/dist/session/agent-session-extensions.js.map +1 -1
  166. package/packages/gsd-agent-core/package.json +5 -5
  167. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  168. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js +106 -40
  169. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  170. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-extension-widgets.d.ts.map +1 -1
  171. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-extension-widgets.js +6 -0
  172. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-extension-widgets.js.map +1 -1
  173. package/packages/gsd-agent-modes/package.json +7 -7
  174. package/packages/mcp-server/dist/server.d.ts +10 -0
  175. package/packages/mcp-server/dist/server.d.ts.map +1 -1
  176. package/packages/mcp-server/dist/server.js +8 -0
  177. package/packages/mcp-server/dist/server.js.map +1 -1
  178. package/packages/mcp-server/dist/workflow-tools.d.ts +41 -0
  179. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  180. package/packages/mcp-server/dist/workflow-tools.js +2 -1
  181. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  182. package/packages/mcp-server/package.json +3 -3
  183. package/packages/native/package.json +1 -1
  184. package/packages/pi-agent-core/package.json +1 -1
  185. package/packages/pi-ai/dist/models.generated.d.ts +8 -93
  186. package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
  187. package/packages/pi-ai/dist/models.generated.js +35 -120
  188. package/packages/pi-ai/dist/models.generated.js.map +1 -1
  189. package/packages/pi-ai/package.json +1 -1
  190. package/packages/pi-coding-agent/package.json +7 -7
  191. package/packages/pi-tui/dist/components/input.js +1 -1
  192. package/packages/pi-tui/dist/components/input.js.map +1 -1
  193. package/packages/pi-tui/dist/keys.d.ts.map +1 -1
  194. package/packages/pi-tui/dist/keys.js +39 -30
  195. package/packages/pi-tui/dist/keys.js.map +1 -1
  196. package/packages/pi-tui/dist/stdin-buffer.d.ts.map +1 -1
  197. package/packages/pi-tui/dist/stdin-buffer.js +22 -0
  198. package/packages/pi-tui/dist/stdin-buffer.js.map +1 -1
  199. package/packages/pi-tui/package.json +2 -2
  200. package/packages/rpc-client/package.json +2 -2
  201. package/pkg/package.json +1 -1
  202. package/src/resources/extensions/ask-user-questions.ts +87 -24
  203. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +108 -281
  204. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +240 -0
  205. package/src/resources/extensions/claude-code-cli/turn-assembler.ts +287 -0
  206. package/src/resources/extensions/github-sync/templates.ts +3 -3
  207. package/src/resources/extensions/github-sync/tests/templates.test.ts +2 -2
  208. package/src/resources/extensions/gsd/artifact-projection.ts +31 -0
  209. package/src/resources/extensions/gsd/auto/contracts.ts +32 -2
  210. package/src/resources/extensions/gsd/auto/loop-deps.ts +2 -0
  211. package/src/resources/extensions/gsd/auto/loop.ts +83 -61
  212. package/src/resources/extensions/gsd/auto/orchestrator.ts +125 -12
  213. package/src/resources/extensions/gsd/auto/phases.ts +35 -3
  214. package/src/resources/extensions/gsd/auto/run-unit.ts +2 -1
  215. package/src/resources/extensions/gsd/auto/session.ts +4 -0
  216. package/src/resources/extensions/gsd/auto-dashboard.ts +18 -4
  217. package/src/resources/extensions/gsd/auto-dispatch.ts +20 -7
  218. package/src/resources/extensions/gsd/auto-model-selection.ts +8 -0
  219. package/src/resources/extensions/gsd/auto-post-unit.ts +4 -3
  220. package/src/resources/extensions/gsd/auto-prompts.ts +107 -9
  221. package/src/resources/extensions/gsd/auto-recovery.ts +50 -50
  222. package/src/resources/extensions/gsd/auto-runtime-state.ts +26 -0
  223. package/src/resources/extensions/gsd/auto-start.ts +17 -20
  224. package/src/resources/extensions/gsd/auto-timers.ts +16 -2
  225. package/src/resources/extensions/gsd/auto-tool-tracking.ts +35 -0
  226. package/src/resources/extensions/gsd/auto-unit-tool-scope.ts +9 -30
  227. package/src/resources/extensions/gsd/auto-verification.ts +7 -8
  228. package/src/resources/extensions/gsd/auto-worktree.ts +33 -26
  229. package/src/resources/extensions/gsd/auto.ts +15 -8
  230. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +29 -37
  231. package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +10 -37
  232. package/src/resources/extensions/gsd/bootstrap/query-tools.ts +2 -2
  233. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +116 -151
  234. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +107 -3
  235. package/src/resources/extensions/gsd/closeout-consistency-gate.ts +27 -5
  236. package/src/resources/extensions/gsd/codebase-generator.ts +9 -5
  237. package/src/resources/extensions/gsd/commands/handlers/auto.ts +3 -0
  238. package/src/resources/extensions/gsd/commands-handlers.ts +18 -0
  239. package/src/resources/extensions/gsd/commands-inspect.ts +7 -8
  240. package/src/resources/extensions/gsd/commands-maintenance.ts +74 -40
  241. package/src/resources/extensions/gsd/commands-ship.ts +2 -2
  242. package/src/resources/extensions/gsd/commands-verdict.ts +19 -2
  243. package/src/resources/extensions/gsd/db-workspace.ts +170 -0
  244. package/src/resources/extensions/gsd/delegation-policy.ts +3 -11
  245. package/src/resources/extensions/gsd/discussion-handoff.ts +276 -0
  246. package/src/resources/extensions/gsd/docs/preferences-reference.md +9 -0
  247. package/src/resources/extensions/gsd/doctor.ts +15 -5
  248. package/src/resources/extensions/gsd/error-classifier.ts +1 -1
  249. package/src/resources/extensions/gsd/git-conflict-state.ts +17 -1
  250. package/src/resources/extensions/gsd/gsd-db.ts +12 -0
  251. package/src/resources/extensions/gsd/guided-flow.ts +47 -558
  252. package/src/resources/extensions/gsd/guided-unit-completion.ts +275 -0
  253. package/src/resources/extensions/gsd/markdown-renderer.ts +2 -1
  254. package/src/resources/extensions/gsd/mcp-filter.ts +2 -1
  255. package/src/resources/extensions/gsd/mcp-tool-name.ts +35 -0
  256. package/src/resources/extensions/gsd/md-importer.ts +3 -3
  257. package/src/resources/extensions/gsd/migrate/safety.ts +2 -2
  258. package/src/resources/extensions/gsd/migration-auto-check.ts +2 -2
  259. package/src/resources/extensions/gsd/milestone-closeout-proof.ts +131 -0
  260. package/src/resources/extensions/gsd/milestone-closeout.ts +12 -4
  261. package/src/resources/extensions/gsd/milestone-merge-transaction.ts +47 -0
  262. package/src/resources/extensions/gsd/milestone-planning-persistence.ts +224 -0
  263. package/src/resources/extensions/gsd/milestone-readiness.ts +125 -0
  264. package/src/resources/extensions/gsd/milestone-settlement.ts +81 -0
  265. package/src/resources/extensions/gsd/milestone-validation-evidence.ts +95 -0
  266. package/src/resources/extensions/gsd/milestone-validation-verdict.ts +80 -0
  267. package/src/resources/extensions/gsd/parallel-eligibility.ts +4 -5
  268. package/src/resources/extensions/gsd/parallel-orchestrator.ts +6 -2
  269. package/src/resources/extensions/gsd/preferences-diagnostics.ts +98 -0
  270. package/src/resources/extensions/gsd/preferences-types.ts +16 -0
  271. package/src/resources/extensions/gsd/preferences.ts +173 -28
  272. package/src/resources/extensions/gsd/prompts/complete-slice.md +1 -0
  273. package/src/resources/extensions/gsd/prompts/execute-task.md +2 -0
  274. package/src/resources/extensions/gsd/prompts/guided-discuss-project.md +3 -1
  275. package/src/resources/extensions/gsd/prompts/plan-milestone.md +2 -0
  276. package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -0
  277. package/src/resources/extensions/gsd/prompts/refine-slice.md +1 -0
  278. package/src/resources/extensions/gsd/prompts/system.md +1 -1
  279. package/src/resources/extensions/gsd/provider-payload-policy.ts +140 -0
  280. package/src/resources/extensions/gsd/pull-request-process.ts +41 -0
  281. package/src/resources/extensions/gsd/quality-gate-closure.ts +140 -0
  282. package/src/resources/extensions/gsd/question-transport.ts +138 -0
  283. package/src/resources/extensions/gsd/roadmap-slices.ts +8 -2
  284. package/src/resources/extensions/gsd/slice-parallel-orchestrator.ts +6 -2
  285. package/src/resources/extensions/gsd/state.ts +15 -5
  286. package/src/resources/extensions/gsd/templates/plan.md +7 -0
  287. package/src/resources/extensions/gsd/templates/project.md +1 -0
  288. package/src/resources/extensions/gsd/templates/roadmap.md +1 -1
  289. package/src/resources/extensions/gsd/templates/uat.md +5 -1
  290. package/src/resources/extensions/gsd/tests/ask-user-questions-render.test.ts +92 -0
  291. package/src/resources/extensions/gsd/tests/auto-dashboard.test.ts +29 -1
  292. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +321 -5
  293. package/src/resources/extensions/gsd/tests/auto-milestone-target.test.ts +23 -0
  294. package/src/resources/extensions/gsd/tests/auto-model-selection-tool-poisoning.test.ts +18 -0
  295. package/src/resources/extensions/gsd/tests/auto-orchestrator.test.ts +133 -4
  296. package/src/resources/extensions/gsd/tests/auto-runtime-state.test.ts +34 -0
  297. package/src/resources/extensions/gsd/tests/canonical-milestone-root.test.ts +20 -0
  298. package/src/resources/extensions/gsd/tests/codebase-generator.test.ts +22 -0
  299. package/src/resources/extensions/gsd/tests/commands-dispatcher-workspace-git.test.ts +11 -0
  300. package/src/resources/extensions/gsd/tests/commands-verdict.test.ts +38 -1
  301. package/src/resources/extensions/gsd/tests/dispatch-complete-milestone-guard.test.ts +34 -3
  302. package/src/resources/extensions/gsd/tests/dispatch-run-uat-browser-tools.test.ts +88 -0
  303. package/src/resources/extensions/gsd/tests/doctor-scope-db-unavailable.test.ts +18 -0
  304. package/src/resources/extensions/gsd/tests/execute-task-rendering.test.ts +1 -0
  305. package/src/resources/extensions/gsd/tests/fixtures/pr-body/swarm-lane-no-blockers.md +1 -5
  306. package/src/resources/extensions/gsd/tests/fixtures/pr-body/swarm-lane-with-blockers.md +1 -5
  307. package/src/resources/extensions/gsd/tests/gate-state-canonicalization.test.ts +48 -1
  308. package/src/resources/extensions/gsd/tests/mcp-tool-name.test.ts +34 -0
  309. package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +58 -0
  310. package/src/resources/extensions/gsd/tests/milestone-closeout-proof.test.ts +99 -0
  311. package/src/resources/extensions/gsd/tests/milestone-closeout.test.ts +25 -0
  312. package/src/resources/extensions/gsd/tests/milestone-merge-transaction.test.ts +46 -0
  313. package/src/resources/extensions/gsd/tests/milestone-readiness.test.ts +65 -0
  314. package/src/resources/extensions/gsd/tests/milestone-validation-evidence.test.ts +41 -0
  315. package/src/resources/extensions/gsd/tests/milestone-validation-verdict.test.ts +55 -0
  316. package/src/resources/extensions/gsd/tests/plan-milestone.test.ts +45 -0
  317. package/src/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +2 -0
  318. package/src/resources/extensions/gsd/tests/planning-crossval.test.ts +45 -0
  319. package/src/resources/extensions/gsd/tests/preferences-diagnostics.test.ts +67 -0
  320. package/src/resources/extensions/gsd/tests/preferences.test.ts +183 -0
  321. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +46 -0
  322. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +9 -0
  323. package/src/resources/extensions/gsd/tests/provider-payload-policy.test.ts +165 -0
  324. package/src/resources/extensions/gsd/tests/pull-request-process.test.ts +47 -0
  325. package/src/resources/extensions/gsd/tests/register-hooks-depth-verification.test.ts +94 -0
  326. package/src/resources/extensions/gsd/tests/roadmap-parse-regression.test.ts +40 -0
  327. package/src/resources/extensions/gsd/tests/runtime-invariant-modules.test.ts +25 -1
  328. package/src/resources/extensions/gsd/tests/session-start-footer.test.ts +80 -0
  329. package/src/resources/extensions/gsd/tests/single-writer-invariant.test.ts +101 -1
  330. package/src/resources/extensions/gsd/tests/stale-queued-milestone.test.ts +27 -0
  331. package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +2 -1
  332. package/src/resources/extensions/gsd/tests/tool-availability-audit.test.ts +35 -0
  333. package/src/resources/extensions/gsd/tests/tool-naming.test.ts +35 -42
  334. package/src/resources/extensions/gsd/tests/uat-policy.test.ts +23 -0
  335. package/src/resources/extensions/gsd/tests/unit-context-composer.test.ts +47 -0
  336. package/src/resources/extensions/gsd/tests/user-input-boundary.test.ts +86 -1
  337. package/src/resources/extensions/gsd/tests/validate-milestone-stuck-guard.test.ts +39 -0
  338. package/src/resources/extensions/gsd/tests/web-app-uat.test.ts +150 -0
  339. package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +126 -9
  340. package/src/resources/extensions/gsd/tests/workspace-git-preflight.test.ts +15 -0
  341. package/src/resources/extensions/gsd/tests/worktree-manager.test.ts +21 -0
  342. package/src/resources/extensions/gsd/tests/worktree-projection-writers.test.ts +1 -1
  343. package/src/resources/extensions/gsd/tests/worktree-telemetry.test.ts +22 -0
  344. package/src/resources/extensions/gsd/tests/write-gate.test.ts +79 -0
  345. package/src/resources/extensions/gsd/tool-contract.ts +86 -8
  346. package/src/resources/extensions/gsd/tool-presentation-plan.ts +16 -33
  347. package/src/resources/extensions/gsd/tool-surface-snapshot.ts +47 -0
  348. package/src/resources/extensions/gsd/tools/plan-milestone.ts +19 -160
  349. package/src/resources/extensions/gsd/tools/reassess-roadmap.ts +43 -0
  350. package/src/resources/extensions/gsd/tools/validate-milestone.ts +25 -84
  351. package/src/resources/extensions/gsd/uat-policy.ts +19 -10
  352. package/src/resources/extensions/gsd/uat-run.ts +10 -14
  353. package/src/resources/extensions/gsd/unit-context-composer.ts +85 -20
  354. package/src/resources/extensions/gsd/unit-runtime.ts +3 -2
  355. package/src/resources/extensions/gsd/unit-tool-contracts.ts +2 -1
  356. package/src/resources/extensions/gsd/user-input-boundary.ts +18 -0
  357. package/src/resources/extensions/gsd/validation-block-guard.ts +2 -0
  358. package/src/resources/extensions/gsd/web-app-uat.ts +101 -0
  359. package/src/resources/extensions/gsd/workflow-mcp.ts +22 -110
  360. package/src/resources/extensions/gsd/workflow-reconcile.ts +3 -3
  361. package/src/resources/extensions/gsd/workflow-tool-surface.ts +73 -0
  362. package/src/resources/extensions/gsd/workspace-git-guard.ts +1 -0
  363. package/src/resources/extensions/gsd/worktree-lifecycle.ts +7 -16
  364. package/src/resources/extensions/gsd/worktree-state-projection.ts +55 -7
  365. package/src/resources/extensions/gsd/worktree-telemetry.ts +16 -0
  366. package/src/resources/extensions/shared/interview-ui.ts +15 -2
  367. package/src/resources/shared/claude-runtime-floor.ts +248 -0
  368. package/dist/web/standalone/.next/static/chunks/2659.feb6499ca863ebfc.js +0 -1
  369. package/dist/web/standalone/.next/static/chunks/2772.151789db0edea835.js +0 -1
  370. package/dist/web/standalone/.next/static/chunks/4283.10a065467b5340d8.js +0 -2
  371. package/dist/web/standalone/.next/static/chunks/5826.960dc4634cc9b0d3.js +0 -1
  372. package/dist/web/standalone/.next/static/chunks/796.46f811c0fac23aab.js +0 -10
  373. package/dist/web/standalone/.next/static/chunks/8785.d32f7a61f55c1600.js +0 -1
  374. /package/dist/web/standalone/.next/static/{Qbr81pQ-pbQXP4bq2VXLv → 3PtrU9qGPEXwNLWkIyiqk}/_buildManifest.js +0 -0
  375. /package/dist/web/standalone/.next/static/{Qbr81pQ-pbQXP4bq2VXLv → 3PtrU9qGPEXwNLWkIyiqk}/_ssgManifest.js +0 -0
@@ -4,6 +4,7 @@ import { readFileSync } from "node:fs";
4
4
  import { join } from "node:path";
5
5
  import {
6
6
  RUN_UAT_BROWSER_TOOL_NAMES,
7
+ resolveToolPresentationPlan,
7
8
  buildRunUatResultPresentation,
8
9
  buildRunUatPresentationForType,
9
10
  RUN_UAT_READ_ONLY_TOOL_NAMES,
@@ -179,6 +180,33 @@ test("browser-executable UAT presentation uses direct browser tools", () => {
179
180
  assert.ok(!presentation.presentedTools.some((toolName) => toolName.startsWith("mcp__gsd-browser__")));
180
181
  });
181
182
 
183
+ test("run-uat presentation plans carry typed tool-surface snapshots", () => {
184
+ const plan = resolveToolPresentationPlan({
185
+ phase: "run-uat",
186
+ surface: "mcp",
187
+ workflowMcpServerName: "gsd-workflow",
188
+ availableToolNames: [
189
+ "gsd_uat_exec",
190
+ "gsd_uat_result_save",
191
+ "read",
192
+ ],
193
+ includeBrowserTools: [],
194
+ });
195
+
196
+ assert.equal(plan.toolSurface.source, "presentation-plan");
197
+ assert.equal(plan.toolSurface.phase, "run-uat");
198
+ assert.deepEqual(plan.toolSurface.scopedToolNames, [
199
+ "gsd_uat_exec",
200
+ "gsd_uat_result_save",
201
+ "read",
202
+ ]);
203
+ assert.deepEqual(plan.toolSurface.presentedToolNames, [
204
+ "mcp__gsd-workflow__gsd_uat_exec",
205
+ "mcp__gsd-workflow__gsd_uat_result_save",
206
+ "read",
207
+ ]);
208
+ });
209
+
182
210
  test("live-runtime and mixed UAT presentations also surface browser tools", () => {
183
211
  // Regression (M001/S03): the run-uat prompt tells live-runtime and mixed to
184
212
  // drive a browser, so the runner must actually receive the browser tools and
@@ -517,6 +545,24 @@ test("plan-slice prompt clarifies gsd_plan_slice handles task persistence", () =
517
545
  assert.match(prompt, /gsd_plan_slice` handles task persistence/i);
518
546
  });
519
547
 
548
+ test("plan-slice prompt references web app UAT guidance when planning browser work", () => {
549
+ const prompt = readPrompt("plan-slice");
550
+ assert.match(prompt, /Web App UAT guidance/i);
551
+ assert.match(prompt, /Playwright smoke scaffolding/i);
552
+ });
553
+
554
+ test("complete-slice prompt references web app UAT mode rules", () => {
555
+ const prompt = readPrompt("complete-slice");
556
+ assert.match(prompt, /Web App UAT guidance/i);
557
+ assert.match(prompt, /browser-executable|runtime-executable/i);
558
+ });
559
+
560
+ test("plan-milestone prompt seeds web verification strategy", () => {
561
+ const prompt = readPrompt("plan-milestone");
562
+ assert.match(prompt, /Web apps/i);
563
+ assert.match(prompt, /Playwright|browser_\*/i);
564
+ });
565
+
520
566
  test("replan-slice prompt uses gsd_replan_slice as canonical DB-backed tool", () => {
521
567
  const prompt = readPrompt("replan-slice");
522
568
  assert.match(prompt, /gsd_replan_slice/);
@@ -740,6 +740,15 @@ test("MAX_TRANSIENT_AUTO_RESUMES is at least 8 for sustained overload resilience
740
740
  );
741
741
  });
742
742
 
743
+ // ── OpenAI-completions mid-stream cut (#577) ─────────────────────────────────
744
+
745
+ test("classifyError: 'Stream ended without finish_reason' is transient network (#577)", () => {
746
+ const result = classifyError("Stream ended without finish_reason");
747
+ assert.ok(isTransient(result), "Stream ended without finish_reason must be transient");
748
+ assert.equal(result.kind, "network");
749
+ assert.ok("retryAfterMs" in result && result.retryAfterMs > 0);
750
+ });
751
+
743
752
  // ── Stream idle timeout / partial response (#4558) ──────────────────────────
744
753
 
745
754
  test("classifyError: 'Stream idle timeout - partial response received' is transient network", () => {
@@ -0,0 +1,165 @@
1
+ import test from "node:test";
2
+ import assert from "node:assert/strict";
3
+
4
+ import type { ContextManagementConfig } from "../preferences-types.js";
5
+ import type { ProviderPayloadPolicyDeps } from "../provider-payload-policy.js";
6
+
7
+ import { applyProviderPayloadPolicy } from "../provider-payload-policy.js";
8
+
9
+ function createDeps(
10
+ overrides: Partial<ProviderPayloadPolicyDeps> & {
11
+ context?: ContextManagementConfig | undefined;
12
+ autoActive?: boolean;
13
+ sourceContextBlock?: string | null;
14
+ } = {},
15
+ ): ProviderPayloadPolicyDeps {
16
+ return {
17
+ isAutoActive: () => overrides.autoActive ?? false,
18
+ loadContextManagementConfig: () => overrides.context,
19
+ renderSourceContextBlock: () => overrides.sourceContextBlock ?? null,
20
+ getEffectiveServiceTier: () => undefined,
21
+ supportsServiceTier: () => false,
22
+ ...overrides,
23
+ };
24
+ }
25
+
26
+ function textFromMessage(message: { content?: unknown }): string {
27
+ const content = message.content;
28
+ if (typeof content === "string") return content;
29
+ if (!Array.isArray(content)) return "";
30
+ const text = content.find((block): block is { text: string } => {
31
+ return Boolean(block && typeof block === "object" && "text" in block && typeof block.text === "string");
32
+ });
33
+ return text?.text ?? "";
34
+ }
35
+
36
+ test("provider payload policy truncates tool results outside auto-mode without masking", () => {
37
+ const messageText = "m".repeat(50);
38
+ const responsesOutput = "r".repeat(50);
39
+ const payload = {
40
+ messages: [
41
+ { role: "user", content: [{ type: "text", text: "keep me" }] },
42
+ { role: "toolResult", content: [{ type: "text", text: messageText }] },
43
+ ],
44
+ input: [
45
+ { role: "user", content: [{ type: "input_text", text: "keep me" }] },
46
+ { type: "function_call_output", call_id: "call_test", output: responsesOutput },
47
+ ],
48
+ };
49
+
50
+ applyProviderPayloadPolicy({
51
+ payload,
52
+ deps: createDeps({ context: { observation_mask_turns: 1, tool_result_max_chars: 10 } }),
53
+ });
54
+
55
+ const truncatedMessage = textFromMessage(payload.messages[1]);
56
+ const truncatedResponsesOutput = String(payload.input[1]?.output ?? "");
57
+ assert.match(truncatedMessage, /\[truncated\]/);
58
+ assert.match(truncatedResponsesOutput, /\[truncated\]/);
59
+ assert.doesNotMatch(truncatedMessage, /result masked/);
60
+ assert.doesNotMatch(truncatedResponsesOutput, /result masked/);
61
+ });
62
+
63
+ test("provider payload policy appends source context after masking and truncation", () => {
64
+ const sourceContextBlock = "## Source Context Block\n\n" + "full source text ".repeat(20);
65
+ const payload = {
66
+ messages: [
67
+ { role: "user", content: [{ type: "text", text: "old turn" }] },
68
+ { role: "toolResult", content: [{ type: "text", text: "old result ".repeat(20) }] },
69
+ { role: "assistant", content: [{ type: "text", text: "ok" }] },
70
+ { role: "user", content: [{ type: "text", text: "new turn" }] },
71
+ { role: "toolResult", content: [{ type: "text", text: "new result ".repeat(20) }] },
72
+ ],
73
+ };
74
+
75
+ applyProviderPayloadPolicy({
76
+ payload,
77
+ deps: createDeps({
78
+ autoActive: true,
79
+ context: { observation_mask_turns: 1, tool_result_max_chars: 80 },
80
+ sourceContextBlock,
81
+ }),
82
+ });
83
+
84
+ const oldResult = textFromMessage(payload.messages[1]);
85
+ const newResult = textFromMessage(payload.messages[4]);
86
+ const appendedContext = textFromMessage(payload.messages[payload.messages.length - 1]);
87
+
88
+ assert.match(oldResult, /result masked/);
89
+ assert.match(newResult, /\[truncated\]/);
90
+ assert.equal(appendedContext, sourceContextBlock);
91
+ assert.doesNotMatch(appendedContext, /\[truncated\]/);
92
+ });
93
+
94
+ test("provider payload policy applies ordering to Responses input payloads", () => {
95
+ const sourceContextBlock = "## Source Context Block\n\n" + "responses source text ".repeat(20);
96
+ const payload = {
97
+ input: [
98
+ { role: "user", content: [{ type: "input_text", text: "old turn" }] },
99
+ { type: "function_call_output", call_id: "call_old", output: "old result ".repeat(20) },
100
+ { type: "message", role: "assistant", content: [{ type: "output_text", text: "ok" }] },
101
+ { role: "user", content: [{ type: "input_text", text: "new turn" }] },
102
+ { type: "function_call_output", call_id: "call_new", output: "new result ".repeat(20) },
103
+ ],
104
+ };
105
+
106
+ applyProviderPayloadPolicy({
107
+ payload,
108
+ deps: createDeps({
109
+ autoActive: true,
110
+ context: { observation_mask_turns: 1, tool_result_max_chars: 80 },
111
+ sourceContextBlock,
112
+ }),
113
+ });
114
+
115
+ assert.match(String(payload.input[1]?.output ?? ""), /result masked/);
116
+ assert.match(String(payload.input[4]?.output ?? ""), /\[truncated\]/);
117
+ assert.equal(textFromMessage(payload.input[payload.input.length - 1]), sourceContextBlock);
118
+ });
119
+
120
+ test("provider payload policy replaces existing source context blocks", () => {
121
+ const payload = {
122
+ messages: [
123
+ { role: "user", content: [{ type: "text", text: "keep me" }] },
124
+ { role: "user", content: [{ type: "text", text: "## Source Context Block\n\nstale" }] },
125
+ ],
126
+ };
127
+
128
+ applyProviderPayloadPolicy({
129
+ payload,
130
+ deps: createDeps({
131
+ autoActive: true,
132
+ sourceContextBlock: "## Source Context Block\n\nfresh",
133
+ }),
134
+ });
135
+
136
+ const sourceMessages = payload.messages.filter((message) => {
137
+ return textFromMessage(message).startsWith("## Source Context Block");
138
+ });
139
+ assert.equal(sourceMessages.length, 1);
140
+ assert.equal(textFromMessage(sourceMessages[0]), "## Source Context Block\n\nfresh");
141
+ });
142
+
143
+ test("provider payload policy sets service tier only for supported models", () => {
144
+ const unsupported = {};
145
+ applyProviderPayloadPolicy({
146
+ payload: unsupported,
147
+ modelId: "claude-opus-4-6",
148
+ deps: createDeps({
149
+ getEffectiveServiceTier: () => "priority",
150
+ supportsServiceTier: (modelId) => modelId === "gpt-5.4",
151
+ }),
152
+ });
153
+ assert.equal("service_tier" in unsupported, false);
154
+
155
+ const supported: Record<string, unknown> = {};
156
+ applyProviderPayloadPolicy({
157
+ payload: supported,
158
+ modelId: "gpt-5.4",
159
+ deps: createDeps({
160
+ getEffectiveServiceTier: () => "priority",
161
+ supportsServiceTier: (modelId) => modelId === "gpt-5.4",
162
+ }),
163
+ });
164
+ assert.equal(supported.service_tier, "priority");
165
+ });
@@ -0,0 +1,47 @@
1
+ // Project/App: gsd-pi
2
+ // File Purpose: Tests for process-level pull request policy.
3
+
4
+ import test from "node:test";
5
+ import assert from "node:assert/strict";
6
+
7
+ import {
8
+ buildPullRequestEvidence,
9
+ createDraftPullRequestFromEvidence,
10
+ } from "../pull-request-process.js";
11
+
12
+ test("buildPullRequestEvidence omits AI credit by policy", () => {
13
+ const evidence = buildPullRequestEvidence({
14
+ milestoneId: "M001",
15
+ milestoneTitle: "Git process",
16
+ aiAssisted: true,
17
+ });
18
+
19
+ assert.equal(evidence.title, "feat: Git process");
20
+ assert.ok(!evidence.body.includes("## AI Assistance Disclosure"));
21
+ assert.ok(!evidence.body.includes("AI assistance"));
22
+ });
23
+
24
+ test("createDraftPullRequestFromEvidence forwards exact evidence and branch options", () => {
25
+ const calls: unknown[][] = [];
26
+ const url = createDraftPullRequestFromEvidence(
27
+ "/repo",
28
+ "M001",
29
+ { title: "feat: Git process", body: "body" },
30
+ { head: "milestone/M001", base: "main" },
31
+ {
32
+ createDraftPR: (basePath, milestoneId, title, body, opts) => {
33
+ calls.push([basePath, milestoneId, title, body, opts]);
34
+ return "https://github.example/pr/1";
35
+ },
36
+ },
37
+ );
38
+
39
+ assert.equal(url, "https://github.example/pr/1");
40
+ assert.deepEqual(calls, [[
41
+ "/repo",
42
+ "M001",
43
+ "feat: Git process",
44
+ "body",
45
+ { head: "milestone/M001", base: "main" },
46
+ ]]);
47
+ });
@@ -18,6 +18,11 @@ import {
18
18
  } from "../bootstrap/write-gate.ts";
19
19
  import { classifyCommand } from "../safety/destructive-guard.ts";
20
20
  import { toRoundResultResponse } from "../../remote-questions/manager.ts";
21
+ import {
22
+ markInteractiveElicitationStart,
23
+ markInteractiveElicitationEnd,
24
+ clearInFlightTools,
25
+ } from "../auto-tool-tracking.ts";
21
26
 
22
27
  function makeTempDir(prefix: string): string {
23
28
  const dir = join(
@@ -708,3 +713,92 @@ test("register-hooks gates MCP ask_user_questions cancellation before requiremen
708
713
  assert.equal(requirementBlock?.block, true, "requirement save must be blocked while gate is pending");
709
714
  assert.match(requirementBlock?.reason ?? "", /has not been confirmed/);
710
715
  });
716
+
717
+ // ─── Foreground self-cancel regression (#cc-elicitation-self-cancel) ───
718
+ // Product-visible symptom: under claude-code-cli + gsd-MCP, ask_user_questions
719
+ // is routed as an SDK elicitation (the human boundary). The message_update hook
720
+ // would arm the approval-gate pause and emit the "waiting for your approval -
721
+ // pausing" notice, tearing down that elicitation and looping a re-ask. The fix
722
+ // makes message_update bail while an interactive elicitation is in flight, while
723
+ // still pausing for prose-only approvals (native-TUI provider, where the marker
724
+ // is always false). This drives the real registered hook end-to-end.
725
+ test("register-hooks message_update does NOT pause while an interactive elicitation is the human boundary, but still pauses otherwise", async (t) => {
726
+ const dir = makeTempDir("elicitation-pause-guard");
727
+ const originalCwd = process.cwd();
728
+ process.chdir(dir);
729
+ resetWriteGateState(dir);
730
+ clearPendingAutoStart(dir);
731
+ clearInFlightTools();
732
+
733
+ t.after(() => {
734
+ try {
735
+ resetWriteGateState(dir);
736
+ clearPendingAutoStart(dir);
737
+ clearInFlightTools();
738
+ } finally {
739
+ process.chdir(originalCwd);
740
+ rmSync(dir, { recursive: true, force: true });
741
+ }
742
+ });
743
+
744
+ const handlers = new Map<string, Array<(event: any, ctx?: any) => Promise<any> | any>>();
745
+ const pi = {
746
+ on(event: string, handler: (event: any, ctx?: any) => Promise<any> | any) {
747
+ const existing = handlers.get(event) ?? [];
748
+ existing.push(handler);
749
+ handlers.set(event, existing);
750
+ },
751
+ } as any;
752
+
753
+ const notices: Array<{ text: string; level: string }> = [];
754
+ const ctx = {
755
+ cwd: dir,
756
+ ui: { notify: (text: string, level: string) => notices.push({ text, level }) },
757
+ } as any;
758
+
759
+ registerHooks(pi, []);
760
+
761
+ // A discuss-milestone is the active unit, so the approval text would normally
762
+ // arm the pause/notice path.
763
+ setPendingAutoStart(dir, {
764
+ basePath: dir,
765
+ milestoneId: "M001",
766
+ ctx,
767
+ pi: { sendMessage: () => undefined } as any,
768
+ });
769
+
770
+ // The model's plain-text approval question — identical in both phases.
771
+ const approvalMessage = {
772
+ role: "assistant",
773
+ content: [
774
+ { type: "text", text: "Here is the milestone plan.\n\nDid I capture the project correctly?" },
775
+ ],
776
+ };
777
+
778
+ const fireMessageUpdate = async () => {
779
+ for (const handler of handlers.get("message_update") ?? []) {
780
+ await handler({ message: approvalMessage }, ctx);
781
+ }
782
+ };
783
+
784
+ // Phase 1 — FIX: an interactive elicitation is in flight (claude-code-cli
785
+ // foreground). The pause/notice MUST be suppressed.
786
+ markInteractiveElicitationStart();
787
+ await fireMessageUpdate();
788
+ assert.equal(
789
+ notices.some((n) => /waiting for your approval - pausing/.test(n.text)),
790
+ false,
791
+ "must NOT emit the approval-pause notice while the elicitation is the human boundary",
792
+ );
793
+ markInteractiveElicitationEnd();
794
+
795
+ // Phase 2 — control: no elicitation in flight (native-TUI provider or a
796
+ // prose-only approval). The same message MUST still pause.
797
+ notices.length = 0;
798
+ await fireMessageUpdate();
799
+ assert.equal(
800
+ notices.some((n) => /discuss-milestone M001 is waiting for your approval - pausing/.test(n.text)),
801
+ true,
802
+ "prose-only approval with no elicitation in flight must still arm the pause notice",
803
+ );
804
+ });
@@ -396,4 +396,44 @@ test('T. #1736: Checkbox format unchanged', () => {
396
396
  assert.deepStrictEqual(slices[1].done, false, '#1736 checkbox compat: S02 not done');
397
397
  });
398
398
 
399
+ // ═══════════════════════════════════════════════════════════════════════
400
+ // U. Regression #566: double-bracket depends serialization recovery
401
+ // ═══════════════════════════════════════════════════════════════════════
402
+ test('U. #566: double-bracket depends:[[S01]] recovers to ["S01"]', () => {
403
+ // Simulates a roadmap written by markdown-renderer when the DB had
404
+ // depends=["[S01]"] — produces `depends:[[S01]]` on disk.
405
+ const content = [
406
+ '# M020: Corrupted Depends',
407
+ '',
408
+ '## Slices',
409
+ '',
410
+ '- [ ] **S01: Foundation** `risk:low` `depends:[]`',
411
+ '- [ ] **S02: Build** `risk:medium` `depends:[[S01]]`',
412
+ '- [ ] **S03: Ship** `risk:high` `depends:[[S01, S02]]`',
413
+ '',
414
+ ].join('\n');
415
+
416
+ const slices = parseRoadmapSlices(content);
417
+ assert.deepStrictEqual(slices.length, 3, '#566 double-bracket: 3 slices');
418
+ assert.deepStrictEqual(slices[1].depends, ['S01'], '#566: S02 depends recovered to ["S01"]');
419
+ assert.deepStrictEqual(slices[2].depends, ['S01', 'S02'], '#566: S03 depends recovered to ["S01","S02"]');
420
+ });
421
+
422
+ test('U. #566: bracket-wrapped single element depends:[[S01]] recovers to ["S01"]', () => {
423
+ // Simulates the exact DB corruption seen in the forensic evidence: depends=["[S01]"]
424
+ const content = [
425
+ '# M020: Single Bracket Corruption',
426
+ '',
427
+ '## Slices',
428
+ '',
429
+ '- [ ] **S01: First** `risk:low` `depends:[]`',
430
+ '- [ ] **S02: Second** `risk:medium` `depends:[[S01]]`',
431
+ '',
432
+ ].join('\n');
433
+
434
+ const slices = parseRoadmapSlices(content);
435
+ assert.deepStrictEqual(slices[1].depends.length, 1, '#566 single: S02 has exactly 1 dep');
436
+ assert.deepStrictEqual(slices[1].depends[0], 'S01', '#566 single: dep is "S01" not "[S01]"');
437
+ });
438
+
399
439
  });
@@ -6,7 +6,7 @@ import assert from "node:assert/strict";
6
6
 
7
7
  import { classifyFailure } from "../recovery-classification.js";
8
8
  import { reconcileBeforeDispatch } from "../state-reconciliation.js";
9
- import { compileUnitToolContract } from "../tool-contract.js";
9
+ import { compileUnitContextContract, compileUnitToolContract } from "../tool-contract.js";
10
10
  import { shouldBlockAutoUnitToolCall } from "../auto-unit-tool-scope.js";
11
11
  import type { GSDState } from "../types.js";
12
12
 
@@ -68,6 +68,16 @@ test("Tool Contract compiles known Unit prompt and tool policy", () => {
68
68
  assert.equal(result.ok && result.contract.toolsPolicy.mode, "all");
69
69
  assert.ok(result.ok && result.contract.validationRules.includes("closeout-tool-present"));
70
70
  assert.ok(result.ok && result.contract.validationRules.includes("source-observation-contract-present"));
71
+ assert.deepEqual(result.ok && result.contract.promptContext.artifacts.inline, [
72
+ "task-plan",
73
+ "slice-plan",
74
+ "prior-task-summaries",
75
+ "templates",
76
+ ]);
77
+ assert.deepEqual(result.ok && result.contract.promptContext.artifacts.onDemand, ["slice-research"]);
78
+ assert.ok(result.ok && result.contract.promptObligations.includes("context-inline:task-plan,slice-plan,prior-task-summaries,templates"));
79
+ assert.ok(result.ok && result.contract.promptObligations.includes("context-on-demand:slice-research"));
80
+ assert.ok(result.ok && result.contract.promptObligations.includes("source-observations:whole-file-active-unit"));
71
81
  assert.deepEqual(result.ok && result.contract.sourceObservations, {
72
82
  mode: "whole-file-active-unit",
73
83
  seedFields: ["task.files", "task.inputs"],
@@ -77,6 +87,20 @@ test("Tool Contract compiles known Unit prompt and tool policy", () => {
77
87
  });
78
88
  });
79
89
 
90
+ test("Unit Context Contract exposes prompt context without workflow tool surface", () => {
91
+ const result = compileUnitContextContract("execute-task");
92
+
93
+ assert.equal(result.ok, true);
94
+ assert.equal(result.ok && result.contract.unitType, "execute-task");
95
+ assert.equal(result.ok && result.contract.contextMode, "execution");
96
+ assert.equal(result.ok && result.contract.toolsPolicy.mode, "all");
97
+ assert.deepEqual(result.ok && result.contract.artifacts.excerpt, []);
98
+ assert.deepEqual(result.ok && result.contract.artifacts.computed, []);
99
+ assert.deepEqual(result.ok && result.contract.artifacts.prepend, []);
100
+ assert.equal(result.ok && result.contract.maxSystemPromptChars, 1_500_000);
101
+ assert.equal(result.ok && result.contract.sourceObservations.mode, "whole-file-active-unit");
102
+ });
103
+
80
104
  test("Tool Contract records high-risk cross-phase tool boundaries without single-owning every tool", () => {
81
105
  const completeSlice = compileUnitToolContract("complete-slice");
82
106
  const runUat = compileUnitToolContract("run-uat");
@@ -24,6 +24,7 @@ import { fileURLToPath } from "node:url";
24
24
 
25
25
  import { autoSession } from "../auto-runtime-state.ts";
26
26
  import { registerHooks } from "../bootstrap/register-hooks.ts";
27
+ import { _resetPreferenceDiagnosticNotificationsForTests } from "../preferences-diagnostics.ts";
27
28
 
28
29
  const __dirname = dirname(fileURLToPath(import.meta.url));
29
30
  const HOOKS_SOURCE = readFileSync(
@@ -485,3 +486,82 @@ test("session_start and session_switch apply disabled model provider policy from
485
486
  "session_switch should re-read preferences for the switched project/session context",
486
487
  );
487
488
  });
489
+
490
+ test("session_start surfaces malformed preference diagnostics through visible notifications", async (t) => {
491
+ const dir = join(
492
+ tmpdir(),
493
+ `gsd-session-pref-diagnostics-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
494
+ );
495
+ mkdirSync(join(dir, ".gsd"), { recursive: true });
496
+ const tempGsdHome = join(dir, "home");
497
+ mkdirSync(tempGsdHome, { recursive: true });
498
+
499
+ const originalCwd = process.cwd();
500
+ const originalGsdHome = process.env.GSD_HOME;
501
+ process.env.GSD_HOME = tempGsdHome;
502
+ process.chdir(dir);
503
+ _resetPreferenceDiagnosticNotificationsForTests();
504
+ t.after(() => {
505
+ process.chdir(originalCwd);
506
+ if (originalGsdHome === undefined) delete process.env.GSD_HOME;
507
+ else process.env.GSD_HOME = originalGsdHome;
508
+ _resetPreferenceDiagnosticNotificationsForTests();
509
+ try { rmSync(dir, { recursive: true, force: true }); } catch { /* best-effort */ }
510
+ });
511
+
512
+ writeFileSync(
513
+ join(dir, ".gsd", "PREFERENCES.md"),
514
+ [
515
+ "---",
516
+ "version: 1",
517
+ "models:",
518
+ " validation:",
519
+ " openrouter/deepseek/deepseek-v4-pro",
520
+ " thinking: high",
521
+ "---",
522
+ "",
523
+ ].join("\n"),
524
+ "utf-8",
525
+ );
526
+
527
+ const notifications: Array<{ message: string; level?: string }> = [];
528
+ const handlers = new Map<string, (event: unknown, ctx: any) => Promise<void> | void>();
529
+ const pi = {
530
+ on(event: string, handler: (event: unknown, ctx: any) => Promise<void> | void) {
531
+ handlers.set(event, handler);
532
+ },
533
+ } as any;
534
+ const ctx = {
535
+ hasUI: true,
536
+ ui: {
537
+ notify: (message: string, level?: string) => notifications.push({ message, level }),
538
+ setStatus: () => {},
539
+ setFooter: () => {},
540
+ setWorkingMessage: () => {},
541
+ onTerminalInput: () => () => {},
542
+ setWidget: () => {},
543
+ },
544
+ sessionManager: { getSessionId: () => null },
545
+ model: null,
546
+ setCompactionThresholdOverride: () => {},
547
+ modelRegistry: {
548
+ setDisabledModelProviders: () => {},
549
+ getProviderAuthMode: () => undefined,
550
+ isProviderRequestReady: () => false,
551
+ },
552
+ };
553
+
554
+ registerHooks(pi, []);
555
+
556
+ const sessionStart = handlers.get("session_start");
557
+ assert.ok(sessionStart, "session_start handler must be registered");
558
+ await sessionStart!({}, ctx);
559
+
560
+ const diagnostic = notifications.find((notification) =>
561
+ notification.message.includes("GSD project preferences error"),
562
+ );
563
+ assert.equal(diagnostic?.level, "error");
564
+ assert.match(diagnostic?.message ?? "", /could not be parsed/);
565
+ assert.match(diagnostic?.message ?? "", /line 5, column 5/);
566
+ assert.match(diagnostic?.message ?? "", /Preferences from this file were ignored/);
567
+ });