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

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 (424) hide show
  1. package/dist/cli-style.d.ts +17 -0
  2. package/dist/cli-style.js +28 -0
  3. package/dist/cli.js +1 -1
  4. package/dist/headless-events.d.ts +4 -2
  5. package/dist/headless-events.js +14 -34
  6. package/dist/models-resolver.d.ts +3 -13
  7. package/dist/models-resolver.js +3 -22
  8. package/dist/resource-loader.js +2 -14
  9. package/dist/resources/.managed-resources-content-hash +1 -1
  10. package/dist/resources/GSD-WORKFLOW.md +5 -4
  11. package/dist/resources/extensions/async-jobs/async-bash-tool.js +30 -64
  12. package/dist/resources/extensions/async-jobs/await-tool.js +80 -12
  13. package/dist/resources/extensions/async-jobs/index.js +65 -0
  14. package/dist/resources/extensions/async-jobs/job-manager.js +12 -1
  15. package/dist/resources/extensions/bg-shell/bg-shell-command.js +6 -6
  16. package/dist/resources/extensions/bg-shell/bg-shell-tool.js +10 -7
  17. package/dist/resources/extensions/bg-shell/overlay.js +9 -6
  18. package/dist/resources/extensions/bg-shell/process-manager.js +54 -25
  19. package/dist/resources/extensions/bg-shell/readiness-detector.js +11 -0
  20. package/dist/resources/extensions/bg-shell/utilities.js +3 -0
  21. package/dist/resources/extensions/browser-tools/engine/managed-gsd-browser.js +209 -88
  22. package/dist/resources/extensions/browser-tools/engine/selection.js +73 -5
  23. package/dist/resources/extensions/browser-tools/index.js +69 -12
  24. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +30 -4
  25. package/dist/resources/extensions/gsd/auto/custom-verify-retry-store.js +17 -2
  26. package/dist/resources/extensions/gsd/auto/detect-stuck.js +33 -13
  27. package/dist/resources/extensions/gsd/auto/dispatch-history.js +105 -0
  28. package/dist/resources/extensions/gsd/auto/dispatch-key.js +37 -0
  29. package/dist/resources/extensions/gsd/auto/loop.js +4 -1
  30. package/dist/resources/extensions/gsd/auto/orchestrator.js +61 -44
  31. package/dist/resources/extensions/gsd/auto/phases.js +2 -2
  32. package/dist/resources/extensions/gsd/auto-direct-dispatch.js +8 -32
  33. package/dist/resources/extensions/gsd/auto-dispatch.js +40 -57
  34. package/dist/resources/extensions/gsd/auto-model-selection.js +25 -6
  35. package/dist/resources/extensions/gsd/auto-post-unit.js +23 -8
  36. package/dist/resources/extensions/gsd/auto-prompts.js +81 -19
  37. package/dist/resources/extensions/gsd/auto-start.js +18 -15
  38. package/dist/resources/extensions/gsd/auto-tool-tracking.js +18 -0
  39. package/dist/resources/extensions/gsd/auto-unit-tool-scope.js +12 -20
  40. package/dist/resources/extensions/gsd/auto-verification.js +9 -28
  41. package/dist/resources/extensions/gsd/auto-worktree.js +30 -90
  42. package/dist/resources/extensions/gsd/auto.js +4 -13
  43. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +3 -2
  44. package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +23 -6
  45. package/dist/resources/extensions/gsd/bootstrap/register-extension.js +19 -0
  46. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +212 -48
  47. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +303 -77
  48. package/dist/resources/extensions/gsd/branch-patterns.js +2 -0
  49. package/dist/resources/extensions/gsd/browser-daemon-auto-prep.js +83 -0
  50. package/dist/resources/extensions/gsd/browser-evidence.js +8 -2
  51. package/dist/resources/extensions/gsd/captures.js +4 -6
  52. package/dist/resources/extensions/gsd/consent-question.js +337 -0
  53. package/dist/resources/extensions/gsd/consent-verdict.js +63 -0
  54. package/dist/resources/extensions/gsd/constants.js +0 -2
  55. package/dist/resources/extensions/gsd/crash-recovery.js +4 -12
  56. package/dist/resources/extensions/gsd/db/queries.js +26 -0
  57. package/dist/resources/extensions/gsd/dispatch-guard.js +10 -35
  58. package/dist/resources/extensions/gsd/doctor-environment.js +2 -6
  59. package/dist/resources/extensions/gsd/doctor-format.js +9 -6
  60. package/dist/resources/extensions/gsd/doctor-runtime-checks.js +13 -15
  61. package/dist/resources/extensions/gsd/engine-hook-contract.js +70 -0
  62. package/dist/resources/extensions/gsd/error-classifier.js +9 -0
  63. package/dist/resources/extensions/gsd/exec-sandbox.js +30 -10
  64. package/dist/resources/extensions/gsd/files.js +33 -19
  65. package/dist/resources/extensions/gsd/guidance.js +158 -0
  66. package/dist/resources/extensions/gsd/guided-flow.js +17 -2
  67. package/dist/resources/extensions/gsd/markdown-renderer.js +10 -0
  68. package/dist/resources/extensions/gsd/mcp-filter.js +2 -19
  69. package/dist/resources/extensions/gsd/mcp-tool-name.js +5 -13
  70. package/dist/resources/extensions/gsd/memory-consolidation-scanner.js +1 -1
  71. package/dist/resources/extensions/gsd/migrate/safety.js +4 -1
  72. package/dist/resources/extensions/gsd/milestone-closeout.js +13 -23
  73. package/dist/resources/extensions/gsd/notification-store.js +11 -4
  74. package/dist/resources/extensions/gsd/parallel-monitor-overlay.js +6 -4
  75. package/dist/resources/extensions/gsd/parsers-legacy.js +16 -4
  76. package/dist/resources/extensions/gsd/paths.js +27 -0
  77. package/dist/resources/extensions/gsd/pre-execution-checks.js +91 -3
  78. package/dist/resources/extensions/gsd/preferences-models.js +14 -48
  79. package/dist/resources/extensions/gsd/prompts/complete-slice.md +2 -2
  80. package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  81. package/dist/resources/extensions/gsd/prompts/refine-slice.md +1 -1
  82. package/dist/resources/extensions/gsd/prompts/replan-slice.md +1 -1
  83. package/dist/resources/extensions/gsd/prompts/run-uat.md +6 -4
  84. package/dist/resources/extensions/gsd/prompts/system.md +5 -2
  85. package/dist/resources/extensions/gsd/provider-error-guidance.js +1 -5
  86. package/dist/resources/extensions/gsd/provider-switch-observer.js +1 -1
  87. package/dist/resources/extensions/gsd/publication.js +87 -0
  88. package/dist/resources/extensions/gsd/reactive-graph.js +8 -1
  89. package/dist/resources/extensions/gsd/recovery-classification.js +37 -94
  90. package/dist/resources/extensions/gsd/safety/destructive-confirmation.js +108 -0
  91. package/dist/resources/extensions/gsd/state.js +6 -20
  92. package/dist/resources/extensions/gsd/stop-notice.js +57 -0
  93. package/dist/resources/extensions/gsd/tool-presentation-plan.js +4 -4
  94. package/dist/resources/extensions/gsd/tool-surface-readiness.js +56 -0
  95. package/dist/resources/extensions/gsd/tools/complete-slice.js +20 -10
  96. package/dist/resources/extensions/gsd/tools/exec-tool.js +9 -7
  97. package/dist/resources/extensions/gsd/tools/plan-slice.js +12 -6
  98. package/dist/resources/extensions/gsd/uat-policy.js +42 -16
  99. package/dist/resources/extensions/gsd/unit-closeout.js +138 -0
  100. package/dist/resources/extensions/gsd/unit-context-composer.js +74 -1
  101. package/dist/resources/extensions/gsd/unit-context-manifest.js +4 -27
  102. package/dist/resources/extensions/gsd/unit-registry.js +337 -0
  103. package/dist/resources/extensions/gsd/unit-tool-contracts.js +9 -182
  104. package/dist/resources/extensions/gsd/verdict-parser.js +1 -1
  105. package/dist/resources/extensions/gsd/web-app-uat.js +45 -8
  106. package/dist/resources/extensions/gsd/workflow-tool-surface.js +1 -1
  107. package/dist/resources/extensions/gsd/worktree-git-recovery.js +15 -9
  108. package/dist/resources/extensions/gsd/worktree-lifecycle.js +3 -2
  109. package/dist/resources/extensions/gsd/worktree-root.js +11 -0
  110. package/dist/resources/extensions/gsd/worktree-session-state.js +4 -5
  111. package/dist/resources/extensions/search-the-web/native-search.js +5 -3
  112. package/dist/resources/extensions/shared/browser-contract.js +59 -0
  113. package/dist/resources/extensions/shared/gsd-browser-cli.js +96 -5
  114. package/dist/resources/shared/package.json +3 -0
  115. package/dist/resources/skills/create-skill/references/executable-code.md +1 -1
  116. package/dist/resources/skills/create-skill/workflows/add-reference.md +8 -3
  117. package/dist/resources/skills/create-skill/workflows/add-script.md +4 -2
  118. package/dist/resources/skills/create-skill/workflows/add-template.md +3 -1
  119. package/dist/resources/skills/create-skill/workflows/add-workflow.md +8 -3
  120. package/dist/resources/skills/create-skill/workflows/upgrade-to-router.md +10 -5
  121. package/dist/resources/skills/create-skill/workflows/verify-skill.md +9 -4
  122. package/dist/resources/skills/spike-wrap-up/SKILL.md +9 -9
  123. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  124. package/dist/web/standalone/.next/BUILD_ID +1 -1
  125. package/dist/web/standalone/.next/app-path-routes-manifest.json +12 -12
  126. package/dist/web/standalone/.next/build-manifest.json +3 -3
  127. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  128. package/dist/web/standalone/.next/react-loadable-manifest.json +1 -1
  129. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  130. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  131. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  132. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  133. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  134. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  135. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  136. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  137. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  138. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  139. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  140. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  141. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  142. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  143. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  144. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  145. package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
  146. package/dist/web/standalone/.next/server/app/api/update/route.js.nft.json +1 -1
  147. package/dist/web/standalone/.next/server/app/index.html +1 -1
  148. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  149. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  150. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  151. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  152. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  153. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  154. package/dist/web/standalone/.next/server/app-paths-manifest.json +12 -12
  155. package/dist/web/standalone/.next/server/chunks/5124.js +1 -1
  156. package/dist/web/standalone/.next/server/chunks/8357.js +1 -1
  157. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  158. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  159. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  160. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  161. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  162. package/dist/web/standalone/.next/static/chunks/{796.cf859a427a2cb2ac.js → 796.e0bdc932325d7e03.js} +1 -1
  163. package/dist/web/standalone/.next/static/chunks/{webpack-fbea77b5f9953368.js → webpack-f0285ce91d4ec9ef.js} +1 -1
  164. package/dist/web/standalone/package.json +1 -1
  165. package/dist/worktree-cli.js +3 -6
  166. package/dist/worktree-status-banner.js +7 -15
  167. package/package.json +1 -1
  168. package/packages/cloud-mcp-gateway/package.json +2 -2
  169. package/packages/contracts/dist/rpc.d.ts +1 -0
  170. package/packages/contracts/dist/rpc.d.ts.map +1 -1
  171. package/packages/contracts/dist/rpc.js.map +1 -1
  172. package/packages/contracts/dist/workflow.d.ts +4 -0
  173. package/packages/contracts/dist/workflow.d.ts.map +1 -1
  174. package/packages/contracts/dist/workflow.js.map +1 -1
  175. package/packages/contracts/package.json +1 -1
  176. package/packages/daemon/package.json +4 -4
  177. package/packages/gsd-agent-core/package.json +5 -5
  178. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts +5 -0
  179. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  180. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js +5 -0
  181. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js.map +1 -1
  182. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  183. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js +7 -0
  184. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  185. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/input-controller.d.ts.map +1 -1
  186. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/input-controller.js +8 -1
  187. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/input-controller.js.map +1 -1
  188. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-chat-render.d.ts.map +1 -1
  189. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-chat-render.js +11 -1
  190. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-chat-render.js.map +1 -1
  191. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-auth.d.ts.map +1 -1
  192. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-auth.js +4 -4
  193. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-auth.js.map +1 -1
  194. package/packages/gsd-agent-modes/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  195. package/packages/gsd-agent-modes/dist/modes/rpc/rpc-mode.js +3 -1
  196. package/packages/gsd-agent-modes/dist/modes/rpc/rpc-mode.js.map +1 -1
  197. package/packages/gsd-agent-modes/package.json +7 -7
  198. package/packages/mcp-server/dist/cli.js +6 -3
  199. package/packages/mcp-server/dist/cli.js.map +1 -1
  200. package/packages/mcp-server/dist/workflow-tools.d.ts +8 -0
  201. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  202. package/packages/mcp-server/dist/workflow-tools.js +17 -1
  203. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  204. package/packages/mcp-server/package.json +3 -3
  205. package/packages/native/package.json +1 -1
  206. package/packages/pi-agent-core/dist/harness/env/nodejs.d.ts +1 -0
  207. package/packages/pi-agent-core/dist/harness/env/nodejs.d.ts.map +1 -1
  208. package/packages/pi-agent-core/dist/harness/env/nodejs.js +34 -3
  209. package/packages/pi-agent-core/dist/harness/env/nodejs.js.map +1 -1
  210. package/packages/pi-agent-core/dist/index.d.ts +1 -0
  211. package/packages/pi-agent-core/dist/index.d.ts.map +1 -1
  212. package/packages/pi-agent-core/dist/index.js +3 -0
  213. package/packages/pi-agent-core/dist/index.js.map +1 -1
  214. package/packages/pi-agent-core/package.json +1 -1
  215. package/packages/pi-ai/README.md +1 -0
  216. package/packages/pi-ai/dist/image-models.generated.d.ts +2 -2
  217. package/packages/pi-ai/dist/image-models.generated.js +6 -6
  218. package/packages/pi-ai/dist/image-models.generated.js.map +1 -1
  219. package/packages/pi-ai/dist/models.generated.d.ts +35 -125
  220. package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
  221. package/packages/pi-ai/dist/models.generated.js +46 -120
  222. package/packages/pi-ai/dist/models.generated.js.map +1 -1
  223. package/packages/pi-ai/package.json +3 -2
  224. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts +2 -2
  225. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts.map +1 -1
  226. package/packages/pi-coding-agent/dist/core/auth-storage.js +19 -13
  227. package/packages/pi-coding-agent/dist/core/auth-storage.js.map +1 -1
  228. package/packages/pi-coding-agent/dist/core/provider-readiness.d.ts.map +1 -1
  229. package/packages/pi-coding-agent/dist/core/provider-readiness.js +13 -6
  230. package/packages/pi-coding-agent/dist/core/provider-readiness.js.map +1 -1
  231. package/packages/pi-coding-agent/dist/core/tools/bash.d.ts +11 -0
  232. package/packages/pi-coding-agent/dist/core/tools/bash.d.ts.map +1 -1
  233. package/packages/pi-coding-agent/dist/core/tools/bash.js +53 -11
  234. package/packages/pi-coding-agent/dist/core/tools/bash.js.map +1 -1
  235. package/packages/pi-coding-agent/dist/index.d.ts +1 -1
  236. package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
  237. package/packages/pi-coding-agent/dist/index.js +1 -1
  238. package/packages/pi-coding-agent/dist/index.js.map +1 -1
  239. package/packages/pi-coding-agent/dist/utils/shell.d.ts +28 -2
  240. package/packages/pi-coding-agent/dist/utils/shell.d.ts.map +1 -1
  241. package/packages/pi-coding-agent/dist/utils/shell.js +56 -10
  242. package/packages/pi-coding-agent/dist/utils/shell.js.map +1 -1
  243. package/packages/pi-coding-agent/package.json +7 -7
  244. package/packages/pi-tui/dist/tui.d.ts.map +1 -1
  245. package/packages/pi-tui/dist/tui.js +9 -0
  246. package/packages/pi-tui/dist/tui.js.map +1 -1
  247. package/packages/pi-tui/package.json +2 -2
  248. package/packages/rpc-client/package.json +2 -2
  249. package/pkg/package.json +1 -1
  250. package/src/resources/GSD-WORKFLOW.md +5 -4
  251. package/src/resources/extensions/async-jobs/async-bash-cancel.test.ts +360 -0
  252. package/src/resources/extensions/async-jobs/async-bash-tool.ts +33 -56
  253. package/src/resources/extensions/async-jobs/await-tool.test.ts +139 -0
  254. package/src/resources/extensions/async-jobs/await-tool.ts +82 -12
  255. package/src/resources/extensions/async-jobs/index.ts +79 -0
  256. package/src/resources/extensions/async-jobs/job-manager.ts +21 -1
  257. package/src/resources/extensions/bg-shell/bg-shell-command.ts +6 -6
  258. package/src/resources/extensions/bg-shell/bg-shell-tool.ts +10 -6
  259. package/src/resources/extensions/bg-shell/overlay.ts +9 -5
  260. package/src/resources/extensions/bg-shell/process-manager.ts +50 -25
  261. package/src/resources/extensions/bg-shell/readiness-detector.ts +12 -0
  262. package/src/resources/extensions/bg-shell/tests/lifecycle-and-utilities.test.ts +48 -1
  263. package/src/resources/extensions/bg-shell/utilities.ts +3 -0
  264. package/src/resources/extensions/browser-tools/engine/managed-gsd-browser.ts +265 -98
  265. package/src/resources/extensions/browser-tools/engine/selection.ts +90 -4
  266. package/src/resources/extensions/browser-tools/index.ts +71 -13
  267. package/src/resources/extensions/browser-tools/tests/browser-engine-selection.test.mjs +83 -13
  268. package/src/resources/extensions/browser-tools/tests/gsd-browser-launch-config.test.mjs +29 -1
  269. package/src/resources/extensions/browser-tools/tests/managed-gsd-browser-tools.test.mjs +136 -0
  270. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +34 -4
  271. package/src/resources/extensions/gsd/auto/custom-verify-retry-store.ts +21 -3
  272. package/src/resources/extensions/gsd/auto/detect-stuck.ts +32 -9
  273. package/src/resources/extensions/gsd/auto/dispatch-history.ts +152 -0
  274. package/src/resources/extensions/gsd/auto/dispatch-key.ts +39 -0
  275. package/src/resources/extensions/gsd/auto/loop.ts +4 -1
  276. package/src/resources/extensions/gsd/auto/orchestrator.ts +70 -46
  277. package/src/resources/extensions/gsd/auto/phases.ts +2 -2
  278. package/src/resources/extensions/gsd/auto-direct-dispatch.ts +8 -32
  279. package/src/resources/extensions/gsd/auto-dispatch.ts +38 -52
  280. package/src/resources/extensions/gsd/auto-model-selection.ts +25 -5
  281. package/src/resources/extensions/gsd/auto-post-unit.ts +25 -8
  282. package/src/resources/extensions/gsd/auto-prompts.ts +118 -35
  283. package/src/resources/extensions/gsd/auto-start.ts +18 -17
  284. package/src/resources/extensions/gsd/auto-tool-tracking.ts +19 -0
  285. package/src/resources/extensions/gsd/auto-unit-tool-scope.ts +14 -21
  286. package/src/resources/extensions/gsd/auto-verification.ts +8 -26
  287. package/src/resources/extensions/gsd/auto-worktree.ts +30 -93
  288. package/src/resources/extensions/gsd/auto.ts +8 -15
  289. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +3 -5
  290. package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +23 -6
  291. package/src/resources/extensions/gsd/bootstrap/register-extension.ts +24 -0
  292. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +251 -47
  293. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +352 -84
  294. package/src/resources/extensions/gsd/branch-patterns.ts +3 -0
  295. package/src/resources/extensions/gsd/browser-daemon-auto-prep.ts +108 -0
  296. package/src/resources/extensions/gsd/browser-evidence.ts +18 -2
  297. package/src/resources/extensions/gsd/captures.ts +4 -6
  298. package/src/resources/extensions/gsd/consent-question.ts +416 -0
  299. package/src/resources/extensions/gsd/consent-verdict.ts +86 -0
  300. package/src/resources/extensions/gsd/constants.ts +0 -3
  301. package/src/resources/extensions/gsd/crash-recovery.ts +3 -9
  302. package/src/resources/extensions/gsd/db/queries.ts +37 -0
  303. package/src/resources/extensions/gsd/dispatch-guard.ts +8 -31
  304. package/src/resources/extensions/gsd/doctor-environment.ts +2 -7
  305. package/src/resources/extensions/gsd/doctor-format.ts +12 -7
  306. package/src/resources/extensions/gsd/doctor-runtime-checks.ts +13 -15
  307. package/src/resources/extensions/gsd/engine-hook-contract.ts +79 -0
  308. package/src/resources/extensions/gsd/error-classifier.ts +11 -0
  309. package/src/resources/extensions/gsd/exec-sandbox.ts +49 -9
  310. package/src/resources/extensions/gsd/files.ts +33 -12
  311. package/src/resources/extensions/gsd/guidance.ts +217 -0
  312. package/src/resources/extensions/gsd/guided-flow.ts +16 -2
  313. package/src/resources/extensions/gsd/markdown-renderer.ts +11 -0
  314. package/src/resources/extensions/gsd/mcp-filter.ts +2 -23
  315. package/src/resources/extensions/gsd/mcp-tool-name.ts +6 -11
  316. package/src/resources/extensions/gsd/memory-consolidation-scanner.ts +1 -1
  317. package/src/resources/extensions/gsd/migrate/safety.ts +4 -1
  318. package/src/resources/extensions/gsd/milestone-closeout.ts +13 -23
  319. package/src/resources/extensions/gsd/notification-store.ts +26 -3
  320. package/src/resources/extensions/gsd/parallel-monitor-overlay.ts +6 -4
  321. package/src/resources/extensions/gsd/parsers-legacy.ts +16 -4
  322. package/src/resources/extensions/gsd/paths.ts +33 -0
  323. package/src/resources/extensions/gsd/pre-execution-checks.ts +109 -3
  324. package/src/resources/extensions/gsd/preferences-models.ts +12 -47
  325. package/src/resources/extensions/gsd/prompts/complete-slice.md +2 -2
  326. package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  327. package/src/resources/extensions/gsd/prompts/refine-slice.md +1 -1
  328. package/src/resources/extensions/gsd/prompts/replan-slice.md +1 -1
  329. package/src/resources/extensions/gsd/prompts/run-uat.md +6 -4
  330. package/src/resources/extensions/gsd/prompts/system.md +5 -2
  331. package/src/resources/extensions/gsd/provider-error-guidance.ts +4 -9
  332. package/src/resources/extensions/gsd/provider-switch-observer.ts +1 -1
  333. package/src/resources/extensions/gsd/publication.ts +122 -0
  334. package/src/resources/extensions/gsd/reactive-graph.ts +11 -1
  335. package/src/resources/extensions/gsd/recovery-classification.ts +42 -96
  336. package/src/resources/extensions/gsd/safety/destructive-confirmation.ts +134 -0
  337. package/src/resources/extensions/gsd/state.ts +9 -21
  338. package/src/resources/extensions/gsd/stop-notice.ts +75 -0
  339. package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +22 -0
  340. package/src/resources/extensions/gsd/tests/auto-orchestrator.test.ts +101 -26
  341. package/src/resources/extensions/gsd/tests/browser-automation-contract-fixture.ts +39 -0
  342. package/src/resources/extensions/gsd/tests/browser-contract.test.ts +44 -0
  343. package/src/resources/extensions/gsd/tests/browser-daemon-auto-prep.test.ts +144 -0
  344. package/src/resources/extensions/gsd/tests/checkout-branch-stash-guard.test.ts +66 -1
  345. package/src/resources/extensions/gsd/tests/clear-stale-autostart.test.ts +22 -0
  346. package/src/resources/extensions/gsd/tests/commands-verdict.test.ts +8 -7
  347. package/src/resources/extensions/gsd/tests/complete-slice-verification-gate.test.ts +42 -0
  348. package/src/resources/extensions/gsd/tests/consent-question.test.ts +336 -0
  349. package/src/resources/extensions/gsd/tests/custom-verify-retry-store.test.ts +67 -0
  350. package/src/resources/extensions/gsd/tests/deep-project-auto-loop.test.ts +10 -10
  351. package/src/resources/extensions/gsd/tests/destructive-confirmation.test.ts +303 -0
  352. package/src/resources/extensions/gsd/tests/dispatch-history.test.ts +273 -0
  353. package/src/resources/extensions/gsd/tests/dispatch-run-uat-browser-tools.test.ts +2 -1
  354. package/src/resources/extensions/gsd/tests/dynamic-bash-no-cap.test.ts +132 -0
  355. package/src/resources/extensions/gsd/tests/engine-hook-contract.test.ts +148 -0
  356. package/src/resources/extensions/gsd/tests/exec-graceful-kill.test.ts +193 -0
  357. package/src/resources/extensions/gsd/tests/exec-tool.test.ts +29 -1
  358. package/src/resources/extensions/gsd/tests/extension-bootstrap-isolation.test.ts +35 -1
  359. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +27 -0
  360. package/src/resources/extensions/gsd/tests/guidance.test.ts +148 -0
  361. package/src/resources/extensions/gsd/tests/integration/auto-worktree-milestone-merge.test.ts +53 -11
  362. package/src/resources/extensions/gsd/tests/integration/auto-worktree.test.ts +73 -58
  363. package/src/resources/extensions/gsd/tests/integration/gsd-integration-fixture.ts +80 -0
  364. package/src/resources/extensions/gsd/tests/integration/run-uat.test.ts +199 -0
  365. package/src/resources/extensions/gsd/tests/mcp-project-config.test.ts +3 -1
  366. package/src/resources/extensions/gsd/tests/model-unittype-mapping.test.ts +32 -1
  367. package/src/resources/extensions/gsd/tests/notification-store.test.ts +32 -0
  368. package/src/resources/extensions/gsd/tests/oauth-api-model-routing.test.ts +167 -0
  369. package/src/resources/extensions/gsd/tests/parallel-research-dispatch.test.ts +18 -0
  370. package/src/resources/extensions/gsd/tests/parsers-legacy-importers.test.ts +139 -0
  371. package/src/resources/extensions/gsd/tests/pre-execution-checks.test.ts +193 -1
  372. package/src/resources/extensions/gsd/tests/prompt-db.test.ts +124 -6
  373. package/src/resources/extensions/gsd/tests/provider-error-guidance.test.ts +3 -3
  374. package/src/resources/extensions/gsd/tests/publication.test.ts +120 -0
  375. package/src/resources/extensions/gsd/tests/register-hooks-depth-verification.test.ts +157 -0
  376. package/src/resources/extensions/gsd/tests/runtime-invariant-modules.test.ts +1 -0
  377. package/src/resources/extensions/gsd/tests/stop-notice.test.ts +70 -0
  378. package/src/resources/extensions/gsd/tests/token-tool-gating.test.ts +76 -0
  379. package/src/resources/extensions/gsd/tests/tool-invocation-error-loop-break.test.ts +8 -0
  380. package/src/resources/extensions/gsd/tests/tool-surface-readiness.test.ts +155 -0
  381. package/src/resources/extensions/gsd/tests/uat-policy.test.ts +112 -29
  382. package/src/resources/extensions/gsd/tests/unit-closeout.test.ts +209 -0
  383. package/src/resources/extensions/gsd/tests/unit-context-composer.test.ts +67 -2
  384. package/src/resources/extensions/gsd/tests/unit-registry.test.ts +163 -0
  385. package/src/resources/extensions/gsd/tests/web-app-uat.test.ts +44 -1
  386. package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +2 -2
  387. package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +2 -2
  388. package/src/resources/extensions/gsd/tests/write-gate-seam.test.ts +358 -0
  389. package/src/resources/extensions/gsd/tests/write-gate.test.ts +67 -1
  390. package/src/resources/extensions/gsd/tool-presentation-plan.ts +4 -4
  391. package/src/resources/extensions/gsd/tool-surface-readiness.ts +76 -0
  392. package/src/resources/extensions/gsd/tools/complete-slice.ts +20 -10
  393. package/src/resources/extensions/gsd/tools/exec-tool.ts +8 -7
  394. package/src/resources/extensions/gsd/tools/plan-slice.ts +12 -6
  395. package/src/resources/extensions/gsd/uat-policy.ts +62 -16
  396. package/src/resources/extensions/gsd/unit-closeout.ts +201 -0
  397. package/src/resources/extensions/gsd/unit-context-composer.ts +111 -1
  398. package/src/resources/extensions/gsd/unit-context-manifest.ts +4 -28
  399. package/src/resources/extensions/gsd/unit-registry.ts +412 -0
  400. package/src/resources/extensions/gsd/unit-tool-contracts.ts +27 -192
  401. package/src/resources/extensions/gsd/verdict-parser.ts +1 -1
  402. package/src/resources/extensions/gsd/web-app-uat.ts +51 -8
  403. package/src/resources/extensions/gsd/workflow-tool-surface.ts +4 -1
  404. package/src/resources/extensions/gsd/worktree-git-recovery.ts +15 -9
  405. package/src/resources/extensions/gsd/worktree-lifecycle.ts +3 -8
  406. package/src/resources/extensions/gsd/worktree-root.ts +12 -0
  407. package/src/resources/extensions/gsd/worktree-session-state.ts +3 -5
  408. package/src/resources/extensions/search-the-web/native-search.ts +5 -3
  409. package/src/resources/extensions/shared/browser-contract.ts +66 -0
  410. package/src/resources/extensions/shared/gsd-browser-cli.ts +119 -5
  411. package/src/resources/shared/package.json +3 -0
  412. package/src/resources/skills/create-skill/references/executable-code.md +1 -1
  413. package/src/resources/skills/create-skill/workflows/add-reference.md +8 -3
  414. package/src/resources/skills/create-skill/workflows/add-script.md +4 -2
  415. package/src/resources/skills/create-skill/workflows/add-template.md +3 -1
  416. package/src/resources/skills/create-skill/workflows/add-workflow.md +8 -3
  417. package/src/resources/skills/create-skill/workflows/upgrade-to-router.md +10 -5
  418. package/src/resources/skills/create-skill/workflows/verify-skill.md +9 -4
  419. package/src/resources/skills/spike-wrap-up/SKILL.md +9 -9
  420. package/dist/resources/extensions/gsd/user-input-boundary.js +0 -218
  421. package/src/resources/extensions/gsd/tests/user-input-boundary.test.ts +0 -173
  422. package/src/resources/extensions/gsd/user-input-boundary.ts +0 -216
  423. /package/dist/web/standalone/.next/static/{C24pqUd-aru-l0Dp0gLZP → jmTLg6xZmAuq_LIqKOxrH}/_buildManifest.js +0 -0
  424. /package/dist/web/standalone/.next/static/{C24pqUd-aru-l0Dp0gLZP → jmTLg6xZmAuq_LIqKOxrH}/_ssgManifest.js +0 -0
