@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
@@ -55,6 +55,31 @@ test("isMilestoneCloseoutSettled requires DB closed and summary artifact", async
55
55
  assert.equal(settled, true);
56
56
  });
57
57
 
58
+ test("isMilestoneCloseoutSettled accepts summary artifacts in a live milestone worktree", async () => {
59
+ const base = mkdtempSync(join(tmpdir(), "gsd-milestone-closeout-worktree-"));
60
+ tmpDirs.push(base);
61
+ mkdirSync(join(base, ".gsd"), { recursive: true });
62
+ openDatabase(join(base, ".gsd", "gsd.db"));
63
+ insertMilestone({ id: "M001", title: "Done", status: "complete" });
64
+ insertSlice({ id: "S01", milestoneId: "M001", title: "Done Slice", status: "complete" });
65
+ insertAssessment({
66
+ path: "milestones/M001/M001-VALIDATION.md",
67
+ milestoneId: "M001",
68
+ status: "pass",
69
+ scope: "milestone-validation",
70
+ fullContent: "verdict: pass",
71
+ });
72
+
73
+ const worktreeRoot = join(base, ".gsd", "worktrees", "M001");
74
+ const milestoneDir = join(worktreeRoot, ".gsd", "milestones", "M001");
75
+ mkdirSync(milestoneDir, { recursive: true });
76
+ writeFileSync(join(worktreeRoot, ".git"), `gitdir: ${join(base, ".git", "worktrees", "M001")}\n`);
77
+ writeFileSync(join(milestoneDir, "M001-SUMMARY.md"), "# Milestone Summary\n");
78
+
79
+ const settled = await isMilestoneCloseoutSettled("M001", base);
80
+ assert.equal(settled, true);
81
+ });
82
+
58
83
  test("isMilestoneCloseoutSettled returns false when summary artifact is missing", async () => {
59
84
  const base = mkdtempSync(join(tmpdir(), "gsd-milestone-closeout-missing-"));
60
85
  tmpDirs.push(base);
@@ -0,0 +1,46 @@
1
+ // Project/App: gsd-pi
2
+ // File Purpose: Tests for the milestone merge transaction wrapper.
3
+
4
+ import test from "node:test";
5
+ import assert from "node:assert/strict";
6
+
7
+ import {
8
+ createMilestoneMergeTransaction,
9
+ runMilestoneMergeTransaction,
10
+ } from "../milestone-merge-transaction.js";
11
+
12
+ test("runMilestoneMergeTransaction delegates to the supplied merge runner", () => {
13
+ const calls: unknown[][] = [];
14
+ const result = runMilestoneMergeTransaction(
15
+ {
16
+ mergeMilestoneToMain: (basePath, milestoneId, roadmapContent) => {
17
+ calls.push([basePath, milestoneId, roadmapContent]);
18
+ return { pushed: true, codeFilesChanged: true, commitMessage: "merge M001" };
19
+ },
20
+ },
21
+ {
22
+ basePath: "/repo",
23
+ milestoneId: "M001",
24
+ roadmapContent: "# M001",
25
+ },
26
+ );
27
+
28
+ assert.deepEqual(calls, [["/repo", "M001", "# M001"]]);
29
+ assert.deepEqual(result, {
30
+ pushed: true,
31
+ codeFilesChanged: true,
32
+ commitMessage: "merge M001",
33
+ });
34
+ });
35
+
36
+ test("createMilestoneMergeTransaction returns a lifecycle-compatible runner", () => {
37
+ const runner = createMilestoneMergeTransaction(() => ({
38
+ pushed: false,
39
+ codeFilesChanged: false,
40
+ }));
41
+
42
+ assert.deepEqual(runner("/repo", "M002", "# M002"), {
43
+ pushed: false,
44
+ codeFilesChanged: false,
45
+ });
46
+ });
@@ -0,0 +1,65 @@
1
+ import test from "node:test";
2
+ import assert from "node:assert/strict";
3
+
4
+ import {
5
+ classifyMilestoneReadiness,
6
+ describeMilestoneReadinessPhase,
7
+ formatAcceptedDiscussHandoffMessage,
8
+ readinessNeedsDiscussion,
9
+ } from "../milestone-readiness.ts";
10
+
11
+ test("classifyMilestoneReadiness distinguishes queued shells from draft discussion work", () => {
12
+ const shell = classifyMilestoneReadiness({
13
+ status: "queued",
14
+ hasDraftContext: true,
15
+ sliceCount: 0,
16
+ });
17
+
18
+ assert.equal(shell.kind, "queued-shell");
19
+ assert.equal(readinessNeedsDiscussion(shell), true);
20
+ });
21
+
22
+ test("classifyMilestoneReadiness marks context-only milestones as planning pending", () => {
23
+ const readiness = classifyMilestoneReadiness({
24
+ status: "queued",
25
+ hasContext: true,
26
+ sliceCount: 0,
27
+ });
28
+
29
+ assert.equal(readiness.kind, "planning-pending");
30
+ assert.equal(formatAcceptedDiscussHandoffMessage("M001", readiness), "Milestone M001 context captured. Continuing the planning pipeline.");
31
+ });
32
+
33
+ test("classifyMilestoneReadiness marks persisted slices as executable plan", () => {
34
+ const readiness = classifyMilestoneReadiness({
35
+ status: "active",
36
+ hasContext: true,
37
+ sliceCount: 1,
38
+ });
39
+
40
+ assert.equal(readiness.kind, "executable-plan");
41
+ assert.equal(formatAcceptedDiscussHandoffMessage("M001", readiness), "Milestone M001 ready.");
42
+ });
43
+
44
+ test("formatAcceptedDiscussHandoffMessage preserves ready copy for any executable plan", () => {
45
+ const readiness = classifyMilestoneReadiness({
46
+ status: "complete",
47
+ hasContext: true,
48
+ sliceCount: 1,
49
+ });
50
+
51
+ assert.equal(readiness.kind, "terminal");
52
+ assert.equal(formatAcceptedDiscussHandoffMessage("M001", readiness), "Milestone M001 ready.");
53
+ });
54
+
55
+ test("describeMilestoneReadinessPhase keeps dashboard phase copy centralized", () => {
56
+ assert.deepEqual(describeMilestoneReadinessPhase("needs-discussion"), {
57
+ label: "Discuss milestone draft",
58
+ description: "Milestone has a draft context — needs discussion before planning.",
59
+ });
60
+ assert.deepEqual(describeMilestoneReadinessPhase("pre-planning"), {
61
+ label: "Research & plan milestone",
62
+ description: "Scout the landscape and create the roadmap.",
63
+ });
64
+ assert.equal(describeMilestoneReadinessPhase("executing"), null);
65
+ });
@@ -0,0 +1,41 @@
1
+ // Project/App: gsd-pi
2
+ // File Purpose: Tests for forwarded milestone validation evidence helpers.
3
+
4
+ import test from "node:test";
5
+ import assert from "node:assert/strict";
6
+
7
+ import {
8
+ applyBrowserEvidenceGate,
9
+ hasRuntimeExecutableUatEvidenceText,
10
+ } from "../milestone-validation-evidence.js";
11
+
12
+ test("hasRuntimeExecutableUatEvidenceText requires runtime-executable PASS evidence", () => {
13
+ const evidence = [
14
+ "---",
15
+ "uatType: runtime-executable",
16
+ "verdict: PASS",
17
+ "---",
18
+ "| Check | Mode | Result | Evidence |",
19
+ "| DOM | runtime | PASS | gsd_uat_exec:.gsd/evidence/uat/M001/S01/dom.json |",
20
+ ].join("\n");
21
+
22
+ assert.equal(hasRuntimeExecutableUatEvidenceText(evidence), true);
23
+ assert.equal(hasRuntimeExecutableUatEvidenceText(evidence.replace("runtime | PASS", "manual | PASS")), false);
24
+ });
25
+
26
+ test("applyBrowserEvidenceGate records downgrade rationale", () => {
27
+ const params = applyBrowserEvidenceGate({
28
+ milestoneId: "M001",
29
+ verdict: "pass",
30
+ remediationRound: 0,
31
+ successCriteriaChecklist: "",
32
+ sliceDeliveryAudit: "",
33
+ crossSliceIntegration: "",
34
+ requirementCoverage: "",
35
+ verdictRationale: "Initial rationale.",
36
+ });
37
+
38
+ assert.equal(params.verdict, "needs-attention");
39
+ assert.match(params.verdictRationale, /Initial rationale/);
40
+ assert.match(params.verdictRationale, /Browser evidence gate/);
41
+ });
@@ -0,0 +1,55 @@
1
+ import test from "node:test";
2
+ import assert from "node:assert/strict";
3
+ import { mkdirSync, writeFileSync, rmSync } from "node:fs";
4
+ import { join } from "node:path";
5
+ import { tmpdir } from "node:os";
6
+
7
+ import { resolveMilestoneValidationVerdict } from "../milestone-validation-verdict.ts";
8
+ import {
9
+ openDatabase,
10
+ closeDatabase,
11
+ insertAssessment,
12
+ insertMilestone,
13
+ } from "../gsd-db.ts";
14
+ import { invalidateAllCaches } from "../cache.ts";
15
+ import { _clearGsdRootCache } from "../paths.ts";
16
+
17
+ function setup(base: string): void {
18
+ mkdirSync(join(base, ".gsd", "milestones", "M001"), { recursive: true });
19
+ process.chdir(base);
20
+ _clearGsdRootCache();
21
+ openDatabase(join(base, ".gsd", "gsd.db"));
22
+ invalidateAllCaches();
23
+ }
24
+
25
+ test("resolveMilestoneValidationVerdict prefers DB pass over stale worktree needs-attention", async () => {
26
+ const base = join(tmpdir(), `validation-verdict-${Date.now()}`);
27
+ mkdirSync(base, { recursive: true });
28
+ const worktree = join(base, ".gsd", "worktrees", "M001");
29
+ mkdirSync(join(worktree, ".gsd", "milestones", "M001"), { recursive: true });
30
+ writeFileSync(join(worktree, ".git"), "gitdir: ../.git/worktrees/M001\n", "utf-8");
31
+ writeFileSync(
32
+ join(worktree, ".gsd", "milestones", "M001", "M001-VALIDATION.md"),
33
+ "---\nverdict: needs-attention\n---\n\n# Validation\nStale worktree copy.\n",
34
+ );
35
+
36
+ try {
37
+ setup(base);
38
+ insertMilestone({ id: "M001", title: "Test", status: "active" });
39
+ insertAssessment({
40
+ path: join(worktree, ".gsd", "milestones", "M001", "M001-VALIDATION.md"),
41
+ milestoneId: "M001",
42
+ sliceId: null,
43
+ taskId: null,
44
+ status: "pass",
45
+ scope: "milestone-validation",
46
+ fullContent: "---\nverdict: pass\n---\n\n# Validation\nManual override.\n",
47
+ });
48
+
49
+ const verdict = await resolveMilestoneValidationVerdict(base, "M001");
50
+ assert.equal(verdict, "pass");
51
+ } finally {
52
+ closeDatabase();
53
+ rmSync(base, { recursive: true, force: true });
54
+ }
55
+ });
@@ -347,3 +347,48 @@ test('handlePlanMilestone updates depends_on when a queued milestone row already
347
347
  cleanup(base);
348
348
  }
349
349
  });
