@opengsd/gsd-pi 1.2.0-dev.e8563f58 → 1.2.0-dev.fbdca60b

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 (432) hide show
  1. package/dist/cli-model-override.d.ts +15 -0
  2. package/dist/cli-model-override.js +21 -0
  3. package/dist/cli.js +1 -18
  4. package/dist/loader.js +6 -4
  5. package/dist/register-agent-bundles.d.ts +11 -2
  6. package/dist/register-agent-bundles.js +18 -4
  7. package/dist/resource-loader.d.ts +10 -5
  8. package/dist/resource-loader.js +121 -6
  9. package/dist/resources/.managed-resources-content-hash +1 -1
  10. package/dist/resources/extensions/ask-user-questions.js +3 -2
  11. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +447 -215
  12. package/dist/resources/extensions/claude-code-cli/turn-assembler.js +33 -1
  13. package/dist/resources/extensions/gsd/auto/closeout.js +215 -0
  14. package/dist/resources/extensions/gsd/auto/dispatch-history.js +21 -6
  15. package/dist/resources/extensions/gsd/auto/dispatch.js +365 -0
  16. package/dist/resources/extensions/gsd/auto/finalize.js +347 -0
  17. package/dist/resources/extensions/gsd/auto/loop.js +4 -1
  18. package/dist/resources/extensions/gsd/auto/milestone-lease-reclaim.js +56 -0
  19. package/dist/resources/extensions/gsd/auto/orchestrator.js +85 -15
  20. package/dist/resources/extensions/gsd/auto/phase-helpers.js +146 -0
  21. package/dist/resources/extensions/gsd/auto/phases.js +17 -2329
  22. package/dist/resources/extensions/gsd/auto/pre-dispatch.js +534 -0
  23. package/dist/resources/extensions/gsd/auto/session.js +3 -0
  24. package/dist/resources/extensions/gsd/auto/unit-phase.js +694 -0
  25. package/dist/resources/extensions/gsd/auto/workflow-unit-dispatch.js +1 -1
  26. package/dist/resources/extensions/gsd/auto/worktree-safety-phase.js +125 -0
  27. package/dist/resources/extensions/gsd/auto-direct-dispatch.js +3 -2
  28. package/dist/resources/extensions/gsd/auto-dispatch.js +11 -2
  29. package/dist/resources/extensions/gsd/auto-post-unit.js +18 -6
  30. package/dist/resources/extensions/gsd/auto-start.js +23 -3
  31. package/dist/resources/extensions/gsd/auto-unit-closeout.js +45 -21
  32. package/dist/resources/extensions/gsd/auto-verification.js +14 -2
  33. package/dist/resources/extensions/gsd/auto-worktree.js +15 -2
  34. package/dist/resources/extensions/gsd/auto.js +45 -2
  35. package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +37 -7
  36. package/dist/resources/extensions/gsd/commands/context.js +16 -2
  37. package/dist/resources/extensions/gsd/commands-mcp-status.js +2 -2
  38. package/dist/resources/extensions/gsd/commands-workflow-templates.js +9 -2
  39. package/dist/resources/extensions/gsd/crash-recovery.js +8 -3
  40. package/dist/resources/extensions/gsd/db/engine.js +24 -6
  41. package/dist/resources/extensions/gsd/db/queries.js +30 -0
  42. package/dist/resources/extensions/gsd/db-migration-backup.js +51 -8
  43. package/dist/resources/extensions/gsd/db-transaction.js +27 -23
  44. package/dist/resources/extensions/gsd/db-writer.js +8 -17
  45. package/dist/resources/extensions/gsd/doctor-engine-checks.js +5 -5
  46. package/dist/resources/extensions/gsd/doctor-environment.js +256 -125
  47. package/dist/resources/extensions/gsd/gsd-db.js +15 -20
  48. package/dist/resources/extensions/gsd/guided-flow.js +93 -4
  49. package/dist/resources/extensions/gsd/health-widget.js +87 -28
  50. package/dist/resources/extensions/gsd/mcp-bridge.js +10 -0
  51. package/dist/resources/extensions/gsd/memory-relations.js +1 -1
  52. package/dist/resources/extensions/gsd/milestone-planning-persistence.js +2 -2
  53. package/dist/resources/extensions/gsd/milestone-reopen-events.js +3 -5
  54. package/dist/resources/extensions/gsd/milestone-settlement.js +2 -2
  55. package/dist/resources/extensions/gsd/notifications.js +12 -7
  56. package/dist/resources/extensions/gsd/projection-flush.js +7 -0
  57. package/dist/resources/extensions/gsd/prompts/complete-slice.md +2 -2
  58. package/dist/resources/extensions/gsd/prompts/execute-task.md +3 -2
  59. package/dist/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
  60. package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  61. package/dist/resources/extensions/gsd/prompts/quick-task.md +1 -1
  62. package/dist/resources/extensions/gsd/prompts/reassess-roadmap.md +1 -1
  63. package/dist/resources/extensions/gsd/prompts/refine-slice.md +1 -1
  64. package/dist/resources/extensions/gsd/prompts/replan-slice.md +1 -1
  65. package/dist/resources/extensions/gsd/prompts/research-milestone.md +1 -1
  66. package/dist/resources/extensions/gsd/prompts/research-slice.md +1 -1
  67. package/dist/resources/extensions/gsd/prompts/rewrite-docs.md +1 -1
  68. package/dist/resources/extensions/gsd/prompts/run-uat.md +3 -1
  69. package/dist/resources/extensions/gsd/prompts/triage-captures.md +1 -1
  70. package/dist/resources/extensions/gsd/prompts/validate-milestone.md +1 -1
  71. package/dist/resources/extensions/gsd/prompts/workflow-start.md +2 -1
  72. package/dist/resources/extensions/gsd/roadmap-slices.js +25 -3
  73. package/dist/resources/extensions/gsd/session-lock.js +1 -1
  74. package/dist/resources/extensions/gsd/skill-activation.js +3 -6
  75. package/dist/resources/extensions/gsd/state.js +6 -2
  76. package/dist/resources/extensions/gsd/tool-contract.js +14 -3
  77. package/dist/resources/extensions/gsd/tool-surface-readiness.js +83 -31
  78. package/dist/resources/extensions/gsd/tools/complete-milestone.js +3 -2
  79. package/dist/resources/extensions/gsd/tools/complete-slice.js +2 -2
  80. package/dist/resources/extensions/gsd/tools/complete-task.js +65 -2
  81. package/dist/resources/extensions/gsd/tools/plan-slice.js +2 -2
  82. package/dist/resources/extensions/gsd/tools/plan-task.js +2 -2
  83. package/dist/resources/extensions/gsd/tools/reassess-roadmap.js +2 -2
  84. package/dist/resources/extensions/gsd/tools/reopen-milestone.js +2 -2
  85. package/dist/resources/extensions/gsd/tools/reopen-slice.js +2 -2
  86. package/dist/resources/extensions/gsd/tools/reopen-task.js +2 -2
  87. package/dist/resources/extensions/gsd/tools/replan-slice.js +2 -2
  88. package/dist/resources/extensions/gsd/unit-context-composer.js +1 -1
  89. package/dist/resources/extensions/gsd/unit-registry.js +34 -4
  90. package/dist/resources/extensions/gsd/verification-verdict.js +2 -1
  91. package/dist/resources/extensions/gsd/workflow-event-ledger.js +91 -0
  92. package/dist/resources/extensions/gsd/workflow-event-vocabulary.js +46 -0
  93. package/dist/resources/extensions/gsd/workflow-events.js +6 -18
  94. package/dist/resources/extensions/gsd/workflow-mcp-auto-prep.js +2 -0
  95. package/dist/resources/extensions/gsd/workflow-mcp-readiness-cache.js +105 -0
  96. package/dist/resources/extensions/gsd/workflow-reconcile.js +21 -56
  97. package/dist/resources/extensions/gsd/worktree-manager.js +7 -1
  98. package/dist/resources/extensions/gsd/worktree-safety.js +28 -26
  99. package/dist/resources/extensions/gsd/worktree.js +8 -1
  100. package/dist/resources/extensions/mcp-client/manager.js +6 -1
  101. package/dist/resources/skills/create-skill/SKILL.md +3 -0
  102. package/dist/resources/skills/create-skill/references/skill-structure.md +1 -0
  103. package/dist/runtime-checks.d.ts +10 -0
  104. package/dist/runtime-checks.js +27 -0
  105. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  106. package/dist/web/standalone/.next/BUILD_ID +1 -1
  107. package/dist/web/standalone/.next/app-path-routes-manifest.json +7 -7
  108. package/dist/web/standalone/.next/build-manifest.json +2 -2
  109. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  110. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  111. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  112. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  113. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  114. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  115. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  116. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  117. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  118. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  119. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  120. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  121. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  122. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  123. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  124. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  125. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  126. package/dist/web/standalone/.next/server/app/api/boot/route.js.nft.json +1 -1
  127. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js.nft.json +1 -1
  128. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js.nft.json +1 -1
  129. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js.nft.json +1 -1
  130. package/dist/web/standalone/.next/server/app/api/captures/route.js.nft.json +1 -1
  131. package/dist/web/standalone/.next/server/app/api/cleanup/route.js.nft.json +1 -1
  132. package/dist/web/standalone/.next/server/app/api/doctor/route.js.nft.json +1 -1
  133. package/dist/web/standalone/.next/server/app/api/export-data/route.js.nft.json +1 -1
  134. package/dist/web/standalone/.next/server/app/api/files/route.js.nft.json +1 -1
  135. package/dist/web/standalone/.next/server/app/api/forensics/route.js.nft.json +1 -1
  136. package/dist/web/standalone/.next/server/app/api/git/route.js.nft.json +1 -1
  137. package/dist/web/standalone/.next/server/app/api/history/route.js.nft.json +1 -1
  138. package/dist/web/standalone/.next/server/app/api/hooks/route.js.nft.json +1 -1
  139. package/dist/web/standalone/.next/server/app/api/inspect/route.js.nft.json +1 -1
  140. package/dist/web/standalone/.next/server/app/api/knowledge/route.js.nft.json +1 -1
  141. package/dist/web/standalone/.next/server/app/api/live-state/route.js.nft.json +1 -1
  142. package/dist/web/standalone/.next/server/app/api/mcp-connections/route.js.nft.json +1 -1
  143. package/dist/web/standalone/.next/server/app/api/notifications/route.js.nft.json +1 -1
  144. package/dist/web/standalone/.next/server/app/api/onboarding/route.js.nft.json +1 -1
  145. package/dist/web/standalone/.next/server/app/api/projects/route.js.nft.json +1 -1
  146. package/dist/web/standalone/.next/server/app/api/recovery/route.js.nft.json +1 -1
  147. package/dist/web/standalone/.next/server/app/api/session/browser/route.js.nft.json +1 -1
  148. package/dist/web/standalone/.next/server/app/api/session/command/route.js.nft.json +1 -1
  149. package/dist/web/standalone/.next/server/app/api/session/events/route.js.nft.json +1 -1
  150. package/dist/web/standalone/.next/server/app/api/session/manage/route.js.nft.json +1 -1
  151. package/dist/web/standalone/.next/server/app/api/settings-data/route.js.nft.json +1 -1
  152. package/dist/web/standalone/.next/server/app/api/shutdown/route.js.nft.json +1 -1
  153. package/dist/web/standalone/.next/server/app/api/skill-health/route.js.nft.json +1 -1
  154. package/dist/web/standalone/.next/server/app/api/steer/route.js.nft.json +1 -1
  155. package/dist/web/standalone/.next/server/app/api/switch-root/route.js.nft.json +1 -1
  156. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js.nft.json +1 -1
  157. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js.nft.json +1 -1
  158. package/dist/web/standalone/.next/server/app/api/undo/route.js.nft.json +1 -1
  159. package/dist/web/standalone/.next/server/app/api/visualizer/route.js.nft.json +1 -1
  160. package/dist/web/standalone/.next/server/app/index.html +1 -1
  161. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  162. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  163. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  164. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  165. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  166. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  167. package/dist/web/standalone/.next/server/app-paths-manifest.json +7 -7
  168. package/dist/web/standalone/.next/server/chunks/{5942.js → 1128.js} +1 -1
  169. package/dist/web/standalone/.next/server/chunks/8357.js +1 -1
  170. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  171. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  172. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  173. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  174. package/dist/web/standalone/node_modules/node-pty/build/Makefile +1 -1
  175. package/package.json +3 -3
  176. package/packages/cloud-mcp-gateway/package.json +2 -2
  177. package/packages/contracts/package.json +1 -1
  178. package/packages/daemon/package.json +4 -4
  179. package/packages/gsd-agent-core/dist/sdk.d.ts.map +1 -1
  180. package/packages/gsd-agent-core/dist/sdk.js +6 -4
  181. package/packages/gsd-agent-core/dist/sdk.js.map +1 -1
  182. package/packages/gsd-agent-core/package.json +5 -5
  183. package/packages/gsd-agent-modes/dist/modes/interactive/components/settings-selector.d.ts +2 -0
  184. package/packages/gsd-agent-modes/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  185. package/packages/gsd-agent-modes/dist/modes/interactive/components/settings-selector.js +10 -0
  186. package/packages/gsd-agent-modes/dist/modes/interactive/components/settings-selector.js.map +1 -1
  187. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts +8 -0
  188. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  189. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js +50 -6
  190. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js.map +1 -1
  191. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.d.ts +2 -0
  192. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.d.ts.map +1 -1
  193. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js +34 -5
  194. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js.map +1 -1
  195. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.d.ts +1 -0
  196. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  197. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.js +17 -0
  198. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.js.map +1 -1
  199. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-settings.d.ts.map +1 -1
  200. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-settings.js +4 -0
  201. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-settings.js.map +1 -1
  202. package/packages/gsd-agent-modes/package.json +7 -7
  203. package/packages/mcp-server/README.md +12 -3
  204. package/packages/mcp-server/dist/cli-runner.d.ts +40 -0
  205. package/packages/mcp-server/dist/cli-runner.d.ts.map +1 -0
  206. package/packages/mcp-server/dist/cli-runner.js +137 -0
  207. package/packages/mcp-server/dist/cli-runner.js.map +1 -0
  208. package/packages/mcp-server/dist/cli.js +2 -53
  209. package/packages/mcp-server/dist/cli.js.map +1 -1
  210. package/packages/mcp-server/dist/pid-registry.d.ts +46 -0
  211. package/packages/mcp-server/dist/pid-registry.d.ts.map +1 -0
  212. package/packages/mcp-server/dist/pid-registry.js +459 -0
  213. package/packages/mcp-server/dist/pid-registry.js.map +1 -0
  214. package/packages/mcp-server/dist/probe-mode.d.ts +4 -0
  215. package/packages/mcp-server/dist/probe-mode.d.ts.map +1 -0
  216. package/packages/mcp-server/dist/probe-mode.js +10 -0
  217. package/packages/mcp-server/dist/probe-mode.js.map +1 -0
  218. package/packages/mcp-server/dist/stdio-watchdog.d.ts +8 -0
  219. package/packages/mcp-server/dist/stdio-watchdog.d.ts.map +1 -0
  220. package/packages/mcp-server/dist/stdio-watchdog.js +40 -0
  221. package/packages/mcp-server/dist/stdio-watchdog.js.map +1 -0
  222. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  223. package/packages/mcp-server/dist/workflow-tools.js +62 -43
  224. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  225. package/packages/mcp-server/package.json +5 -5
  226. package/packages/native/package.json +1 -1
  227. package/packages/pi-agent-core/dist/agent-loop.js +43 -2
  228. package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
  229. package/packages/pi-agent-core/package.json +1 -1
  230. package/packages/pi-ai/package.json +1 -1
  231. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +3 -0
  232. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
  233. package/packages/pi-coding-agent/dist/core/settings-manager.js +11 -0
  234. package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
  235. package/packages/pi-coding-agent/dist/theme/theme.d.ts.map +1 -1
  236. package/packages/pi-coding-agent/dist/theme/theme.js +45 -17
  237. package/packages/pi-coding-agent/dist/theme/theme.js.map +1 -1
  238. package/packages/pi-coding-agent/package.json +7 -7
  239. package/packages/pi-tui/README.md +15 -0
  240. package/packages/pi-tui/dist/index.d.ts +2 -2
  241. package/packages/pi-tui/dist/index.d.ts.map +1 -1
  242. package/packages/pi-tui/dist/index.js +2 -2
  243. package/packages/pi-tui/dist/index.js.map +1 -1
  244. package/packages/pi-tui/dist/terminal-image.d.ts +33 -0
  245. package/packages/pi-tui/dist/terminal-image.d.ts.map +1 -1
  246. package/packages/pi-tui/dist/terminal-image.js +54 -2
  247. package/packages/pi-tui/dist/terminal-image.js.map +1 -1
  248. package/packages/pi-tui/dist/terminal.d.ts +12 -0
  249. package/packages/pi-tui/dist/terminal.d.ts.map +1 -1
  250. package/packages/pi-tui/dist/terminal.js +70 -25
  251. package/packages/pi-tui/dist/terminal.js.map +1 -1
  252. package/packages/pi-tui/dist/tui.d.ts +15 -0
  253. package/packages/pi-tui/dist/tui.d.ts.map +1 -1
  254. package/packages/pi-tui/dist/tui.js +106 -21
  255. package/packages/pi-tui/dist/tui.js.map +1 -1
  256. package/packages/pi-tui/dist/utils.d.ts.map +1 -1
  257. package/packages/pi-tui/dist/utils.js +110 -36
  258. package/packages/pi-tui/dist/utils.js.map +1 -1
  259. package/packages/pi-tui/package.json +2 -2
  260. package/packages/rpc-client/package.json +2 -2
  261. package/pkg/dist/theme/theme.d.ts.map +1 -1
  262. package/pkg/dist/theme/theme.js +45 -17
  263. package/pkg/dist/theme/theme.js.map +1 -1
  264. package/pkg/package.json +1 -1
  265. package/src/resources/extensions/ask-user-questions.ts +7 -2
  266. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +531 -226
  267. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +672 -7
  268. package/src/resources/extensions/claude-code-cli/turn-assembler.ts +38 -1
  269. package/src/resources/extensions/gsd/auto/closeout.ts +309 -0
  270. package/src/resources/extensions/gsd/auto/dispatch-history.ts +22 -6
  271. package/src/resources/extensions/gsd/auto/dispatch.ts +449 -0
  272. package/src/resources/extensions/gsd/auto/finalize.ts +445 -0
  273. package/src/resources/extensions/gsd/auto/loop.ts +4 -1
  274. package/src/resources/extensions/gsd/auto/milestone-lease-reclaim.ts +74 -0
  275. package/src/resources/extensions/gsd/auto/orchestrator.ts +95 -15
  276. package/src/resources/extensions/gsd/auto/phase-helpers.ts +199 -0
  277. package/src/resources/extensions/gsd/auto/phases.ts +58 -3022
  278. package/src/resources/extensions/gsd/auto/pre-dispatch.ts +704 -0
  279. package/src/resources/extensions/gsd/auto/session.ts +3 -0
  280. package/src/resources/extensions/gsd/auto/unit-phase.ts +910 -0
  281. package/src/resources/extensions/gsd/auto/workflow-unit-dispatch.ts +1 -1
  282. package/src/resources/extensions/gsd/auto/worktree-safety-phase.ts +149 -0
  283. package/src/resources/extensions/gsd/auto-direct-dispatch.ts +10 -16
  284. package/src/resources/extensions/gsd/auto-dispatch.ts +11 -10
  285. package/src/resources/extensions/gsd/auto-post-unit.ts +21 -6
  286. package/src/resources/extensions/gsd/auto-start.ts +24 -4
  287. package/src/resources/extensions/gsd/auto-unit-closeout.ts +83 -28
  288. package/src/resources/extensions/gsd/auto-verification.ts +18 -2
  289. package/src/resources/extensions/gsd/auto-worktree.ts +15 -2
  290. package/src/resources/extensions/gsd/auto.ts +56 -2
  291. package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +56 -6
  292. package/src/resources/extensions/gsd/commands/context.ts +16 -2
  293. package/src/resources/extensions/gsd/commands-mcp-status.ts +2 -2
  294. package/src/resources/extensions/gsd/commands-workflow-templates.ts +11 -4
  295. package/src/resources/extensions/gsd/crash-recovery.ts +10 -2
  296. package/src/resources/extensions/gsd/db/engine.ts +26 -6
  297. package/src/resources/extensions/gsd/db/queries.ts +29 -0
  298. package/src/resources/extensions/gsd/db-migration-backup.ts +56 -7
  299. package/src/resources/extensions/gsd/db-transaction.ts +37 -20
  300. package/src/resources/extensions/gsd/db-writer.ts +11 -19
  301. package/src/resources/extensions/gsd/doctor-engine-checks.ts +5 -4
  302. package/src/resources/extensions/gsd/doctor-environment.ts +267 -142
  303. package/src/resources/extensions/gsd/gsd-db.ts +15 -19
  304. package/src/resources/extensions/gsd/guided-flow.ts +145 -24
  305. package/src/resources/extensions/gsd/health-widget.ts +91 -27
  306. package/src/resources/extensions/gsd/mcp-bridge.ts +39 -0
  307. package/src/resources/extensions/gsd/memory-relations.ts +1 -1
  308. package/src/resources/extensions/gsd/milestone-planning-persistence.ts +2 -2
  309. package/src/resources/extensions/gsd/milestone-reopen-events.ts +3 -6
  310. package/src/resources/extensions/gsd/milestone-settlement.ts +2 -2
  311. package/src/resources/extensions/gsd/notifications.ts +13 -6
  312. package/src/resources/extensions/gsd/projection-flush.ts +20 -0
  313. package/src/resources/extensions/gsd/prompts/complete-slice.md +2 -2
  314. package/src/resources/extensions/gsd/prompts/execute-task.md +3 -2
  315. package/src/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
  316. package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  317. package/src/resources/extensions/gsd/prompts/quick-task.md +1 -1
  318. package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +1 -1
  319. package/src/resources/extensions/gsd/prompts/refine-slice.md +1 -1
  320. package/src/resources/extensions/gsd/prompts/replan-slice.md +1 -1
  321. package/src/resources/extensions/gsd/prompts/research-milestone.md +1 -1
  322. package/src/resources/extensions/gsd/prompts/research-slice.md +1 -1
  323. package/src/resources/extensions/gsd/prompts/rewrite-docs.md +1 -1
  324. package/src/resources/extensions/gsd/prompts/run-uat.md +3 -1
  325. package/src/resources/extensions/gsd/prompts/triage-captures.md +1 -1
  326. package/src/resources/extensions/gsd/prompts/validate-milestone.md +1 -1
  327. package/src/resources/extensions/gsd/prompts/workflow-start.md +2 -1
  328. package/src/resources/extensions/gsd/roadmap-slices.ts +28 -3
  329. package/src/resources/extensions/gsd/session-lock.ts +1 -1
  330. package/src/resources/extensions/gsd/skill-activation.ts +3 -6
  331. package/src/resources/extensions/gsd/state.ts +7 -1
  332. package/src/resources/extensions/gsd/tests/auto-abort-pause-regression.test.ts +1 -1
  333. package/src/resources/extensions/gsd/tests/auto-blocked-remediation-message.test.ts +1 -1
  334. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +206 -22
  335. package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +6 -1
  336. package/src/resources/extensions/gsd/tests/auto-orchestrator.test.ts +76 -12
  337. package/src/resources/extensions/gsd/tests/auto-pause-double-entry-guard.test.ts +1 -1
  338. package/src/resources/extensions/gsd/tests/auto-paused-ui-cleanup.test.ts +77 -1
  339. package/src/resources/extensions/gsd/tests/auto-phases-lifecycle.test.ts +2 -1
  340. package/src/resources/extensions/gsd/tests/auto-remote-session-lock-cleanup.test.ts +65 -3
  341. package/src/resources/extensions/gsd/tests/auto-start-orphan-bootstrap.test.ts +236 -0
  342. package/src/resources/extensions/gsd/tests/auto-unit-closeout.test.ts +169 -1
  343. package/src/resources/extensions/gsd/tests/complete-task.test.ts +141 -5
  344. package/src/resources/extensions/gsd/tests/db-migration-backup.test.ts +68 -19
  345. package/src/resources/extensions/gsd/tests/db-transaction.test.ts +59 -0
  346. package/src/resources/extensions/gsd/tests/db-writer.test.ts +15 -4
  347. package/src/resources/extensions/gsd/tests/deep-project-auto-loop.test.ts +2 -1
  348. package/src/resources/extensions/gsd/tests/derive-state-helpers.test.ts +62 -0
  349. package/src/resources/extensions/gsd/tests/discuss-routing-fixes.test.ts +12 -2
  350. package/src/resources/extensions/gsd/tests/dispatch-history.test.ts +55 -0
  351. package/src/resources/extensions/gsd/tests/dist-redirect.mjs +8 -0
  352. package/src/resources/extensions/gsd/tests/engine-interfaces-contract.test.ts +117 -91
  353. package/src/resources/extensions/gsd/tests/ensure-db-open.test.ts +113 -0
  354. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +19 -0
  355. package/src/resources/extensions/gsd/tests/guided-dispatch-root.test.ts +18 -6
  356. package/src/resources/extensions/gsd/tests/integration/auto-worktree.test.ts +15 -0
  357. package/src/resources/extensions/gsd/tests/integration/doctor-environment-async.test.ts +104 -0
  358. package/src/resources/extensions/gsd/tests/integration/run-uat.test.ts +18 -0
  359. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +47 -16
  360. package/src/resources/extensions/gsd/tests/mcp-readiness-preflight.test.ts +205 -0
  361. package/src/resources/extensions/gsd/tests/mcp-status.test.ts +6 -5
  362. package/src/resources/extensions/gsd/tests/milestone-merge-stash-restore.test.ts +1 -1
  363. package/src/resources/extensions/gsd/tests/milestone-report-path.test.ts +1 -1
  364. package/src/resources/extensions/gsd/tests/milestone-settlement.test.ts +92 -0
  365. package/src/resources/extensions/gsd/tests/milestone-transition-state-rebuild.test.ts +1 -1
  366. package/src/resources/extensions/gsd/tests/notifications.test.ts +64 -9
  367. package/src/resources/extensions/gsd/tests/parallel-skill-prompt-integration.test.ts +2 -2
  368. package/src/resources/extensions/gsd/tests/parsers-legacy-importers.test.ts +5 -0
  369. package/src/resources/extensions/gsd/tests/phases-merge-error-stops-auto.test.ts +1 -1
  370. package/src/resources/extensions/gsd/tests/phases-terminal-complete-idempotent.test.ts +242 -0
  371. package/src/resources/extensions/gsd/tests/plan-gate-failed-doctor-heal-hint.test.ts +3 -3
  372. package/src/resources/extensions/gsd/tests/post-exec-retry-bypass.test.ts +63 -2
  373. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +10 -2
  374. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +2 -4
  375. package/src/resources/extensions/gsd/tests/remote-notification-from-desktop.test.ts +31 -81
  376. package/src/resources/extensions/gsd/tests/roadmap-slices.test.ts +68 -0
  377. package/src/resources/extensions/gsd/tests/runtime-invariant-modules.test.ts +26 -2
  378. package/src/resources/extensions/gsd/tests/single-writer-invariant.test.ts +170 -48
  379. package/src/resources/extensions/gsd/tests/skill-activation.test.ts +20 -17
  380. package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +7 -3
  381. package/src/resources/extensions/gsd/tests/stop-auto-race-null-unit.test.ts +1 -1
  382. package/src/resources/extensions/gsd/tests/teardown-chdir-failure-clears-registry.test.ts +17 -0
  383. package/src/resources/extensions/gsd/tests/token-tool-gating.test.ts +4 -2
  384. package/src/resources/extensions/gsd/tests/tool-surface-readiness.test.ts +184 -10
  385. package/src/resources/extensions/gsd/tests/tool-unavailable-retry.test.ts +33 -0
  386. package/src/resources/extensions/gsd/tests/transport-gate-double-complete.test.ts +139 -0
  387. package/src/resources/extensions/gsd/tests/uok-audit-unified.test.ts +8 -0
  388. package/src/resources/extensions/gsd/tests/uok-plan-v2-wiring.test.ts +1 -1
  389. package/src/resources/extensions/gsd/tests/verification-verdict.test.ts +2 -0
  390. package/src/resources/extensions/gsd/tests/workflow-events.test.ts +19 -0
  391. package/src/resources/extensions/gsd/tests/workflow-mcp-readiness-cache.test.ts +119 -0
  392. package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +65 -2
  393. package/src/resources/extensions/gsd/tests/workflow-phase-contract-matrix.test.ts +332 -0
  394. package/src/resources/extensions/gsd/tests/workflow-reconcile.test.ts +20 -0
  395. package/src/resources/extensions/gsd/tests/workflow-templates.test.ts +92 -0
  396. package/src/resources/extensions/gsd/tests/worktree-health-dispatch.test.ts +1 -1
  397. package/src/resources/extensions/gsd/tests/worktree-project-root-degrade.test.ts +1 -1
  398. package/src/resources/extensions/gsd/tests/worktree-safety-phase.test.ts +100 -0
  399. package/src/resources/extensions/gsd/tests/worktree-safety.test.ts +72 -0
  400. package/src/resources/extensions/gsd/tests/worktree-teardown-safety.test.ts +22 -0
  401. package/src/resources/extensions/gsd/tests/worktree.test.ts +18 -0
  402. package/src/resources/extensions/gsd/tool-contract.ts +38 -3
  403. package/src/resources/extensions/gsd/tool-surface-readiness.ts +126 -19
  404. package/src/resources/extensions/gsd/tools/complete-milestone.ts +3 -2
  405. package/src/resources/extensions/gsd/tools/complete-slice.ts +2 -2
  406. package/src/resources/extensions/gsd/tools/complete-task.ts +90 -2
  407. package/src/resources/extensions/gsd/tools/plan-slice.ts +2 -2
  408. package/src/resources/extensions/gsd/tools/plan-task.ts +2 -2
  409. package/src/resources/extensions/gsd/tools/reassess-roadmap.ts +2 -2
  410. package/src/resources/extensions/gsd/tools/reopen-milestone.ts +2 -2
  411. package/src/resources/extensions/gsd/tools/reopen-slice.ts +2 -2
  412. package/src/resources/extensions/gsd/tools/reopen-task.ts +2 -2
  413. package/src/resources/extensions/gsd/tools/replan-slice.ts +2 -2
  414. package/src/resources/extensions/gsd/unit-context-composer.ts +1 -1
  415. package/src/resources/extensions/gsd/unit-registry.ts +34 -4
  416. package/src/resources/extensions/gsd/verification-verdict.ts +4 -2
  417. package/src/resources/extensions/gsd/workflow-event-ledger.ts +131 -0
  418. package/src/resources/extensions/gsd/workflow-event-vocabulary.ts +59 -0
  419. package/src/resources/extensions/gsd/workflow-events.ts +12 -20
  420. package/src/resources/extensions/gsd/workflow-mcp-auto-prep.ts +2 -0
  421. package/src/resources/extensions/gsd/workflow-mcp-readiness-cache.ts +150 -0
  422. package/src/resources/extensions/gsd/workflow-reconcile.ts +29 -62
  423. package/src/resources/extensions/gsd/worktree-manager.ts +6 -1
  424. package/src/resources/extensions/gsd/worktree-safety.ts +41 -39
  425. package/src/resources/extensions/gsd/worktree.ts +7 -1
  426. package/src/resources/extensions/mcp-client/manager.ts +7 -1
  427. package/src/resources/skills/create-skill/SKILL.md +3 -0
  428. package/src/resources/skills/create-skill/references/skill-structure.md +1 -0
  429. package/dist/resources/skills/gsd-browser/SKILL.md +0 -41
  430. package/src/resources/skills/gsd-browser/SKILL.md +0 -41
  431. /package/dist/web/standalone/.next/static/{LDHRKiRBIVZmiuMjrL1Vy → 2T9IOdiiM3o3gZ4UbPi8E}/_buildManifest.js +0 -0
  432. /package/dist/web/standalone/.next/static/{LDHRKiRBIVZmiuMjrL1Vy → 2T9IOdiiM3o3gZ4UbPi8E}/_ssgManifest.js +0 -0