@@ -731,6 +731,75 @@ test('(u) run-uat prompt promotes artifact-driven browser specs to browser-execu
731
731
  }
732
732
  });
733
733
 
734
+ test('(w) run-uat prompt promotes browser-executable harness specs to runtime-executable (M006/S01)', async () => {
735
+ const base = createFixtureBase();
736
+ try {
737
+ const uatRel = '.gsd/milestones/M006/slices/S01/S01-UAT.md';
738
+ const uatContent = [
739
+ '# S01 UAT',
740
+ '',
741
+ '## UAT Type',
742
+ '- UAT mode: browser-executable',
743
+ '',
744
+ '## Preconditions',
745
+ '- Start the local app server with `npm run start`.',
746
+ '- Open the app at `http://127.0.0.1:4173`.',
747
+ '',
748
+ '## Evidence',
749
+ '- Fresh closeout verification command: `npm run test:uat`',
750
+ ].join('\n');
751
+ writeSliceFile(base, 'M006', 'S01', 'UAT', uatContent);
752
+
753
+ const prompt = await buildRunUatPrompt('M006', 'S01', uatRel, uatContent, base);
754
+
755
+ assert.match(prompt, /\*\*Detected UAT mode:\*\*\s*`runtime-executable`/);
756
+ assert.match(prompt, /uatType: "runtime-executable"/);
757
+ assert.match(prompt, /Runtime harness override/i);
758
+ assert.match(prompt, /Do \*\*not\*\* call `uat-service-start`/);
759
+ assert.doesNotMatch(prompt, /uatType: "browser-executable"/);
760
+ } finally {
761
+ cleanup(base);
762
+ }
763
+ });
764
+
765
+ test('(w2) run-uat prompt promotes harness from slice context when UAT only names test:server (M007/S01)', async () => {
766
+ const base = createFixtureBase();
767
+ try {
768
+ const uatRel = '.gsd/milestones/M007/slices/S01/S01-UAT.md';
769
+ const uatContent = [
770
+ '# S01 UAT',
771
+ '',
772
+ '## UAT Type',
773
+ '- UAT mode: browser-executable',
774
+ '',
775
+ '## Preconditions',
776
+ '- Start the dev/local verification server with `npm run test:server`.',
777
+ '- Open the app at the localhost URL printed by the server.',
778
+ ].join('\n');
779
+ writeSliceFile(base, 'M007', 'S01', 'UAT', uatContent);
780
+ writeSliceFile(
781
+ base,
782
+ 'M007',
783
+ 'S01',
784
+ 'SUMMARY',
785
+ [
786
+ '# S01 Summary',
787
+ '',
788
+ 'Verification: `npm run test:uat` passed with clean browser diagnostics.',
789
+ ].join('\n'),
790
+ );
791
+
792
+ const prompt = await buildRunUatPrompt('M007', 'S01', uatRel, uatContent, base);
793
+
794
+ assert.match(prompt, /\*\*Detected UAT mode:\*\*\s*`runtime-executable`/);
795
+ assert.match(prompt, /Runtime harness override/i);
796
+ assert.match(prompt, /npm run test:server/);
797
+ assert.match(prompt, /uatType: "runtime-executable"/);
798
+ } finally {
799
+ cleanup(base);
800
+ }
801
+ });
802
+
734
803
  test('(v) run-uat prompt keeps deferred browser work artifact-driven', async () => {
735
804
  const base = createFixtureBase();
736
805
  try {
@@ -748,4 +817,134 @@ test('(v) run-uat prompt keeps deferred browser work artifact-driven', async ()
748
817
  cleanup(base);
749
818
  }
750
819
  });
820
+
821
+ test('(x) checkNeedsRunUat returns runtime-executable when slice SUMMARY names a self-contained harness (M007/S01)', async () => {
822
+ // Regression: the dispatch gate must surface the same effective UAT mode
823
+ // that buildRunUatPrompt emits. When the UAT file alone declares
824
+ // `browser-executable` but the slice SUMMARY references `npm run test:uat`,
825
+ // the prompt promotes to `runtime-executable` — and so must the gate, or
826
+ // the auto-dispatch path requires browser tools / warms up the browser
827
+ // daemon (and may stop dispatch entirely) for a UAT that never touches
828
+ // the browser. See cursor[bot] review on PR #696.
829
+ const base = createFixtureBase();
830
+ try {
831
+ const roadmapDir = join(base, '.gsd', 'milestones', 'M007');
832
+ mkdirSync(roadmapDir, { recursive: true });
833
+ writeFileSync(
834
+ join(roadmapDir, 'M007-ROADMAP.md'),
835
+ [
836
+ '# M007: Test roadmap',
837
+ '',
838
+ '## Slices',
839
+ '',
840
+ '- [x] **S01: Only slice** `risk:low` `depends:[]`',
841
+ '',
842
+ '## Boundary Map',
843
+ '',
844
+ ].join('\n'),
845
+ );
846
+ writeSliceFile(
847
+ base,
848
+ 'M007',
849
+ 'S01',
850
+ 'UAT',
851
+ [
852
+ '# S01 UAT',
853
+ '',
854
+ '## UAT Type',
855
+ '- UAT mode: browser-executable',
856
+ '',
857
+ '## Preconditions',
858
+ '- Start the dev/local verification server with `npm run test:server`.',
859
+ ].join('\n'),
860
+ );
861
+ writeSliceFile(
862
+ base,
863
+ 'M007',
864
+ 'S01',
865
+ 'SUMMARY',
866
+ [
867
+ '# S01 Summary',
868
+ '',
869
+ 'Verification: `npm run test:uat` passed with clean browser diagnostics.',
870
+ ].join('\n'),
871
+ );
872
+
873
+ const state = {
874
+ activeMilestone: { id: 'M007', title: 'Test roadmap' },
875
+ activeSlice: null,
876
+ activeTask: null,
877
+ phase: 'validating-milestone',
878
+ recentDecisions: [],
879
+ blockers: [],
880
+ nextAction: 'Validate M007',
881
+ registry: [],
882
+ } as const;
883
+
884
+ const result = await checkNeedsRunUat(base, 'M007', state as any, { uat_dispatch: true } as any);
885
+ assert.deepStrictEqual(
886
+ result,
887
+ { sliceId: 'S01', uatType: 'runtime-executable' },
888
+ 'dispatch gate must mirror the prompt`s runtime-executable promotion so it does not require browser tools',
889
+ );
890
+ } finally {
891
+ cleanup(base);
892
+ }
893
+ });
894
+
895
+ test('(x2) checkNeedsRunUat leaves true browser-executable UAT unpromoted when no harness is referenced', async () => {
896
+ // Counter-test for (x): when the slice SUMMARY does NOT name a
897
+ // self-contained harness, the dispatch gate must still require browser
898
+ // tools for a genuinely browser-executable UAT.
899
+ const base = createFixtureBase();
900
+ try {
901
+ const roadmapDir = join(base, '.gsd', 'milestones', 'M008');
902
+ mkdirSync(roadmapDir, { recursive: true });
903
+ writeFileSync(
904
+ join(roadmapDir, 'M008-ROADMAP.md'),
905
+ [
906
+ '# M008: Test roadmap',
907
+ '',
908
+ '## Slices',
909
+ '',
910
+ '- [x] **S01: Only slice** `risk:low` `depends:[]`',
911
+ '',
912
+ '## Boundary Map',
913
+ '',
914
+ ].join('\n'),
915
+ );
916
+ writeSliceFile(base, 'M008', 'S01', 'UAT', makeBrowserObservableUatContent('browser-executable'));
917
+ writeSliceFile(
918
+ base,
919
+ 'M008',
920
+ 'S01',
921
+ 'SUMMARY',
922
+ [
923
+ '# S01 Summary',
924
+ '',
925
+ 'Verification: clicked through the UI and confirmed the search box filters todos.',
926
+ ].join('\n'),
927
+ );
928
+
929
+ const state = {
930
+ activeMilestone: { id: 'M008', title: 'Test roadmap' },
931
+ activeSlice: null,
932
+ activeTask: null,
933
+ phase: 'validating-milestone',
934
+ recentDecisions: [],
935
+ blockers: [],
936
+ nextAction: 'Validate M008',
937
+ registry: [],
938
+ } as const;
939
+
940
+ const result = await checkNeedsRunUat(base, 'M008', state as any, { uat_dispatch: true } as any);
941
+ assert.deepStrictEqual(
942
+ result,
943
+ { sliceId: 'S01', uatType: 'browser-executable' },
944
+ 'a true browser-executable UAT without a harness reference must keep its browser-executable mode',
945
+ );
946
+ } finally {
947
+ cleanup(base);
948
+ }
949
+ });
751
950
  });