350
+
351
+ // Regression #566: bracket-wrapped slice ID in depends should be rejected
352
+ test('handlePlanMilestone rejects bracket-wrapped depends like ["[S01]"]', async () => {
353
+ const base = makeTmpBase();
354
+ const dbPath = join(base, '.gsd', 'gsd.db');
355
+ openDatabase(dbPath);
356
+
357
+ try {
358
+ const params = validParams();
359
+ // Simulate the LLM sending "[S01]" (the markdown fragment) instead of "S01"
360
+ const corruptSlices = [
361
+ params.slices[0],
362
+ { ...params.slices[1], depends: ['[S01]'] },
363
+ ];
364
+ const result = await handlePlanMilestone({ ...params, slices: corruptSlices }, base);
365
+ assert.ok('error' in result, 'should return an error for bracket-wrapped depends');
366
+ assert.match(result.error, /slices\[1\]\.depends must be an array of valid slice IDs/,
367
+ 'error should mention invalid slice ID format');
368
+ assert.equal(getMilestoneSlices('M001').length, 0, 'no slices should persist on validation failure');
369
+ } finally {
370
+ cleanup(base);
371
+ }
372
+ });
373
+
374
+ // Regression #566: unknown slice ID in depends should be rejected
375
+ test('handlePlanMilestone rejects depends referencing a slice not in the same milestone', async () => {
376
+ const base = makeTmpBase();
377
+ const dbPath = join(base, '.gsd', 'gsd.db');
378
+ openDatabase(dbPath);
379
+
380
+ try {
381
+ const params = validParams();
382
+ const corruptSlices = [
383
+ params.slices[0],
384
+ { ...params.slices[1], depends: ['S99'] },
385
+ ];
386
+ const result = await handlePlanMilestone({ ...params, slices: corruptSlices }, base);
387
+ assert.ok('error' in result, 'should return an error for unknown depends slice');
388
+ assert.match(result.error, /references unknown slice "S99"/,
389
+ 'error should name the unknown slice');
390
+ assert.equal(getMilestoneSlices('M001').length, 0, 'no slices should persist on validation failure');
391
+ } finally {
392
+ cleanup(base);
393
+ }
394
+ });
@@ -137,6 +137,7 @@ test("skillActivation default leaves no unresolved placeholder", () => {
137
137
  carryForwardSection: "Carry forward",
138
138
  resumeSection: "Resume",
139
139
  priorTaskLines: "- (no prior tasks)",
140
+ onDemandContext: "",
140
141
  templatesDir: join(fixtureRoot, "templates"),
141
142
  taskSummaryPath: join(fixtureRoot, ".gsd", "milestones", "M001", "slices", "S01", "tasks", "T01-SUMMARY.md"),
142
143
  inlinedTemplates: "Template",
@@ -163,6 +164,7 @@ test("custom skillActivation is substituted into execute-task", () => {
163
164
  carryForwardSection: "Carry forward",
164
165
  resumeSection: "Resume",
165
166
  priorTaskLines: "- (no prior tasks)",
167
+ onDemandContext: "",
166
168
  templatesDir: join(fixtureRoot, "templates"),
167
169
  taskSummaryPath: join(fixtureRoot, ".gsd", "milestones", "M001", "slices", "S01", "tasks", "T01-SUMMARY.md"),
168
170
  inlinedTemplates: "Template",
@@ -15,6 +15,7 @@ import {
15
15
  insertTask,
16
16
  getMilestoneSlices,
17
17
  getSliceTasks,
18
+ _getAdapter,
18
19
  } from '../gsd-db.ts';
19
20
  import {
20
21
  renderRoadmapFromDb,
@@ -423,4 +424,48 @@ console.log('\n=== planning-crossval Test 6: ROADMAP descriptor projection dir =
423
424
  }
424
425
  }
425
426
 
427
+ // ═══════════════════════════════════════════════════════════════════════════
428
+ // Test 7: Renderer strips bracket-wrapped depends from corrupt DB rows (#566)
429
+ // ═══════════════════════════════════════════════════════════════════════════
430
+ // Simulates a DB that was corrupted before the insertSlice validation guard
431
+ // was added (issue #566). The renderer must recover "[S01]" → "S01" rather
432
+ // than silently dropping the dependency and rendering depends:[].
433
+
434
+ console.log('\n=== planning-crossval Test 7: renderer recovers bracket-wrapped depends (#566) ===');
435
+ {
436
+ const base = realpathSync(createFixtureBase());
437
+ const dbPath = join(base, '.gsd', 'gsd.db');
438
+ openDatabase(dbPath);
439
+ try {
440
+ scaffoldDirs(base, 'M001', ['S01', 'S02']);
441
+
442
+ insertMilestone({
443
+ id: 'M001',
444
+ title: 'Bracket Recovery',
445
+ status: 'active',
446
+ planning: { vision: 'Recover bracket-wrapped depends from corrupt DB.' },
447
+ });
448
+
449
+ insertSlice({ id: 'S01', milestoneId: 'M001', title: 'Foundation', status: 'pending', risk: 'low', depends: [], demo: 'Foundation done.', sequence: 1 });
450
+ insertSlice({ id: 'S02', milestoneId: 'M001', title: 'Build', status: 'pending', risk: 'medium', depends: [], demo: 'Build done.', sequence: 2 });
451
+
452
+ // Bypass insertSlice validation to simulate pre-fix corrupt DB state
453
+ // (insertSlice now rejects bracket-wrapped IDs, but old DBs may have them)
454
+ _getAdapter()?.prepare('UPDATE slices SET depends = ? WHERE id = ? AND milestone_id = ?')
455
+ .run('["[S01]"]', 'S02', 'M001');
456
+
457
+ const rendered = await renderRoadmapFromDb(base, 'M001');
458
+ const content = readFileSync(rendered.roadmapPath, 'utf-8');
459
+ const parsed = parseRoadmapSlices(content);
460
+
461
+ assertEq(parsed.length, 2, 'T7: 2 slices parsed');
462
+ assertEq(parsed[1]?.depends.length, 1, 'T7: S02 has 1 dependency after bracket recovery');
463
+ assertEq(parsed[1]?.depends[0], 'S01', 'T7: recovered dependency is "S01", not "[S01]"');
464
+ assertTrue(content.includes('`depends:[S01]`'), 'T7: S02 renders with recovered depends:[S01] not empty depends:[]');
465
+ } finally {
466
+ closeDatabase();
467
+ cleanup(base);
468
+ }
469
+ }
470
+
426
471
  report();
@@ -0,0 +1,67 @@
1
+ import test from "node:test";
2
+ import assert from "node:assert/strict";
3
+ import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
4
+ import { join } from "node:path";
5
+ import { tmpdir } from "node:os";
6
+
7
+ import {
8
+ _resetPreferenceDiagnosticNotificationsForTests,
9
+ notifyPreferenceDiagnostics,
10
+ } from "../preferences-diagnostics.ts";
11
+ import { _resetParseWarningFlag } from "../preferences.ts";
12
+
13
+ test("notifyPreferenceDiagnostics dedupes within a surface but re-surfaces across surfaces", (t) => {
14
+ const originalCwd = process.cwd();
15
+ const originalGsdHome = process.env.GSD_HOME;
16
+ const tempProject = mkdtempSync(join(tmpdir(), "gsd-prefs-notify-project-"));
17
+ const tempGsdHome = mkdtempSync(join(tmpdir(), "gsd-prefs-notify-home-"));
18
+ t.after(() => {
19
+ process.chdir(originalCwd);
20
+ if (originalGsdHome === undefined) delete process.env.GSD_HOME;
21
+ else process.env.GSD_HOME = originalGsdHome;
22
+ rmSync(tempProject, { recursive: true, force: true });
23
+ rmSync(tempGsdHome, { recursive: true, force: true });
24
+ _resetPreferenceDiagnosticNotificationsForTests();
25
+ _resetParseWarningFlag();
26
+ });
27
+
28
+ mkdirSync(join(tempProject, ".gsd"), { recursive: true });
29
+ process.env.GSD_HOME = tempGsdHome;
30
+ process.chdir(tempProject);
31
+ _resetPreferenceDiagnosticNotificationsForTests();
32
+ _resetParseWarningFlag();
33
+
34
+ writeFileSync(
35
+ join(tempProject, ".gsd", "PREFERENCES.md"),
36
+ "---\nversion: 3\n---\n",
37
+ "utf-8",
38
+ );
39
+
40
+ const notifications: Array<{ message: string; type?: string }> = [];
41
+ const ctx = {
42
+ ui: {
43
+ notify(message: string, type?: "info" | "warning" | "error" | "success") {
44
+ notifications.push({ message, type });
45
+ },
46
+ },
47
+ };
48
+
49
+ assert.equal(
50
+ notifyPreferenceDiagnostics(ctx, tempProject, { surface: "session-start" }),
51
+ 1,
52
+ );
53
+ assert.equal(
54
+ notifyPreferenceDiagnostics(ctx, tempProject, { surface: "session-start" }),
55
+ 0,
56
+ );
57
+ assert.equal(
58
+ notifyPreferenceDiagnostics(ctx, tempProject, { surface: "auto-preflight" }),
59
+ 1,
60
+ );
61
+ assert.equal(notifications.length, 2);
62
+ assert.equal(notifications[0]!.type, "error");
63
+ assert.match(notifications[0]!.message, /GSD project preferences error/);
64
+ assert.match(notifications[0]!.message, /unsupported version 3/);
65
+ assert.match(notifications[0]!.message, /Run \/gsd doctor for details/);
66
+ assert.equal(notifications[1]!.message, notifications[0]!.message);
67
+ });
@@ -26,6 +26,7 @@ import {
26
26
  renderPreferencesForSystemPrompt,
27
27
  _resetParseWarningFlag,
28
28
  } from "../preferences.ts";
29
+ import { collectPreferenceDiagnostics, formatPreferenceDiagnostic } from "../preferences-diagnostics.ts";
29
30
  import { formatConfiguredModel, toPersistedModelId } from "../commands-prefs-wizard.ts";
30
31
  import { _resetLogs, peekLogs } from "../workflow-logger.ts";
31
32
  import type { GSDPreferences, GSDModelConfigV2, GSDPhaseModelConfig } from "../preferences.ts";
@@ -819,6 +820,188 @@ bad: [
819
820
  _resetLogs();
820
821
  });
821
822
 
823
+ test("malformed global preferences load with a structured parse diagnostic", (t) => {
824
+ const originalGsdHome = process.env.GSD_HOME;
825
+ const tempGsdHome = mkdtempSync(join(tmpdir(), "gsd-prefs-diagnostic-home-"));
826
+ t.after(() => {
827
+ if (originalGsdHome === undefined) delete process.env.GSD_HOME;
828
+ else process.env.GSD_HOME = originalGsdHome;
829
+ rmSync(tempGsdHome, { recursive: true, force: true });
830
+ _resetParseWarningFlag();
831
+ _resetLogs();
832
+ });
833
+
834
+ process.env.GSD_HOME = tempGsdHome;
835
+ _resetParseWarningFlag();
836
+ _resetLogs();
837
+ writeFileSync(
838
+ join(tempGsdHome, "PREFERENCES.md"),
839
+ [
840
+ "---",
841
+ "version: 1",
842
+ "models:",
843
+ " validation:",
844
+ " openrouter/deepseek/deepseek-v4-pro",
845
+ " thinking: high",
846
+ "---",
847
+ "",
848
+ ].join("\n"),
849
+ "utf-8",
850
+ );
851
+
852
+ const loaded = loadGlobalGSDPreferences();
853
+ assert.notEqual(loaded, null);
854
+ assert.deepEqual(loaded!.preferences, {});
855
+ const diagnostic = loaded!.diagnostics?.[0];
856
+ assert.equal(diagnostic?.kind, "parse");
857
+ assert.equal(diagnostic?.severity, "error");
858
+ assert.equal(diagnostic?.line, 5);
859
+ assert.equal(diagnostic?.column, 5);
860
+ assert.equal(diagnostic?.ignored, true);
861
+ assert.match(diagnostic?.message ?? "", /Implicit keys need/);
862
+
863
+ const formatted = formatPreferenceDiagnostic(diagnostic!);
864
+ assert.match(formatted, /GSD global preferences error/);
865
+ assert.match(formatted, /line 5, column 5/);
866
+ assert.match(formatted, /Preferences from this file were ignored/);
867
+ });
868
+
869
+ test("project preference validation errors load with structured diagnostics", (t) => {
870
+ const originalCwd = process.cwd();
871
+ const originalGsdHome = process.env.GSD_HOME;
872
+ const tempProject = mkdtempSync(join(tmpdir(), "gsd-prefs-validation-project-"));
873
+ const tempGsdHome = mkdtempSync(join(tmpdir(), "gsd-prefs-validation-home-"));
874
+ t.after(() => {
875
+ process.chdir(originalCwd);
876
+ if (originalGsdHome === undefined) delete process.env.GSD_HOME;
877
+ else process.env.GSD_HOME = originalGsdHome;
878
+ rmSync(tempProject, { recursive: true, force: true });
879
+ rmSync(tempGsdHome, { recursive: true, force: true });
880
+ });
881
+
882
+ mkdirSync(join(tempProject, ".gsd"), { recursive: true });
883
+ process.env.GSD_HOME = tempGsdHome;
884
+ process.chdir(tempProject);
885
+ writeFileSync(
886
+ join(tempProject, ".gsd", "PREFERENCES.md"),
887
+ "---\nversion: 3\n---\n",
888
+ "utf-8",
889
+ );
890
+
891
+ const loaded = loadProjectGSDPreferences(tempProject);
892
+ assert.notEqual(loaded, null);
893
+ assert.equal(loaded!.preferences.version, undefined);
894
+ const diagnostic = loaded!.diagnostics?.find((item) => item.kind === "validation");
895
+ assert.equal(diagnostic?.scope, "project");
896
+ assert.equal(diagnostic?.severity, "error");
897
+ assert.equal(diagnostic?.sanitized, true);
898
+ assert.match(diagnostic?.message ?? "", /unsupported version 3/);
899
+
900
+ const collected = collectPreferenceDiagnostics(tempProject);
901
+ assert.ok(
902
+ collected.some((item) => item.path === loaded!.path && item.message.includes("unsupported version 3")),
903
+ "collector should include project validation diagnostics",
904
+ );
905
+ });
906
+
907
+ test("malformed project preferences do not override effective global wrapper", (t) => {
908
+ const originalCwd = process.cwd();
909
+ const originalGsdHome = process.env.GSD_HOME;
910
+ const tempProject = mkdtempSync(join(tmpdir(), "gsd-prefs-effective-project-"));
911
+ const tempGsdHome = mkdtempSync(join(tmpdir(), "gsd-prefs-effective-home-"));
912
+ t.after(() => {
913
+ process.chdir(originalCwd);
914
+ if (originalGsdHome === undefined) delete process.env.GSD_HOME;
915
+ else process.env.GSD_HOME = originalGsdHome;
916
+ rmSync(tempProject, { recursive: true, force: true });
917
+ rmSync(tempGsdHome, { recursive: true, force: true });
918
+ _resetParseWarningFlag();
919
+ _resetLogs();
920
+ });
921
+
922
+ mkdirSync(join(tempProject, ".gsd"), { recursive: true });
923
+ process.env.GSD_HOME = tempGsdHome;
924
+ process.chdir(tempProject);
925
+ _resetParseWarningFlag();
926
+ _resetLogs();
927
+
928
+ const globalPath = getGlobalGSDPreferencesPath();
929
+ const projectPath = getProjectGSDPreferencesPath(tempProject);
930
+ writeFileSync(globalPath, "---\nlanguage: Spanish\n---\n", "utf-8");
931
+ writeFileSync(
932
+ projectPath,
933
+ [
934
+ "---",
935
+ "version: 1",
936
+ "models:",
937
+ " validation:",
938
+ " openrouter/deepseek/deepseek-v4-pro",
939
+ " thinking: high",
940
+ "---",
941
+ "",
942
+ ].join("\n"),
943
+ "utf-8",
944
+ );
945
+
946
+ const projectPrefs = loadProjectGSDPreferences(tempProject);
947
+ assert.equal(projectPrefs?.ignored, true);
948
+
949
+ const loaded = loadEffectiveGSDPreferences(tempProject);
950
+ assert.notEqual(loaded, null);
951
+ assert.equal(loaded!.path, globalPath);
952
+ assert.equal(loaded!.scope, "global");
953
+ assert.equal(loaded!.preferences.language, "Spanish");
954
+ assert.ok(
955
+ loaded!.diagnostics?.some((item) => item.path === projectPath && item.kind === "parse"),
956
+ "effective global preferences should carry malformed project diagnostics",
957
+ );
958
+ });
959
+
960
+ test("malformed global preferences: loadEffectiveGSDPreferences returns null without project file", (t) => {
961
+ const originalCwd = process.cwd();
962
+ const originalGsdHome = process.env.GSD_HOME;
963
+ const tempProject = mkdtempSync(join(tmpdir(), "gsd-prefs-effective-malformed-global-"));
964
+ const tempGsdHome = mkdtempSync(join(tmpdir(), "gsd-prefs-effective-malformed-home-"));
965
+ t.after(() => {
966
+ process.chdir(originalCwd);
967
+ if (originalGsdHome === undefined) delete process.env.GSD_HOME;
968
+ else process.env.GSD_HOME = originalGsdHome;
969
+ rmSync(tempProject, { recursive: true, force: true });
970
+ rmSync(tempGsdHome, { recursive: true, force: true });
971
+ _resetParseWarningFlag();
972
+ _resetLogs();
973
+ });
974
+
975
+ process.env.GSD_HOME = tempGsdHome;
976
+ process.chdir(tempProject);
977
+ _resetParseWarningFlag();
978
+ _resetLogs();
979
+
980
+ const globalPath = getGlobalGSDPreferencesPath();
981
+ writeFileSync(
982
+ globalPath,
983
+ [
984
+ "---",
985
+ "version: 1",
986
+ "models:",
987
+ " validation:",
988
+ " openrouter/deepseek/deepseek-v4-pro",
989
+ " thinking: high",
990
+ "---",
991
+ "",
992
+ ].join("\n"),
993
+ "utf-8",
994
+ );
995
+
996
+ const globalPrefs = loadGlobalGSDPreferences();
997
+ assert.equal(globalPrefs?.ignored, true, "malformed global should be marked ignored");
998
+
999
+ // Symmetric with malformed project + no global: effective result should be null,
1000
+ // not an effective object with ignored:true leaking through.
1001
+ const loaded = loadEffectiveGSDPreferences(tempProject);
1002
+ assert.equal(loaded, null, "effective preferences should be null when only global file is malformed");
1003
+ });
1004
+
822
1005
  // ── Experimental preferences ─────────────────────────────────────────────────
823
1006
 
824
1007
  test("experimental.rtk: true is accepted and stored", () => {