@@ -5,103 +5,53 @@
5
5
  * as a fire-and-forget side-effect so that Telegram/Slack/Discord channels
6
6
  * receive the same events as native desktop notifications.
7
7
  *
8
- * Testing strategy (structural analysis):
9
- * node:test does not support mock.module without --experimental-test-module-mocks,
10
- * so we use the same source-code structural approach established in this codebase
11
- * (see session-start-footer.test.ts). We read notifications.ts and assert that:
12
- * 1. It imports sendRemoteNotification from the remote-questions/notify module.
13
- * 2. The sendDesktopNotification function body calls sendRemoteNotification
14
- * with title and message as arguments.
15
- * 3. The call uses the void fire-and-forget pattern with a .catch(() => {})
16
- * suppressor so that async failures never break the synchronous caller.
8
+ * Testing strategy (behavioral):
9
+ * Import sendDesktopNotification and the swappable remoteNotificationDispatcher.
10
+ * Mock the dispatcher's send method, call sendDesktopNotification, and assert
11
+ * that the dispatcher received the same title/message and that the call is
12
+ * fire-and-forget (the function returns without awaiting).
17
13
  *
18
14
  * Relates to #4341.
19
15
  */
20
16
 
21
17
  import test from "node:test";
22
18
  import assert from "node:assert/strict";