@@ -60,7 +60,9 @@ test("ensureProjectWorkflowMcpConfig creates .mcp.json with workflow and browser
60
60
  assert.equal(typeof browserArgs[mcpArgIndex + 6], "string");
61
61
  assert.ok((browserArgs[mcpArgIndex + 6] ?? "").length > 0, "identity-key must be non-empty");
62
62
  assert.equal(browserArgs[mcpArgIndex + 7], "--identity-project");
63
- assert.equal(browserArgs[mcpArgIndex + 8], projectRoot);
63
+ assert.equal(typeof browserArgs[mcpArgIndex + 8], "string");
64
+ assert.ok((browserArgs[mcpArgIndex + 8] ?? "").length > 0, "identity-project must be non-empty");
65
+ assert.doesNotMatch(browserArgs[mcpArgIndex + 8] ?? "", /[\\/]/, "identity-project must not be a filesystem path");
64
66
  assert.equal((browserServer as { cwd?: string })?.cwd, projectRoot);
65
67
 
66
68
  const settings = JSON.parse(readFileSync(join(projectRoot, ".claude", "settings.local.json"), "utf-8")) as {
@@ -14,7 +14,7 @@ import { tmpdir } from "node:os";
14
14
  import { resolveExpectedArtifactPath } from "../auto-artifact-paths.ts";
15
15
  import { unitPhaseLabel, unitVerb } from "../auto-dashboard.ts";
16
16
  import { classifyUnitPhase } from "../metrics.ts";
17
- import { resolveModelWithFallbacksForUnit } from "../preferences-models.ts";
17
+ import { resolveDefaultSessionModel, resolveModelWithFallbacksForUnit } from "../preferences-models.ts";
18
18
  import { KNOWN_UNIT_LABELS } from "../preferences-types.ts";
19
19
 
20
20
  function withModelPreferences<T>(fn: () => T): T {
@@ -92,6 +92,37 @@ test("run-uat falls back to completion when uat bucket is not configured", () =>
92
92
  }
93
93
  });
94
94
 
95
+ test("default session model resolves from the explicit project base path", () => {
96
+ const oldHome = process.env.GSD_HOME;
97
+ const originalCwd = process.cwd();
98
+ const home = mkdtempSync(join(tmpdir(), "gsd-model-map-home-"));
99
+ const base = mkdtempSync(join(tmpdir(), "gsd-model-map-project-"));
100
+
101
+ try {
102
+ process.env.GSD_HOME = home;
103
+ mkdirSync(join(base, ".gsd"), { recursive: true });
104
+ writeFileSync(join(base, ".gsd", "PREFERENCES.md"), [
105
+ "---",
106
+ "models:",
107
+ " execution: gpt-5.5",
108
+ "---",
109
+ "",
110
+ ].join("\n"));
111
+ process.chdir(home);
112
+
113
+ assert.deepEqual(resolveDefaultSessionModel("openai-codex", base), {
114
+ provider: "openai-codex",
115
+ id: "gpt-5.5",
116
+ });
117
+ } finally {
118
+ process.chdir(originalCwd);
119
+ if (oldHome === undefined) delete process.env.GSD_HOME;
120
+ else process.env.GSD_HOME = oldHome;
121
+ rmSync(home, { recursive: true, force: true });
122
+ rmSync(base, { recursive: true, force: true });
123
+ }
124
+ });
125
+
95
126
  test("every known unit label with a dispatch phase resolves when all model buckets are configured", () => {
96
127
  withModelPreferences(() => {
97
128
  const missing = KNOWN_UNIT_LABELS.filter((unitType) => !resolveModelWithFallbacksForUnit(unitType));
@@ -306,6 +306,38 @@ describe("notification-store", () => {
306
306
  rmSync(lockPath, { force: true });
307
307
  });
308
308
 
309
+ test("structured meta persists kind and scope on the entry", () => {
310
+ initNotificationStore(tmp);
311
+ appendNotification("Auto-mode blocked — validation gate", "warning", "notify", { kind: "auto-stop", scope: "M005" });
312
+
313
+ const entries = readNotifications();
314
+ assert.equal(entries.length, 1);
315
+ assert.equal(entries[0].kind, "auto-stop");
316
+ assert.equal(entries[0].scope, "M005");
317
+ });
318
+
319
+ test("dedup keys on kind+scope when present, not on prose", () => {
320
+ initNotificationStore(tmp);
321
+ appendNotification("Auto-mode blocked — validation gate", "warning", "notify", { kind: "auto-stop", scope: "M005" });
322
+ // Rephrased message, same structured identity → deduped within the window
323
+ appendNotification("Auto-mode blocked — gate rejected", "warning", "notify", { kind: "auto-stop", scope: "M005" });
324
+ // Same kind, different scope → distinct
325
+ appendNotification("Auto-mode blocked — validation gate", "warning", "notify", { kind: "auto-stop", scope: "M006" });
326
+
327
+ assert.equal(readNotifications().length, 2);
328
+ });
329
+
330
+ test("readNotifications filters by kind and scope", () => {
331
+ initNotificationStore(tmp);
332
+ appendNotification("a", "info", "notify", { kind: "auto-stop", scope: "M005" });
333
+ appendNotification("b", "info", "notify", { kind: "provider-error-pause", scope: "M005" });
334
+ appendNotification("c", "info");
335
+
336
+ assert.equal(readNotifications(tmp, { kind: "auto-stop" }).length, 1);
337
+ assert.equal(readNotifications(tmp, { scope: "M005" }).length, 2);
338
+ assert.equal(readNotifications(tmp, { kind: "provider-error-pause", scope: "M005" })[0].message, "b");
339
+ });
340
+
309
341
  test("listeners are notified on append, markAllRead, and clear", () => {
310
342
  initNotificationStore(tmp);
311
343
  let calls = 0;
@@ -0,0 +1,167 @@
1
+ /**
2
+ * OAuth/subscription vs pay-per-token API routing for bare model IDs.
3
+ *
4
+ * When the same model ID (e.g. gpt-5.5) exists on multiple providers, resolveModelId
5
+ * must prefer subscription/OAuth routes over platform API keys.
6
+ */
7
+
8
+ import test from "node:test";
9
+ import assert from "node:assert/strict";
10
+ import { MODELS } from "../../../../../packages/pi-ai/dist/models.generated.js";
11
+ import {
12
+ BARE_ID_SUBSCRIPTION_PROVIDER_PRECEDENCE,
13
+ resolveModelId,
14
+ } from "../auto-model-selection.js";
15
+
16
+ type ModelRef = { id: string; provider: string };
17
+
18
+ function modelsForProviders(modelId: string, providers: string[]): ModelRef[] {
19
+ return providers.map((provider) => ({ id: modelId, provider }));
20
+ }
21
+
22
+ /** Table-driven precedence cases — one row per conflict shape, not per model ID. */
23
+ const PRECEDENCE_CASES: Array<{
24
+ label: string;
25
+ modelId: string;
26
+ providers: string[];
27
+ expected: string;
28
+ currentProvider?: string;
29
+ }> = [
30
+ {
31
+ label: "ChatGPT OAuth beats OpenAI API (gpt-5.5)",
32
+ modelId: "gpt-5.5",
33
+ providers: ["openai", "openai-codex"],
34
+ expected: "openai-codex",
35
+ },
36
+ {
37
+ label: "Codex beats Copilot beats OpenAI API (gpt-5.5 triple)",
38
+ modelId: "gpt-5.5",
39
+ providers: ["openai", "github-copilot", "openai-codex"],
40
+ expected: "openai-codex",
41
+ },
42
+ {
43
+ label: "Copilot OAuth beats OpenAI API when no Codex (gpt-5.5)",
44
+ modelId: "gpt-5.5",
45
+ providers: ["openai", "github-copilot"],
46
+ expected: "github-copilot",
47
+ },
48
+ {
49
+ label: "Copilot OAuth beats OpenAI API (gpt-5-mini)",
50
+ modelId: "gpt-5-mini",
51
+ providers: ["openai", "github-copilot"],
52
+ expected: "github-copilot",
53
+ },
54
+ {
55
+ label: "Anthropic beats Copilot for Claude (claude-sonnet-4-6)",
56
+ modelId: "claude-sonnet-4-6",
57
+ providers: ["anthropic", "github-copilot"],
58
+ expected: "anthropic",
59
+ },
60
+ {
61
+ label: "Copilot beats Google API for Gemini (gemini-2.5-pro)",
62
+ modelId: "gemini-2.5-pro",
63
+ providers: ["google", "github-copilot"],
64
+ expected: "github-copilot",
65
+ },
66
+ {
67
+ label: "Gemini CLI beats Google API (gemini-2.5-pro)",
68
+ modelId: "gemini-2.5-pro",
69
+ providers: ["google", "google-gemini-cli"],
70
+ expected: "google-gemini-cli",
71
+ },
72
+ {
73
+ label: "Gemini CLI beats Copilot beats Google API",
74
+ modelId: "gemini-2.5-pro",
75
+ providers: ["google", "github-copilot", "google-gemini-cli"],
76
+ expected: "google-gemini-cli",
77
+ },
78
+ {
79
+ label: "Session provider still wins (openai-codex explicit)",
80
+ modelId: "gpt-5.5",
81
+ providers: ["openai", "openai-codex", "github-copilot"],
82
+ expected: "github-copilot",
83
+ currentProvider: "github-copilot",
84
+ },
85
+ {
86
+ label: "claude-code session beats anthropic (#3772)",
87
+ modelId: "claude-sonnet-4-6",
88
+ providers: ["anthropic", "claude-code"],
89
+ expected: "claude-code",
90
+ currentProvider: "claude-code",
91
+ },
92
+ ];
93
+
94
+ for (const row of PRECEDENCE_CASES) {
95
+ test(`resolveModelId precedence: ${row.label}`, () => {
96
+ const available = modelsForProviders(row.modelId, row.providers);
97
+ const result = resolveModelId(row.modelId, available, row.currentProvider);
98
+ assert.ok(result, `expected a match for ${row.modelId}`);
99
+ assert.equal(result.provider, row.expected);
100
+ });
101
+ }
102
+
103
+ test("BARE_ID_SUBSCRIPTION_PROVIDER_PRECEDENCE covers known OAuth/subscription providers", () => {
104
+ const expected = [
105
+ "openai-codex",
106
+ "google-gemini-cli",
107
+ "anthropic",
108
+ "github-copilot",
109
+ "google-antigravity",
110
+ ];
111
+ assert.deepEqual([...BARE_ID_SUBSCRIPTION_PROVIDER_PRECEDENCE], expected);
112
+ });
113
+
114
+ test("every catalog overlap with OAuth/subscription + API resolves to subscription route", () => {
115
+ const oauthProviders = new Set(["anthropic", "github-copilot", "openai-codex"]);
116
+ const subscriptionCli = new Set(["google-gemini-cli", "google-antigravity", "claude-code"]);
117
+ const apiPayPerToken = new Set(["openai", "google", "azure-openai-responses"]);
118
+
119
+ const byId = new Map<string, Set<string>>();
120
+ for (const [provider, models] of Object.entries(MODELS)) {
121
+ for (const id of Object.keys(models)) {
122
+ if (!byId.has(id)) byId.set(id, new Set());
123
+ byId.get(id)!.add(provider);
124
+ }
125
+ }
126
+
127
+ const failures: string[] = [];
128
+
129
+ for (const [modelId, providers] of byId) {
130
+ if (providers.size < 2) continue;
131
+
132
+ const hasApi = [...providers].some((p) => apiPayPerToken.has(p));
133
+ const subscriptionCandidates = [...providers].filter(
134
+ (p) => oauthProviders.has(p) || subscriptionCli.has(p),
135
+ );
136
+ if (!hasApi || subscriptionCandidates.length === 0) continue;
137
+
138
+ const testProviders = new Set<string>(subscriptionCandidates);
139
+ for (const p of providers) {
140
+ if (apiPayPerToken.has(p)) testProviders.add(p);
141
+ }
142
+
143
+ const available = modelsForProviders(modelId, [...testProviders]);
144
+ const result = resolveModelId(modelId, available, undefined);
145
+ if (!result) {
146
+ failures.push(`${modelId}: no resolution among ${[...testProviders].join(", ")}`);
147
+ continue;
148
+ }
149
+
150
+ const winnerIsSubscription =
151
+ oauthProviders.has(result.provider) ||
152
+ subscriptionCli.has(result.provider) ||
153
+ result.provider === "anthropic";
154
+
155
+ if (!winnerIsSubscription) {
156
+ failures.push(
157
+ `${modelId}: got ${result.provider}, expected subscription route among ${[...testProviders].join(", ")}`,
158
+ );
159
+ }
160
+ }
161
+
162
+ assert.equal(
163
+ failures.length,
164
+ 0,
165
+ `catalog overlap routing failures:\n${failures.join("\n")}`,
166
+ );
167
+ });
@@ -41,6 +41,7 @@ after(() => {
41
41
 
42
42
  const { resolveDispatch } = await import("../auto-dispatch.ts");
43
43
  const { buildParallelResearchSlicesPrompt } = await import("../auto-prompts.ts");
44
+ const { openDatabase, closeDatabase, insertMilestone, insertSlice } = await import("../gsd-db.ts");
44
45
 
45
46
  type DispatchState = Parameters<typeof resolveDispatch>[0]["state"];
46
47
 
@@ -51,6 +52,20 @@ function writeRoadmap(
51
52
  ): void {
52
53
  const milestoneDir = join(base, ".gsd", "milestones", mid);
53
54
  mkdirSync(milestoneDir, { recursive: true });
55
+ // The dispatch rule reads slices from the DB (ADR-017), not the ROADMAP
56
+ // projection — seed both so disk-based artifact checks still resolve.
57
+ insertMilestone({ id: mid, title: "Parallel Research Milestone" });
58
+ let sequence = 1;
59
+ for (const s of slices) {
60
+ insertSlice({
61
+ id: s.id,
62
+ milestoneId: mid,
63
+ title: s.title,
64
+ status: s.done ? "complete" : "pending",
65
+ depends: s.depends ?? [],
66
+ sequence: sequence++,
67
+ });
68
+ }
54
69
  const lines = [
55
70
  `# ${mid}: Parallel Research Milestone`,
56
71
  "",
@@ -100,9 +115,12 @@ describe("parallel-research-slices dispatch rule", () => {
100
115
 
101
116
  beforeEach(() => {
102
117
  base = mkdtempSync(join(tmpdir(), "parallel-research-"));
118
+ mkdirSync(join(base, ".gsd"), { recursive: true });
119
+ openDatabase(join(base, ".gsd", "gsd.db"));
103
120
  });
104
121
 
105
122
  afterEach(() => {
123
+ closeDatabase();
106
124
  rmSync(base, { recursive: true, force: true });
107
125
  });
108
126
 
@@ -0,0 +1,139 @@
1
+ // Structural invariant: parsers-legacy is banned from decision paths (ADR-017).
2
+ //
3
+ // The DB is the single source of truth; `.gsd/*.md` files are projections.
4
+ // Dispatch/gate/completion code must read state via gsd-db queries (e.g.
5
+ // getMilestoneSliceSummaries), never by parsing markdown projections.
6
+ //
7
+ // Two assertions:
8
+ // 1. Decision-path modules must NOT import parsers-legacy (hard ban).
9
+ // 2. Every other importer must be on the explicit allowlist below, each with
10
+ // a one-line justification. When this test fails, do not extend the
11
+ // allowlist for a decision path — add/extend a query in db/queries.ts and
12
+ // read the DB instead.
13
+
14
+ import test from "node:test";
15
+ import assert from "node:assert/strict";
16
+ import { readFileSync, readdirSync } from "node:fs";
17
+ import { join, relative } from "node:path";
18
+
19
+ const extensionsDir = join(process.cwd(), "src/resources/extensions");
20
+
21
+ // Modules that make dispatch/gate/completion decisions. Importing
22
+ // parsers-legacy here is always a violation, allowlist or not.
23
+ const BANNED_DECISION_PATHS = new Set([
24
+ "gsd/auto-direct-dispatch.ts",
25
+ "gsd/dispatch-guard.ts",
26
+ "gsd/auto-verification.ts",
27
+ "gsd/auto-dispatch.ts",
28
+ "gsd/auto-post-unit.ts",
29
+ "gsd/milestone-closeout.ts",
30
+ "gsd/auto/phases.ts",
31
+ "gsd/auto/orchestrator.ts",
32
+ "gsd/auto/loop.ts",
33
+ "gsd/tools/complete-slice.ts",
34
+ ]);
35
+
36
+ // Legitimate importers. Each entry carries its justification; anything not
37
+ // listed here (and not under a tests/ directory) fails the test.
38
+ const ALLOWED_IMPORTERS = new Set([
39
+ // migration/import: parses markdown to populate the DB
40
+ "gsd/md-importer.ts",
41
+ "gsd/workflow-migration.ts",
42
+ "gsd/migration-auto-check.ts",
43
+ // drift detection: compares markdown projection against DB by design
44
+ "gsd/state-reconciliation/drift/roadmap.ts",
45
+ // stale-render detection + render verification helpers over rendered output
46
+ "gsd/markdown-renderer.ts",
47
+ // pre-migration fallback: deriveState must work before the DB exists
48
+ "gsd/state.ts",
49
+ // explicit degraded-mode fallback when DB has no task rows (warns on use)
50
+ "gsd/reactive-graph.ts",
51
+ // recovery path: explicit pre-migration/DB-unavailable fallback branches
52
+ "gsd/auto-recovery.ts",
53
+ // diagnostics-only surfaces: report on projections, make no dispatch decisions
54
+ "gsd/doctor.ts",
55
+ "gsd/doctor-git-checks.ts",
56
+ // display/telemetry-only surfaces
57
+ "gsd/workspace-index.ts",
58
+ "gsd/visualizer-data.ts",
59
+ // prompt context text (display strings injected into unit prompts)
60
+ "gsd/auto-prompts.ts",
61
+ // cold-path maintenance command (branch cleanup messaging)
62
+ "gsd/commands-maintenance.ts",
63
+ // display-only GitHub issue/PR body sync
64
+ "github-sync/sync.ts",
65
+ ]);
66
+
67
+ const IMPORT_RE = /from\s+["'][^"']*parsers-legacy(?:\.js)?["']|import\(\s*["'][^"']*parsers-legacy(?:\.js)?["']\s*\)|require\(\s*["'][^"']*parsers-legacy(?:\.js)?["']\s*\)/;
68
+
69
+ function walkTsFiles(root: string): string[] {
70
+ const out: string[] = [];
71
+ const stack: string[] = [root];
72
+
73
+ while (stack.length > 0) {
74
+ const dir = stack.pop()!;
75
+ let entries;
76
+ try {
77
+ entries = readdirSync(dir, { withFileTypes: true });
78
+ } catch {
79
+ continue;
80
+ }
81
+ for (const ent of entries) {
82
+ const full = join(dir, ent.name);
83
+ if (ent.isDirectory()) {
84
+ if (ent.name === "tests" || ent.name === "node_modules") continue;
85
+ stack.push(full);
86
+ continue;
87
+ }
88
+ if (!ent.isFile()) continue;
89
+ if (!ent.name.endsWith(".ts")) continue;
90
+ if (ent.name.startsWith(".")) continue;
91
+ if (ent.name.endsWith(".test.ts")) continue;
92
+ out.push(full);
93
+ }
94
+ }
95
+ return out;
96
+ }
97
+
98
+ function findImporters(): string[] {
99
+ const importers: string[] = [];
100
+ for (const file of walkTsFiles(extensionsDir)) {
101
+ const rel = relative(extensionsDir, file).split("\\").join("/");
102
+ if (rel === "gsd/parsers-legacy.ts") continue; // the module itself
103
+ const content = readFileSync(file, "utf-8");
104
+ if (IMPORT_RE.test(content)) importers.push(rel);
105
+ }
106
+ return importers.sort();
107
+ }
108
+
109
+ test("decision-path modules do not import parsers-legacy (ADR-017)", () => {
110
+ const violations = findImporters().filter((rel) => BANNED_DECISION_PATHS.has(rel));
111
+ assert.deepEqual(
112
+ violations,
113
+ [],
114
+ `Decision-path modules must read the DB (db/queries.ts, e.g. getMilestoneSliceSummaries), ` +
115
+ `not parse .gsd/*.md projections. Violations:\n ${violations.join("\n ")}`,
116
+ );
117
+ });
118
+
119
+ test("every parsers-legacy importer is on the explicit allowlist", () => {
120
+ const unexpected = findImporters().filter((rel) => !ALLOWED_IMPORTERS.has(rel));
121
+ assert.deepEqual(
122
+ unexpected,
123
+ [],
124
+ `New parsers-legacy importer(s) detected:\n ${unexpected.join("\n ")}\n` +
125
+ `If this is migration/drift/display-only code, add it to ALLOWED_IMPORTERS ` +
126
+ `with a one-line justification. If it makes dispatch/gate/completion ` +
127
+ `decisions, read the DB instead (db/queries.ts).`,
128
+ );
129
+ });
130
+
131
+ test("allowlist has no stale entries", () => {
132
+ const importers = new Set(findImporters());
133
+ const stale = [...ALLOWED_IMPORTERS].filter((rel) => !importers.has(rel));
134
+ assert.deepEqual(
135
+ stale,
136
+ [],
137
+ `Allowlist entries no longer import parsers-legacy — remove them:\n ${stale.join("\n ")}`,
138
+ );
139
+ });