23
- import { readFileSync } from "node:fs";
24
- import { join, dirname } from "node:path";
25
- import { fileURLToPath } from "node:url";
26
19
 
27
- const __dirname = dirname(fileURLToPath(import.meta.url));
28
- const SOURCE = readFileSync(join(__dirname, "..", "notifications.ts"), "utf-8");
20
+ import {
21
+ sendDesktopNotification,
22
+ remoteNotificationDispatcher,
23
+ } from "../notifications.js";
29
24
 
30
- test("notifications.ts imports sendRemoteNotification from remote-questions/notify", () => {
31
- const hasImport =
32
- SOURCE.includes('from "../remote-questions/notify.js"') ||
33
- SOURCE.includes("from '../remote-questions/notify.js'");
34
- assert.ok(
35
- hasImport,
36
- "notifications.ts must import from '../remote-questions/notify.js'",
37
- );
38
-
39
- const importLine = SOURCE.split("\n").find(
40
- (line) =>
41
- line.includes("sendRemoteNotification") &&
42
- (line.includes("remote-questions/notify") || line.includes("remote-questions/notify.js")),
43
- );
44
- assert.ok(
45
- importLine,
46
- "The import statement must include sendRemoteNotification from the remote-questions/notify module",
47
- );
48
- });
49
-
50
- test("sendDesktopNotification calls sendRemoteNotification(title, message)", () => {
51
- // Extract the body of sendDesktopNotification — from its opening brace to the
52
- // closing brace at the same indentation level.
53
- const fnStart = SOURCE.indexOf("export function sendDesktopNotification(");
54
- assert.ok(fnStart > -1, "sendDesktopNotification must be present in notifications.ts");
25
+ test("sendDesktopNotification calls sendRemoteNotification with title and message", async (t) => {
26
+ const sendMock = t.mock.method(remoteNotificationDispatcher, "send", async () => {});
55
27
 
56
- // Find the next exported function/const after sendDesktopNotification to bound the search.
57
- const nextExportIdx = SOURCE.indexOf("\nexport ", fnStart + 1);
58
- const fnBody = nextExportIdx > -1 ? SOURCE.slice(fnStart, nextExportIdx) : SOURCE.slice(fnStart);
59
-
60
- assert.ok(
61
- fnBody.includes("sendRemoteNotification"),
62
- "sendDesktopNotification must call sendRemoteNotification",
63
- );
28
+ sendDesktopNotification("Test Title", "Test Message");
64
29
 
65
- assert.ok(
66
- fnBody.includes("sendRemoteNotification(title") || fnBody.includes("sendRemoteNotification(title,"),
67
- "sendRemoteNotification must be called with title as first argument",
68
- );
30
+ assert.equal(sendMock.mock.callCount(), 1);
31
+ assert.deepEqual(sendMock.mock.calls[0].arguments, ["Test Title", "Test Message"]);
69
32
  });
70
33
 
71
- test("sendRemoteNotification is invoked as void fire-and-forget with .catch(() => {})", () => {
72
- const fnStart = SOURCE.indexOf("export function sendDesktopNotification(");
73
- const nextExportIdx = SOURCE.indexOf("\nexport ", fnStart + 1);
74
- const fnBody = nextExportIdx > -1 ? SOURCE.slice(fnStart, nextExportIdx) : SOURCE.slice(fnStart);
34
+ test("sendDesktopNotification does not await the remote notification", async (t) => {
35
+ const sendMock = t.mock.method(remoteNotificationDispatcher, "send", async () => {});
75
36
 
76
- assert.ok(
77
- fnBody.includes("void sendRemoteNotification("),
78
- "sendRemoteNotification must be called with void (fire-and-forget)",
79
- );
37
+ const result = sendDesktopNotification("Async Title", "Async Message");
80
38
 
81
- assert.ok(
82
- fnBody.includes(".catch("),
83
- "sendRemoteNotification call must be followed by .catch() to suppress unhandled-rejection warnings",
84
- );
39
+ assert.equal(result, undefined, "sendDesktopNotification must return void");
40
+ assert.equal(sendMock.mock.callCount(), 1);
85
41
  });
86
42
 
87
- test("sendRemoteNotification call appears before shouldSendDesktopNotification guard", () => {
88
- // Regression guard for the HIGH-severity bug where remote notifications were
89
- // gated behind the desktop-notification preference check. Users who disable
90
- // desktop notifications must still receive Telegram/Slack/Discord messages.
91
- const fnStart = SOURCE.indexOf("export function sendDesktopNotification(");
92
- assert.ok(fnStart > -1, "sendDesktopNotification must be present in notifications.ts");
93
-
94
- const nextExportIdx = SOURCE.indexOf("\nexport ", fnStart + 1);
95
- const fnBody = nextExportIdx > -1 ? SOURCE.slice(fnStart, nextExportIdx) : SOURCE.slice(fnStart);
43
+ test("sendDesktopNotification fires remote notification even when desktop notifications are disabled", async (t) => {
44
+ const sendMock = t.mock.method(remoteNotificationDispatcher, "send", async () => {});
96
45
 
97
- const remoteCallIdx = fnBody.indexOf("sendRemoteNotification(");
98
- const guardIdx = fnBody.indexOf("shouldSendDesktopNotification(");
99
-
100
- assert.ok(remoteCallIdx > -1, "sendRemoteNotification must be called inside sendDesktopNotification");
101
- assert.ok(guardIdx > -1, "shouldSendDesktopNotification guard must be present inside sendDesktopNotification");
102
-
103
- assert.ok(
104
- remoteCallIdx < guardIdx,
105
- `sendRemoteNotification (pos ${remoteCallIdx}) must appear BEFORE the shouldSendDesktopNotification guard (pos ${guardIdx}) so that remote channels fire even when desktop notifications are disabled`,
46
+ sendDesktopNotification(
47
+ "Remote Title",
48
+ "Remote Message",
49
+ "info",
50
+ "complete",
51
+ undefined,
52
+ { notifications: { enabled: false } },
106
53
  );
54
+
55
+ assert.equal(sendMock.mock.callCount(), 1);
56
+ assert.deepEqual(sendMock.mock.calls[0].arguments, ["Remote Title", "Remote Message"]);
107
57
  });
@@ -200,6 +200,74 @@ test("parseRoadmapSlices: checkbox slices with pipe characters do not switch to
200
200
  assert.deepEqual(slices[2]?.depends, ["S01", "S02"]);
201
201
  });
202
202
 
203
+ test("parseRoadmapSlices: demo markdown table does not switch checkbox roadmap to table mode (#721)", () => {
204
+ const checkboxWithDemoTable = [
205
+ "# M004: Demo Table Repro",
206
+ "",
207
+ "## Slices",
208
+ "",
209
+ "- [x] **S01: First** `risk:low` `depends:[]`",
210
+ " > After this: cli can render examples:",
211
+ "| Example | Meaning |",
212
+ "| --- | --- |",
213
+ "| S01 | Not a roadmap row |",
214
+ "- [ ] **S02: Second** `risk:medium` `depends:[S01]`",
215
+ " > After this: cat file | sort returns stable output.",
216
+ "",
217
+ ].join("\n");
218
+
219
+ const slices = parseRoadmapSlices(checkboxWithDemoTable);
220
+ assert.equal(slices.length, 2);
221
+ assert.equal(slices[0]?.id, "S01");
222
+ assert.equal(slices[0]?.title, "First");
223
+ assert.equal(slices[0]?.done, true);
224
+ assert.equal(slices[1]?.id, "S02");
225
+ assert.deepEqual(slices[1]?.depends, ["S01"]);
226
+ });
227
+
228
+ test("parseRoadmapSlices: table preceded by intro prose still parsed as table (#722)", () => {
229
+ const tableWithIntro = [
230
+ "# M005: Intro Before Table",
231
+ "",
232
+ "## Slices",
233
+ "",
234
+ "This milestone is broken into the following slices:",
235
+ "",
236
+ "| Slice | Title | Risk | Status |",
237
+ "| --- | --- | --- | --- |",
238
+ "| S01 | Foundation | Low | [x] Done |",
239
+ "| S02 | Core | High | [ ] Pending |",
240
+ "",
241
+ ].join("\n");
242
+
243
+ const slices = parseRoadmapSlices(tableWithIntro);
244
+ assert.equal(slices.length, 2);
245
+ assert.equal(slices[0]?.id, "S01");
246
+ assert.equal(slices[0]?.done, true);
247
+ assert.equal(slices[1]?.id, "S02");
248
+ assert.equal(slices[1]?.done, false);
249
+ });
250
+
251
+ test("parseRoadmapSlices: table without markdown separator row still parsed as table (#722)", () => {
252
+ const tableWithoutSeparator = [
253
+ "# M006: No Separator",
254
+ "",
255
+ "## Slices",
256
+ "",
257
+ "| Slice | Title | Risk | Status |",
258
+ "| S01 | Foundation | Low | [x] Done |",
259
+ "| S02 | Core | High | [ ] Pending |",
260
+ "",
261
+ ].join("\n");
262
+
263
+ const slices = parseRoadmapSlices(tableWithoutSeparator);
264
+ assert.equal(slices.length, 2);
265
+ assert.equal(slices[0]?.id, "S01");
266
+ assert.equal(slices[0]?.done, true);
267
+ assert.equal(slices[1]?.id, "S02");
268
+ assert.equal(slices[1]?.done, false);
269
+ });
270
+
203
271
  // --- Prose slice header completion marker tests (#1803) ---
204
272
 
205
273
  test("parseRoadmapSlices: prose headers with ✓ marker detected as done", () => {
@@ -6,7 +6,11 @@ import assert from "node:assert/strict";
6
6
 
7
7
  import { classifyFailure } from "../recovery-classification.js";
8
8
  import { reconcileBeforeDispatch } from "../state-reconciliation.js";
9
- import { compileUnitContextContract, compileUnitToolContract } from "../tool-contract.js";
9
+ import {
10
+ compileUnitContextContract,
11
+ compileUnitToolContract,
12
+ getUnitWorkflowDispatchReadinessError,
13
+ } from "../tool-contract.js";
10
14
  import { shouldBlockAutoUnitToolCall } from "../auto-unit-tool-scope.js";
11
15
  import type { GSDState } from "../types.js";
12
16
 
@@ -63,7 +67,13 @@ test("Tool Contract compiles known Unit prompt and tool policy", () => {
63
67
 
64
68
  assert.equal(result.ok, true);
65
69
  assert.equal(result.ok && result.contract.unitType, "execute-task");
66
- assert.deepEqual(result.ok && result.contract.requiredWorkflowTools, ["gsd_task_complete"]);
70
+ assert.deepEqual(result.ok && result.contract.requiredWorkflowTools, [
71
+ "gsd_task_complete",
72
+ "gsd_exec",
73
+ "gsd_exec_search",
74
+ "gsd_resume",
75
+ "gsd_capture_thought",
76
+ ]);
67
77
  assert.deepEqual(result.ok && result.contract.forbiddenWorkflowTools, []);
68
78
  assert.equal(result.ok && result.contract.toolsPolicy.mode, "all");
69
79
  assert.ok(result.ok && result.contract.validationRules.includes("closeout-tool-present"));
@@ -87,6 +97,20 @@ test("Tool Contract compiles known Unit prompt and tool policy", () => {
87
97
  });
88
98
  });
89
99
 
100
+ test("Tool Contract derives dispatch readiness from Unit workflow tools", () => {
101
+ const error = getUnitWorkflowDispatchReadinessError({
102
+ provider: "claude-code",
103
+ unitType: "plan-slice",
104
+ projectRoot: "/tmp/project",
105
+ env: { GSD_WORKFLOW_MCP_COMMAND: "node" },
106
+ surface: "auto-mode",
107
+ authMode: "externalCli",
108
+ baseUrl: "local://claude-code",
109
+ });
110
+
111
+ assert.equal(error, null);
112
+ });
113
+
90
114
  test("Unit Context Contract exposes prompt context without workflow tool surface", () => {
91
115
  const result = compileUnitContextContract("execute-task");
92
116
 
@@ -1,18 +1,23 @@
1
- // Structural invariant: gsd-db.ts is the single writer for .gsd/gsd.db.
1
+ // Structural invariant: gsd-db.ts and the typed writer layer own writes for .gsd/gsd.db.
2
2
  //
3
3
  // No file under src/resources/extensions/gsd/ may issue raw write SQL
4
4
  // (INSERT/UPDATE/DELETE/REPLACE) or raw transaction control (BEGIN/COMMIT/
5
5
  // ROLLBACK via `.exec(...)`) against the engine database. Every bypass must
6
- // route through a typed wrapper exported from gsd-db.ts.
6
+ // route through an explicitly allowlisted typed writer.
7
7
  //
8
8
  // Allowlist:
9
- // - gsd-db.ts itself — the single writer
9
+ // - gsd-db.ts itself — compatibility barrel and remaining mid-migration wrappers
10
+ // - db/engine.ts — schema, migrations, lifecycle, and transaction primitives
11
+ // - db/writers/** — domain writers
12
+ // - typed coordination/runtime writer modules listed in TYPED_DB_WRITER_FILES
13
+ // - schema/migration helper modules listed in SCHEMA_DB_WRITER_FILES
14
+ // - ADR migration/backfill helpers listed in MIGRATION_BACKFILL_WRITER_FILES
10
15
  // - unit-ownership.ts — manages a separate .gsd/unit-claims.db for
11
16
  // cross-worktree claim races; intentionally outside this invariant
12
17
  // - tests/** — fixtures and direct DB inspection are fair game
13
18
  //
14
19
  // When this test fails, do not add a new suppression. Instead:
15
- // 1. Add a typed wrapper to gsd-db.ts that captures the SQL
20
+ // 1. Add a typed wrapper to the explicit writer layer that captures the SQL
16
21
  // 2. Switch the flagged site to call the wrapper
17
22
  //
18
23
  // See `.claude/plans/joyful-doodling-pony.md` for the full rationale.
@@ -29,17 +34,50 @@ const gsdDir = join(process.cwd(), "src/resources/extensions/gsd");
29
34
  // - db/engine.ts — connection lifecycle, schema/migrations (DDL), and the
30
35
  // BEGIN/COMMIT transaction primitives. The shared handle every writer reads.
31
36
  // - db/writers/**.ts — the Single Writer Layer: one cohesive write subsystem
32
- // per file (hierarchy, memory, gates, escalation, reconcile, manifest,
33
- // legacy-import, cascades).
37
+ // per file.
34
38
  // - gsd-db.ts — the barrel that re-exports the layer (still holds wrappers
35
39
  // mid-migration).
40
+ // - typed coordination/runtime writers listed below.
41
+ // - schema/migration helpers listed below.
42
+ // - ADR migration/backfill helpers listed below.
36
43
  // - unit-ownership.ts — a separate .gsd/unit-claims.db, intentionally outside.
37
44
  // db/queries.ts is explicitly NOT allowed write SQL (asserted separately below).
45
+ const TYPED_DB_WRITER_FILES = new Set([
46
+ "db/auto-workers.ts",
47
+ "db/command-queue.ts",
48
+ "db/milestone-leases.ts",
49
+ "db/runtime-kv.ts",
50
+ "db/unit-dispatches.ts",
51
+ ]);
52
+
53
+ const SCHEMA_DB_WRITER_FILES = new Set([
54
+ "db-memory-fts-schema.ts",
55
+ "db-schema-metadata.ts",
56
+ "db-verification-evidence-schema.ts",
57
+ ]);
58
+
59
+ const MIGRATION_BACKFILL_WRITER_FILES = new Set([
60
+ "memory-backfill.ts",
61
+ ]);
62
+
63
+ const DB_WRITER_ALLOWLIST_GUIDANCE = [
64
+ "gsd-db.ts",
65
+ "db/engine.ts",
66
+ "db/writers/**",
67
+ ...TYPED_DB_WRITER_FILES,
68
+ ...SCHEMA_DB_WRITER_FILES,
69
+ ...MIGRATION_BACKFILL_WRITER_FILES,
70
+ "unit-ownership.ts only for .gsd/unit-claims.db",
71
+ ].join(", ");
72
+
38
73
  function isSingleWriterFile(rel: string): boolean {
39
74
  const norm = rel.split("\\").join("/");
40
75
  if (norm === "gsd-db.ts" || norm === "unit-ownership.ts") return true;
41
76
  if (norm === "db/engine.ts") return true;
42
77
  if (norm.startsWith("db/writers/") && norm.endsWith(".ts")) return true;
78
+ if (TYPED_DB_WRITER_FILES.has(norm)) return true;
79
+ if (SCHEMA_DB_WRITER_FILES.has(norm)) return true;
80
+ if (MIGRATION_BACKFILL_WRITER_FILES.has(norm)) return true;
43
81
  return false;
44
82
  }
45
83
 
@@ -83,11 +121,126 @@ interface Violation {
83
121
  kind: string;
84
122
  }
85
123
 
86
- // Match .prepare("... INSERT|UPDATE|DELETE|REPLACE ...") in any quoting style.
87
- const PREPARE_WRITE_RE = /\.prepare\s*\(\s*[`'"][^`'"]*\b(INSERT|UPDATE|DELETE|REPLACE)\b/i;
124
+ const DB_CALL_RE = /\.(prepare|exec)\s*\(/g;
125
+ const PREPARE_WRITE_SQL_RE = /\b(INSERT|UPDATE|DELETE|REPLACE)\b/i;
126
+ const EXEC_WRITE_SQL_RE = /\b(INSERT|UPDATE|DELETE|REPLACE|BEGIN|COMMIT|ROLLBACK)\b/i;
127
+
128
+ function findRawWriteSqlViolations(file: string, content: string): Violation[] {
129
+ const violations: Violation[] = [];
130
+ DB_CALL_RE.lastIndex = 0;
131
+
132
+ let match: RegExpExecArray | null;
133
+ while ((match = DB_CALL_RE.exec(content)) !== null) {
134
+ const method = match[1] as "prepare" | "exec";
135
+ const sql = readFirstStringArgument(content, DB_CALL_RE.lastIndex);
136
+ if (sql === null) continue;
137
+
138
+ const keywordMatch =
139
+ method === "prepare"
140
+ ? PREPARE_WRITE_SQL_RE.exec(sql)
141
+ : EXEC_WRITE_SQL_RE.exec(sql);
142
+ if (keywordMatch === null) continue;
143
+
144
+ violations.push({
145
+ file,
146
+ line: lineNumberAt(content, match.index),
147
+ snippet: lineSnippetAt(content, match.index),
148
+ kind: `${method}(${keywordMatch[1].toUpperCase()})`,
149
+ });
150
+ }
151
+
152
+ return violations;
153
+ }
154
+
155
+ function readFirstStringArgument(content: string, index: number): string | null {
156
+ const quoteIndex = skipWhitespace(content, index);
157
+ const quote = content[quoteIndex];
158
+ if (quote !== "`" && quote !== "'" && quote !== '"') return null;
159
+ return readStringLiteral(content, quoteIndex, quote);
160
+ }
161
+
162
+ function skipWhitespace(content: string, index: number): number {
163
+ let cursor = index;
164
+ while (cursor < content.length && /\s/.test(content[cursor] ?? "")) {
165
+ cursor++;
166
+ }
167
+ return cursor;
168
+ }
169
+
170
+ function readStringLiteral(content: string, quoteIndex: number, quote: string): string | null {
171
+ let value = "";
172
+
173
+ for (let cursor = quoteIndex + 1; cursor < content.length; cursor++) {
174
+ const char = content[cursor];
175
+ if (char === "\\") {
176
+ value += char;
177
+ cursor++;
178
+ if (cursor < content.length) value += content[cursor];
179
+ continue;
180
+ }
181
+ if (char === quote) return value;
182
+ value += char;
183
+ }
184
+
185
+ return null;
186
+ }
187
+
188
+ function lineNumberAt(content: string, index: number): number {
189
+ return content.slice(0, index).split("\n").length;
190
+ }
191
+
192
+ function lineSnippetAt(content: string, index: number): string {
193
+ const lineStart = content.lastIndexOf("\n", index) + 1;
194
+ const nextNewline = content.indexOf("\n", index);
195
+ const lineEnd = nextNewline === -1 ? content.length : nextNewline;
196
+ return content.slice(lineStart, lineEnd).trim();
197
+ }
198
+
199
+ test("scanner catches multiline template-literal db.prepare write SQL", () => {
200
+ const violations = findRawWriteSqlViolations(
201
+ "fixture.ts",
202
+ [
203
+ "const stmt = db.prepare(",
204
+ " `INSERT INTO tasks (id)",
205
+ " VALUES (?)`",
206
+ ");",
207
+ ].join("\n"),
208
+ );
209
+
210
+ assert.deepEqual(violations.map(({ line, kind }) => ({ line, kind })), [
211
+ { line: 1, kind: "prepare(INSERT)" },
212
+ ]);
213
+ });
214
+
215
+ test("scanner catches multiline raw transaction control in exec", () => {
216
+ const violations = findRawWriteSqlViolations(
217
+ "fixture.ts",
218
+ [
219
+ "db.exec(",
220
+ " `BEGIN`",
221
+ ");",
222
+ ].join("\n"),
223
+ );
224
+
225
+ assert.deepEqual(violations.map(({ line, kind }) => ({ line, kind })), [
226
+ { line: 1, kind: "exec(BEGIN)" },
227
+ ]);
228
+ });
229
+
230
+ test("scanner ignores multiline SELECT statements", () => {
231
+ const violations = findRawWriteSqlViolations(
232
+ "fixture.ts",
233
+ [
234
+ "const stmt = db.prepare(",
235
+ " `SELECT id",
236
+ " FROM tasks`",
237
+ ");",
238
+ ].join("\n"),
239
+ );
240
+
241
+ assert.deepEqual(violations, []);
242
+ });
88
243
 
89
- // Match .exec("... INSERT|UPDATE|DELETE|REPLACE ...") or raw BEGIN/COMMIT/ROLLBACK.
90
- const EXEC_WRITE_RE = /\.exec\s*\(\s*[`'"][^`'"]*\b(INSERT|UPDATE|DELETE|REPLACE|BEGIN|COMMIT|ROLLBACK)\b/i;
91
244
  const DB_WORKSPACE_MECHANICS = new Set([
92
245
  "backupDatabaseSnapshot",
93
246
  "checkpointDatabase",
@@ -112,7 +265,7 @@ function importNames(specifierBlock: string): string[] {
112
265
  .filter(Boolean);
113
266
  }
114
267
 
115
- test("no module outside gsd-db.ts issues raw write SQL against the engine DB", () => {
268
+ test("no module outside the explicit DB writer allowlist issues raw write SQL", () => {
116
269
  const files = walkTsFiles(gsdDir);
117
270
  assert.ok(files.length >= 20, `Expected at least 20 .ts files under gsd/, found ${files.length}`);
118
271
 
@@ -129,30 +282,7 @@ test("no module outside gsd-db.ts issues raw write SQL against the engine DB", (
129
282
  continue;
130
283
  }
131
284
 
132
- const lines = content.split("\n");
133
- for (let i = 0; i < lines.length; i++) {
134
- const line = lines[i];
135
-
136
- const prepareMatch = PREPARE_WRITE_RE.exec(line);
137
- if (prepareMatch) {
138
- violations.push({
139
- file: rel,
140
- line: i + 1,
141
- snippet: line.trim(),
142
- kind: `prepare(${prepareMatch[1].toUpperCase()})`,
143
- });
144
- }
145
-
146
- const execMatch = EXEC_WRITE_RE.exec(line);
147
- if (execMatch) {
148
- violations.push({
149
- file: rel,
150
- line: i + 1,
151
- snippet: line.trim(),
152
- kind: `exec(${execMatch[1].toUpperCase()})`,
153
- });
154
- }
155
- }
285
+ violations.push(...findRawWriteSqlViolations(rel, content));
156
286
  }
157
287
 
158
288
  if (violations.length > 0) {
@@ -160,9 +290,9 @@ test("no module outside gsd-db.ts issues raw write SQL against the engine DB", (
160
290
  (v) => ` ${v.file}:${v.line} [${v.kind}] — ${v.snippet}`,
161
291
  );
162
292
  assert.fail(
163
- `Found ${violations.length} raw write SQL bypass(es) outside gsd-db.ts:\n` +
293
+ `Found ${violations.length} raw write SQL bypass(es) outside the explicit DB writer allowlist:\n` +
164
294
  lines.join("\n") +
165
- "\n\nEach of these must be replaced with a typed wrapper exported from gsd-db.ts.",
295
+ `\n\nMove each write to the appropriate allowlisted owner: ${DB_WRITER_ALLOWLIST_GUIDANCE}.`,
166
296
  );
167
297
  }
168
298
  });
@@ -174,19 +304,11 @@ test("db/queries.ts (the Query Module) is read-only — contains no write SQL",
174
304
  // db/writers/ — this is the explicit, positive statement of intent.)
175
305
  const queriesPath = join(gsdDir, "db", "queries.ts");
176
306
  const content = readFileSync(queriesPath, "utf-8");
177
- const lines = content.split("\n");
178
- const violations: Violation[] = [];
179
- for (let i = 0; i < lines.length; i++) {
180
- const line = lines[i];
181
- const m = PREPARE_WRITE_RE.exec(line) ?? EXEC_WRITE_RE.exec(line);
182
- if (m) {
183
- violations.push({ file: "db/queries.ts", line: i + 1, snippet: line.trim(), kind: m[1].toUpperCase() });
184
- }
185
- }
307
+ const violations = findRawWriteSqlViolations("db/queries.ts", content);
186
308
  assert.equal(
187
309
  violations.length,
188
310
  0,
189
- `db/queries.ts must contain no write SQL — move write wrappers to db/writers/:\n` +
311
+ `db/queries.ts must contain no write SQL — move write wrappers to the explicit DB writer allowlist:\n` +
190
312
  violations.map((v) => ` db/queries.ts:${v.line} [${v.kind}] — ${v.snippet}`).join("\n"),
191
313
  );
192
314
  });
@@ -299,7 +421,7 @@ test("production modules do not import DB open-state mechanics from gsd-db.ts",
299
421
  assert.fail(
300
422
  `Found ${violations.length} DB open-state import(s) from gsd-db.ts:\n` +
301
423
  lines.join("\n") +
302
- "\n\nImport these through db-workspace.ts so gsd-db.ts stays the single-writer implementation, not the caller-facing DB Workspace Interface.",
424
+ "\n\nImport these through db-workspace.ts so gsd-db.ts stays the writer compatibility barrel, not the caller-facing DB Workspace Interface.",
303
425
  );
304
426
  }
305
427
  });
@@ -92,14 +92,14 @@ test("buildSkillActivationBlock activates skills via prefer_skills when context
92
92
  prefer_skills: ["react"],
93
93
  });
94
94
 
95
- assert.match(result, /Call Skill\(\{ skill: 'react' \}\)/);
95
+ assert.match(result, /Read the installed 'react' skill file from <available_skills>/);
96
96
  assert.doesNotMatch(result, /swiftui/);
97
97
  } finally {
98
98
  cleanup(base);
99
99
  }
100
100
  });
101
101
 
102
- test("buildSkillActivationBlock includes always_use_skills from preferences using exact Skill tool format", () => {
102
+ test("buildSkillActivationBlock includes always_use_skills using read-based skill loading", () => {
103
103
  const base = makeTempBase();
104
104
  try {
105
105
  writeSkill(base, "swift-testing", "Use for Swift Testing assertions and verification patterns.");
@@ -109,7 +109,10 @@ test("buildSkillActivationBlock includes always_use_skills from preferences usin
109
109
  always_use_skills: ["swift-testing"],
110
110
  });
111
111
 
112
- assert.equal(result, "<skill_activation>Call Skill({ skill: 'swift-testing' }).</skill_activation>");
112
+ assert.equal(
113
+ result,
114
+ "<skill_activation>Read the installed 'swift-testing' skill file from <available_skills>.</skill_activation>",
115
+ );
113
116
  } finally {
114
117
  cleanup(base);
115
118
  }
@@ -137,8 +140,8 @@ test("buildSkillActivationBlock includes skill_rules matches and task-plan skill
137
140
  skill_rules: [{ when: "prisma database schema", use: ["prisma"] }],
138
141
  });
139
142
 
140
- assert.match(result, /Call Skill\(\{ skill: 'accessibility' \}\)/);
141
- assert.match(result, /Call Skill\(\{ skill: 'prisma' \}\)/);
143
+ assert.match(result, /Read the installed 'accessibility' skill file from <available_skills>/);
144
+ assert.match(result, /Read the installed 'prisma' skill file from <available_skills>/);
142
145
  } finally {
143
146
  cleanup(base);
144
147
  }
@@ -160,7 +163,7 @@ test("buildSkillActivationBlock matches skill_rules against exact unit type cont
160
163
  ],
161
164
  });
162
165
 
163
- assert.match(result, /Call Skill\(\{ skill: 'complete-slice-policies' \}\)/);
166
+ assert.match(result, /Read the installed 'complete-slice-policies' skill file from <available_skills>/);
164
167
  assert.doesNotMatch(result, /slice-broad/);
165
168
  } finally {
166
169
  cleanup(base);
@@ -264,8 +267,8 @@ test("buildSkillActivationBlock allows valid skill names and rejects invalid one
264
267
  });
265
268
 
266
269
  assert.match(result, /skill_activation/);
267
- assert.match(result, /Call Skill\(\{ skill: 'react' \}\)/);
268
- assert.match(result, /Call Skill\(\{ skill: 'good-skill-2' \}\)/);
270
+ assert.match(result, /Read the installed 'react' skill file from <available_skills>/);
271
+ assert.match(result, /Read the installed 'good-skill-2' skill file from <available_skills>/);
269
272
  assert.doesNotMatch(result, /bad'name/);
270
273
  } finally {
271
274
  cleanup(base);
@@ -289,8 +292,8 @@ test("buildSkillActivationBlock: explicit always_use_skills bypass the unit-type
289
292
  always_use_skills: ["write-docs", "swiftui"],
290
293
  });
291
294
 
292
- assert.match(result, /Call Skill\(\{ skill: 'write-docs' \}\)/);
293
- assert.match(result, /Call Skill\(\{ skill: 'swiftui' \}\)/);
295
+ assert.match(result, /Read the installed 'write-docs' skill file from <available_skills>/);
296
+ assert.match(result, /Read the installed 'swiftui' skill file from <available_skills>/);
294
297
  } finally {
295
298
  cleanup(base);
296
299
  }
@@ -307,7 +310,7 @@ test("buildSkillActivationBlock falls through to all skills for unknown unit typ
307
310
  });
308
311
 
309
312
  // Unknown unit type = wildcard fallback (pre-manifest behavior).
310
- assert.match(result, /Call Skill\(\{ skill: 'swiftui' \}\)/);
313
+ assert.match(result, /Read the installed 'swiftui' skill file from <available_skills>/);
311
314
  } finally {
312
315
  cleanup(base);
313
316
  }
@@ -324,7 +327,7 @@ test("buildSkillActivationBlock without unitType preserves pre-manifest behavior
324
327
  always_use_skills: ["swiftui"],
325
328
  });
326
329
 
327
- assert.match(result, /Call Skill\(\{ skill: 'swiftui' \}\)/);
330
+ assert.match(result, /Read the installed 'swiftui' skill file from <available_skills>/);
328
331
  } finally {
329
332
  cleanup(base);
330
333
  }
@@ -341,12 +344,12 @@ test("milestone prompt builders propagate always_use_skills through buildSkillAc
341
344
  loadOnlyTestSkills(base);
342
345
 
343
346
  const researchPrompt = await buildResearchMilestonePrompt("M001", "Test", base);
344
- assert.match(researchPrompt, /Call Skill\(\{ skill: 'write-docs' \}\)/);
345
- assert.match(researchPrompt, /Call Skill\(\{ skill: 'swiftui' \}\)/);
347
+ assert.match(researchPrompt, /Read the installed 'write-docs' skill file from <available_skills>/);
348
+ assert.match(researchPrompt, /Read the installed 'swiftui' skill file from <available_skills>/);
346
349
 
347
350
  const planPrompt = await buildPlanMilestonePrompt("M001", "Test", base);
348
- assert.match(planPrompt, /Call Skill\(\{ skill: 'write-docs' \}\)/);
349
- assert.match(planPrompt, /Call Skill\(\{ skill: 'swiftui' \}\)/);
351
+ assert.match(planPrompt, /Read the installed 'write-docs' skill file from <available_skills>/);
352
+ assert.match(planPrompt, /Read the installed 'swiftui' skill file from <available_skills>/);
350
353
  } finally {
351
354
  cleanup(base);
352
355
  }
@@ -377,7 +380,7 @@ test("complete-slice prompt propagates always_use_skills through buildSkillActiv
377
380
 
378
381
  const prompt = await buildCompleteSlicePrompt("M001", "Test", "S01", "Slice", base);
379
382
 
380
- assert.match(prompt, /Call Skill\(\{ skill: 'write-docs' \}\)/);
383
+ assert.match(prompt, /Read the installed 'write-docs' skill file from <available_skills>/);
381
384
  } finally {
382
385
  cleanup(base);
383
386
  }