@opengsd/gsd-pi 1.1.1-dev.a5a2de8 → 1.1.1-dev.b2556262

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 (325) hide show
  1. package/dist/headless-recover.js +56 -1
  2. package/dist/resources/.managed-resources-content-hash +1 -1
  3. package/dist/resources/extensions/browser-tools/engine/managed-gsd-browser.js +18 -2
  4. package/dist/resources/extensions/browser-tools/engine/selection.js +1 -1
  5. package/dist/resources/extensions/browser-tools/extension-manifest.json +1 -1
  6. package/dist/resources/extensions/browser-tools/index.js +68 -24
  7. package/dist/resources/extensions/browser-tools/state.js +12 -0
  8. package/dist/resources/extensions/browser-tools/tools/session.js +3 -2
  9. package/dist/resources/extensions/browser-tools/utils.js +3 -3
  10. package/dist/resources/extensions/browser-tools/web-app-detect.js +52 -0
  11. package/dist/resources/extensions/gsd/auto/loop.js +4 -2
  12. package/dist/resources/extensions/gsd/auto/phases.js +87 -12
  13. package/dist/resources/extensions/gsd/auto/session.js +22 -1
  14. package/dist/resources/extensions/gsd/auto/workflow-kernel.js +1 -0
  15. package/dist/resources/extensions/gsd/auto-dispatch.js +81 -13
  16. package/dist/resources/extensions/gsd/auto-model-selection.js +154 -9
  17. package/dist/resources/extensions/gsd/auto-post-unit.js +19 -2
  18. package/dist/resources/extensions/gsd/auto-prompts.js +26 -21
  19. package/dist/resources/extensions/gsd/auto-recovery.js +4 -2
  20. package/dist/resources/extensions/gsd/auto-runtime-state.js +3 -0
  21. package/dist/resources/extensions/gsd/auto-start.js +1 -1
  22. package/dist/resources/extensions/gsd/auto-timers.js +24 -10
  23. package/dist/resources/extensions/gsd/auto.js +40 -15
  24. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +3 -3
  25. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +192 -77
  26. package/dist/resources/extensions/gsd/bootstrap/system-context.js +1 -1
  27. package/dist/resources/extensions/gsd/closeout-wizard.js +32 -9
  28. package/dist/resources/extensions/gsd/commands/handlers/auto.js +10 -0
  29. package/dist/resources/extensions/gsd/commands/handlers/ops.js +2 -9
  30. package/dist/resources/extensions/gsd/commands-maintenance.js +93 -15
  31. package/dist/resources/extensions/gsd/commands-mcp-status.js +1 -1
  32. package/dist/resources/extensions/gsd/commands-prefs-wizard.js +2 -2
  33. package/dist/resources/extensions/gsd/config-overlay.js +1 -0
  34. package/dist/resources/extensions/gsd/context-masker.js +129 -5
  35. package/dist/resources/extensions/gsd/db-writer.js +35 -0
  36. package/dist/resources/extensions/gsd/docs/preferences-reference.md +50 -1
  37. package/dist/resources/extensions/gsd/gsd-db.js +480 -172
  38. package/dist/resources/extensions/gsd/guided-flow.js +4 -1
  39. package/dist/resources/extensions/gsd/markdown-renderer.js +37 -53
  40. package/dist/resources/extensions/gsd/md-importer.js +38 -3
  41. package/dist/resources/extensions/gsd/migration-auto-check.js +126 -31
  42. package/dist/resources/extensions/gsd/parsers-legacy.js +23 -0
  43. package/dist/resources/extensions/gsd/planner-handoff.js +98 -0
  44. package/dist/resources/extensions/gsd/planning-path-scope.js +22 -4
  45. package/dist/resources/extensions/gsd/pre-execution-checks.js +10 -2
  46. package/dist/resources/extensions/gsd/preferences-models.js +111 -43
  47. package/dist/resources/extensions/gsd/preferences-types.js +13 -0
  48. package/dist/resources/extensions/gsd/preferences-validation.js +68 -3
  49. package/dist/resources/extensions/gsd/preferences.js +4 -1
  50. package/dist/resources/extensions/gsd/prompts/gate-evaluate.md +1 -1
  51. package/dist/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
  52. package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  53. package/dist/resources/extensions/gsd/prompts/refine-slice.md +1 -1
  54. package/dist/resources/extensions/gsd/prompts/run-uat.md +2 -2
  55. package/dist/resources/extensions/gsd/prompts/system.md +1 -1
  56. package/dist/resources/extensions/gsd/roadmap-slices.js +5 -1
  57. package/dist/resources/extensions/gsd/safety/content-validator.js +6 -4
  58. package/dist/resources/extensions/gsd/skill-manifest.js +12 -0
  59. package/dist/resources/extensions/gsd/source-observations.js +306 -0
  60. package/dist/resources/extensions/gsd/state-reconciliation/drift/completion.js +15 -8
  61. package/dist/resources/extensions/gsd/state-reconciliation/drift/stale-render.js +33 -5
  62. package/dist/resources/extensions/gsd/state-reconciliation/drift/stale-worker.js +34 -13
  63. package/dist/resources/extensions/gsd/state-reconciliation/index.js +39 -14
  64. package/dist/resources/extensions/gsd/state-reconciliation/spawn-gate.js +4 -4
  65. package/dist/resources/extensions/gsd/state.js +7 -3
  66. package/dist/resources/extensions/gsd/tool-contract.js +15 -1
  67. package/dist/resources/extensions/gsd/tool-presentation-plan.js +24 -2
  68. package/dist/resources/extensions/gsd/tools/complete-slice.js +28 -0
  69. package/dist/resources/extensions/gsd/tools/plan-slice.js +42 -11
  70. package/dist/resources/extensions/gsd/tools/plan-task.js +7 -1
  71. package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +62 -406
  72. package/dist/resources/extensions/gsd/uat-policy.js +130 -0
  73. package/dist/resources/extensions/gsd/uat-run.js +414 -0
  74. package/dist/resources/extensions/gsd/unit-context-manifest.js +3 -4
  75. package/dist/resources/extensions/gsd/unit-tool-contracts.js +38 -14
  76. package/dist/resources/extensions/gsd/verdict-parser.js +3 -8
  77. package/dist/resources/extensions/gsd/workflow-manifest.js +132 -5
  78. package/dist/resources/extensions/gsd/workflow-mcp.js +2 -3
  79. package/dist/resources/extensions/gsd/workflow-projections.js +8 -0
  80. package/dist/resources/extensions/gsd/worktree-manager.js +26 -0
  81. package/dist/resources/extensions/gsd/worktree-reentry.js +96 -0
  82. package/dist/resources/extensions/gsd/worktree-state-projection.js +18 -17
  83. package/dist/resources/extensions/shared/gsd-browser-cli.js +6 -0
  84. package/dist/resources/extensions/subagent/agents.js +1 -0
  85. package/dist/resources/extensions/subagent/index.js +27 -12
  86. package/dist/resources/extensions/subagent/launch.js +7 -2
  87. package/dist/web/standalone/.next/BUILD_ID +1 -1
  88. package/dist/web/standalone/.next/app-path-routes-manifest.json +6 -6
  89. package/dist/web/standalone/.next/build-manifest.json +2 -2
  90. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  91. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  92. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  93. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  94. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  95. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  96. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  97. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  98. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  99. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  100. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  101. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  102. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  103. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  104. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  105. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  106. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  107. package/dist/web/standalone/.next/server/app/index.html +1 -1
  108. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  109. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  110. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  111. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  112. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  113. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  114. package/dist/web/standalone/.next/server/app-paths-manifest.json +6 -6
  115. package/dist/web/standalone/.next/server/chunks/8357.js +1 -1
  116. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  117. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  118. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  119. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  120. package/dist/web/standalone/node_modules/@gsd/native/dist/native.js +22 -0
  121. package/dist/web/standalone/node_modules/node-pty/build/Makefile +1 -1
  122. package/package.json +4 -4
  123. package/packages/cloud-mcp-gateway/package.json +2 -2
  124. package/packages/contracts/package.json +1 -1
  125. package/packages/daemon/package.json +4 -4
  126. package/packages/gsd-agent-core/package.json +5 -5
  127. package/packages/gsd-agent-modes/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  128. package/packages/gsd-agent-modes/dist/modes/interactive/components/assistant-message.js +21 -23
  129. package/packages/gsd-agent-modes/dist/modes/interactive/components/assistant-message.js.map +1 -1
  130. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts +3 -0
  131. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  132. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js +25 -0
  133. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js.map +1 -1
  134. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.d.ts +1 -0
  135. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.d.ts.map +1 -1
  136. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js +66 -12
  137. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js.map +1 -1
  138. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  139. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js +18 -11
  140. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  141. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-chat-render.d.ts.map +1 -1
  142. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-chat-render.js +16 -0
  143. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-chat-render.js.map +1 -1
  144. package/packages/gsd-agent-modes/package.json +7 -7
  145. package/packages/mcp-server/dist/workflow-tools.js +1 -1
  146. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  147. package/packages/mcp-server/package.json +3 -3
  148. package/packages/native/dist/native.js +22 -0
  149. package/packages/native/package.json +1 -1
  150. package/packages/pi-agent-core/package.json +1 -1
  151. package/packages/pi-ai/dist/image-models.generated.d.ts +30 -0
  152. package/packages/pi-ai/dist/image-models.generated.d.ts.map +1 -1
  153. package/packages/pi-ai/dist/image-models.generated.js +30 -0
  154. package/packages/pi-ai/dist/image-models.generated.js.map +1 -1
  155. package/packages/pi-ai/dist/models.generated.d.ts +174 -29
  156. package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
  157. package/packages/pi-ai/dist/models.generated.js +178 -54
  158. package/packages/pi-ai/dist/models.generated.js.map +1 -1
  159. package/packages/pi-ai/dist/providers/transform-messages.d.ts.map +1 -1
  160. package/packages/pi-ai/dist/providers/transform-messages.js +8 -1
  161. package/packages/pi-ai/dist/providers/transform-messages.js.map +1 -1
  162. package/packages/pi-ai/package.json +1 -1
  163. package/packages/pi-coding-agent/dist/core/settings-manager.js +1 -1
  164. package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
  165. package/packages/pi-coding-agent/dist/theme/themes.js +1 -1
  166. package/packages/pi-coding-agent/dist/theme/themes.js.map +1 -1
  167. package/packages/pi-coding-agent/package.json +7 -7
  168. package/packages/pi-tui/dist/utils.d.ts +11 -0
  169. package/packages/pi-tui/dist/utils.d.ts.map +1 -1
  170. package/packages/pi-tui/dist/utils.js +119 -6
  171. package/packages/pi-tui/dist/utils.js.map +1 -1
  172. package/packages/pi-tui/package.json +2 -1
  173. package/packages/rpc-client/package.json +2 -2
  174. package/pkg/dist/theme/themes.js +1 -1
  175. package/pkg/dist/theme/themes.js.map +1 -1
  176. package/pkg/package.json +1 -1
  177. package/scripts/install/handoff.js +16 -3
  178. package/src/resources/extensions/browser-tools/engine/managed-gsd-browser.ts +21 -2
  179. package/src/resources/extensions/browser-tools/engine/selection.ts +1 -1
  180. package/src/resources/extensions/browser-tools/extension-manifest.json +1 -1
  181. package/src/resources/extensions/browser-tools/index.ts +75 -27
  182. package/src/resources/extensions/browser-tools/state.ts +13 -0
  183. package/src/resources/extensions/browser-tools/tests/browser-engine-selection.test.mjs +2 -2
  184. package/src/resources/extensions/browser-tools/tests/browser-tools-unit.test.cjs +57 -0
  185. package/src/resources/extensions/browser-tools/tests/gsd-browser-launch-config.test.mjs +37 -0
  186. package/src/resources/extensions/browser-tools/tests/web-app-detect.test.mjs +68 -0
  187. package/src/resources/extensions/browser-tools/tools/session.ts +4 -2
  188. package/src/resources/extensions/browser-tools/utils.ts +3 -3
  189. package/src/resources/extensions/browser-tools/web-app-detect.ts +63 -0
  190. package/src/resources/extensions/gsd/auto/loop-deps.ts +1 -0
  191. package/src/resources/extensions/gsd/auto/loop.ts +4 -2
  192. package/src/resources/extensions/gsd/auto/phases.ts +89 -15
  193. package/src/resources/extensions/gsd/auto/session.ts +24 -1
  194. package/src/resources/extensions/gsd/auto/workflow-kernel.ts +1 -0
  195. package/src/resources/extensions/gsd/auto-dispatch.ts +117 -12
  196. package/src/resources/extensions/gsd/auto-model-selection.ts +190 -12
  197. package/src/resources/extensions/gsd/auto-post-unit.ts +20 -2
  198. package/src/resources/extensions/gsd/auto-prompts.ts +25 -22
  199. package/src/resources/extensions/gsd/auto-recovery.ts +22 -3
  200. package/src/resources/extensions/gsd/auto-runtime-state.ts +5 -0
  201. package/src/resources/extensions/gsd/auto-start.ts +1 -1
  202. package/src/resources/extensions/gsd/auto-timers.ts +25 -9
  203. package/src/resources/extensions/gsd/auto.ts +41 -14
  204. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +3 -3
  205. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +250 -78
  206. package/src/resources/extensions/gsd/bootstrap/system-context.ts +1 -1
  207. package/src/resources/extensions/gsd/closeout-wizard.ts +47 -13
  208. package/src/resources/extensions/gsd/commands/handlers/auto.ts +9 -0
  209. package/src/resources/extensions/gsd/commands/handlers/ops.ts +2 -17
  210. package/src/resources/extensions/gsd/commands-maintenance.ts +124 -13
  211. package/src/resources/extensions/gsd/commands-mcp-status.ts +1 -1
  212. package/src/resources/extensions/gsd/commands-prefs-wizard.ts +2 -2
  213. package/src/resources/extensions/gsd/config-overlay.ts +1 -0
  214. package/src/resources/extensions/gsd/context-masker.ts +152 -5
  215. package/src/resources/extensions/gsd/db-writer.ts +38 -0
  216. package/src/resources/extensions/gsd/docs/preferences-reference.md +50 -1
  217. package/src/resources/extensions/gsd/gsd-db.ts +564 -186
  218. package/src/resources/extensions/gsd/guided-flow.ts +4 -1
  219. package/src/resources/extensions/gsd/markdown-renderer.ts +44 -66
  220. package/src/resources/extensions/gsd/md-importer.ts +49 -2
  221. package/src/resources/extensions/gsd/migration-auto-check.ts +154 -34
  222. package/src/resources/extensions/gsd/parsers-legacy.ts +20 -0
  223. package/src/resources/extensions/gsd/planner-handoff.ts +149 -0
  224. package/src/resources/extensions/gsd/planning-path-scope.ts +22 -4
  225. package/src/resources/extensions/gsd/pre-execution-checks.ts +9 -2
  226. package/src/resources/extensions/gsd/preferences-models.ts +113 -43
  227. package/src/resources/extensions/gsd/preferences-types.ts +47 -0
  228. package/src/resources/extensions/gsd/preferences-validation.ts +76 -2
  229. package/src/resources/extensions/gsd/preferences.ts +5 -0
  230. package/src/resources/extensions/gsd/prompts/gate-evaluate.md +1 -1
  231. package/src/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
  232. package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  233. package/src/resources/extensions/gsd/prompts/refine-slice.md +1 -1
  234. package/src/resources/extensions/gsd/prompts/run-uat.md +2 -2
  235. package/src/resources/extensions/gsd/prompts/system.md +1 -1
  236. package/src/resources/extensions/gsd/roadmap-slices.ts +6 -1
  237. package/src/resources/extensions/gsd/safety/content-validator.ts +8 -5
  238. package/src/resources/extensions/gsd/skill-manifest.ts +12 -0
  239. package/src/resources/extensions/gsd/source-observations.ts +402 -0
  240. package/src/resources/extensions/gsd/state-reconciliation/drift/completion.ts +20 -8
  241. package/src/resources/extensions/gsd/state-reconciliation/drift/stale-render.ts +44 -5
  242. package/src/resources/extensions/gsd/state-reconciliation/drift/stale-worker.ts +39 -11
  243. package/src/resources/extensions/gsd/state-reconciliation/index.ts +45 -15
  244. package/src/resources/extensions/gsd/state-reconciliation/spawn-gate.ts +4 -4
  245. package/src/resources/extensions/gsd/state.ts +7 -4
  246. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +114 -0
  247. package/src/resources/extensions/gsd/tests/auto-model-selection-tool-poisoning.test.ts +66 -4
  248. package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +299 -1
  249. package/src/resources/extensions/gsd/tests/auto-paused-ui-cleanup.test.ts +32 -0
  250. package/src/resources/extensions/gsd/tests/auto-phases-lifecycle.test.ts +75 -3
  251. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +22 -1
  252. package/src/resources/extensions/gsd/tests/auto-supervisor.test.mjs +4 -0
  253. package/src/resources/extensions/gsd/tests/before-provider-context-management.test.ts +145 -0
  254. package/src/resources/extensions/gsd/tests/bundled-skill-triggers.test.ts +9 -0
  255. package/src/resources/extensions/gsd/tests/closeout-wizard.test.ts +44 -0
  256. package/src/resources/extensions/gsd/tests/commands-dispatcher-unmerged-milestone.test.ts +26 -1
  257. package/src/resources/extensions/gsd/tests/complete-slice-verification-gate.test.ts +118 -0
  258. package/src/resources/extensions/gsd/tests/content-validator.test.ts +74 -0
  259. package/src/resources/extensions/gsd/tests/context-masker.test.ts +56 -1
  260. package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +17 -2
  261. package/src/resources/extensions/gsd/tests/dispatch-rule-coverage.test.ts +24 -0
  262. package/src/resources/extensions/gsd/tests/doctor-scope-db-unavailable.test.ts +1 -11
  263. package/src/resources/extensions/gsd/tests/gate-dispatch.test.ts +64 -0
  264. package/src/resources/extensions/gsd/tests/gate-storage.test.ts +15 -0
  265. package/src/resources/extensions/gsd/tests/gsd-recover.test.ts +62 -1
  266. package/src/resources/extensions/gsd/tests/integration/run-uat.test.ts +4 -1
  267. package/src/resources/extensions/gsd/tests/interrupted-session-auto.test.ts +27 -0
  268. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +16 -0
  269. package/src/resources/extensions/gsd/tests/markdown-renderer.test.ts +42 -0
  270. package/src/resources/extensions/gsd/tests/mcp-project-config.test.ts +7 -1
  271. package/src/resources/extensions/gsd/tests/mcp-status.test.ts +1 -1
  272. package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +99 -0
  273. package/src/resources/extensions/gsd/tests/plan-slice.test.ts +99 -2
  274. package/src/resources/extensions/gsd/tests/plan-task.test.ts +19 -0
  275. package/src/resources/extensions/gsd/tests/planner-handoff.test.ts +100 -0
  276. package/src/resources/extensions/gsd/tests/preferences.test.ts +14 -0
  277. package/src/resources/extensions/gsd/tests/prefs-wizard-coverage.test.ts +1 -0
  278. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +133 -0
  279. package/src/resources/extensions/gsd/tests/provider-switch-observer.test.ts +55 -0
  280. package/src/resources/extensions/gsd/tests/register-hooks-depth-verification.test.ts +101 -1
  281. package/src/resources/extensions/gsd/tests/repository-registry.test.ts +2 -2
  282. package/src/resources/extensions/gsd/tests/runtime-invariant-modules.test.ts +28 -0
  283. package/src/resources/extensions/gsd/tests/schema-v21-sequence.test.ts +5 -3
  284. package/src/resources/extensions/gsd/tests/schema-v27-v28-sequence.test.ts +162 -18
  285. package/src/resources/extensions/gsd/tests/skill-manifest.test.ts +4 -3
  286. package/src/resources/extensions/gsd/tests/skipped-validation-db-atomicity.test.ts +8 -0
  287. package/src/resources/extensions/gsd/tests/source-observations.test.ts +275 -0
  288. package/src/resources/extensions/gsd/tests/stale-queued-milestone.test.ts +43 -0
  289. package/src/resources/extensions/gsd/tests/state-reconciliation-drift.test.ts +76 -21
  290. package/src/resources/extensions/gsd/tests/thinking-level-resolution.test.ts +203 -0
  291. package/src/resources/extensions/gsd/tests/uat-policy.test.ts +170 -0
  292. package/src/resources/extensions/gsd/tests/unit-context-manifest.test.ts +7 -1
  293. package/src/resources/extensions/gsd/tests/workflow-kernel.test.ts +7 -0
  294. package/src/resources/extensions/gsd/tests/workflow-manifest.test.ts +306 -1
  295. package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +77 -10
  296. package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +260 -5
  297. package/src/resources/extensions/gsd/tests/worktree-db.test.ts +511 -1
  298. package/src/resources/extensions/gsd/tests/worktree-reentry.test.ts +102 -0
  299. package/src/resources/extensions/gsd/tests/worktree-state-projection.test.ts +44 -0
  300. package/src/resources/extensions/gsd/tool-contract.ts +29 -1
  301. package/src/resources/extensions/gsd/tool-presentation-plan.ts +41 -6
  302. package/src/resources/extensions/gsd/tools/complete-slice.ts +29 -0
  303. package/src/resources/extensions/gsd/tools/plan-slice.ts +54 -12
  304. package/src/resources/extensions/gsd/tools/plan-task.ts +8 -1
  305. package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +71 -489
  306. package/src/resources/extensions/gsd/types.ts +1 -0
  307. package/src/resources/extensions/gsd/uat-policy.ts +191 -0
  308. package/src/resources/extensions/gsd/uat-run.ts +550 -0
  309. package/src/resources/extensions/gsd/unit-context-manifest.ts +3 -4
  310. package/src/resources/extensions/gsd/unit-tool-contracts.ts +38 -14
  311. package/src/resources/extensions/gsd/verdict-parser.ts +3 -10
  312. package/src/resources/extensions/gsd/workflow-manifest.ts +193 -7
  313. package/src/resources/extensions/gsd/workflow-mcp.ts +2 -3
  314. package/src/resources/extensions/gsd/workflow-projections.ts +9 -0
  315. package/src/resources/extensions/gsd/worktree-manager.ts +32 -0
  316. package/src/resources/extensions/gsd/worktree-reentry.ts +103 -0
  317. package/src/resources/extensions/gsd/worktree-state-projection.ts +22 -22
  318. package/src/resources/extensions/shared/gsd-browser-cli.ts +6 -0
  319. package/src/resources/extensions/shared/tests/format-utils.test.ts +8 -3
  320. package/src/resources/extensions/subagent/agents.ts +4 -0
  321. package/src/resources/extensions/subagent/index.ts +28 -3
  322. package/src/resources/extensions/subagent/launch.ts +8 -0
  323. package/src/resources/extensions/subagent/tests/model-override.test.ts +31 -0
  324. /package/dist/web/standalone/.next/static/{9y3LeeR2uGr2yRj9RjY3D → tJOKQbQRO-9MiFDO8DIDS}/_buildManifest.js +0 -0
  325. /package/dist/web/standalone/.next/static/{9y3LeeR2uGr2yRj9RjY3D → tJOKQbQRO-9MiFDO8DIDS}/_ssgManifest.js +0 -0
@@ -3,11 +3,21 @@ import assert from "node:assert/strict";
3
3
  import { readFileSync } from "node:fs";
4
4
  import { join } from "node:path";
5
5
  import {
6
+ RUN_UAT_BROWSER_TOOL_NAMES,
6
7
  buildRunUatResultPresentation,
8
+ buildRunUatPresentationForType,
7
9
  RUN_UAT_READ_ONLY_TOOL_NAMES,
8
10
  RUN_UAT_TOOL_PRESENTATION_PLAN_ID,
9
11
  RUN_UAT_WORKFLOW_TOOL_NAMES,
10
12
  } from "../tool-presentation-plan.ts";
13
+ import {
14
+ buildMinimalAutoGsdToolSet,
15
+ MINIMAL_AUTO_BASE_TOOL_NAMES,
16
+ MINIMAL_GSD_TOOL_NAMES,
17
+ } from "../bootstrap/register-hooks.ts";
18
+ import { shouldBlockAutoUnitToolCall } from "../auto-unit-tool-scope.ts";
19
+ import { UNIT_TOOL_CONTRACTS } from "../unit-tool-contracts.ts";
20
+ import { uatTypeIncludesBrowser } from "../uat-policy.ts";
11
21
 
12
22
  const promptsDir = join(process.cwd(), "src/resources/extensions/gsd/prompts");
13
23
  const templatesDir = join(process.cwd(), "src/resources/extensions/gsd/templates");
@@ -20,6 +30,84 @@ function readTemplate(name: string): string {
20
30
  return readFileSync(join(templatesDir, `${name}.md`), "utf-8");
21
31
  }
22
32
 
33
+ function escapeRegExp(value: string): string {
34
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
35
+ }
36
+
37
+ const registeredPhaseToolNames = [
38
+ ...new Set([
39
+ ...MINIMAL_AUTO_BASE_TOOL_NAMES,
40
+ ...MINIMAL_GSD_TOOL_NAMES,
41
+ ...Object.values(UNIT_TOOL_CONTRACTS).flatMap((contract) => contract.allowedGsdTools),
42
+ ]),
43
+ ];
44
+
45
+ const PHASE_PROMPT_TOOL_CALLS: Record<string, readonly string[]> = {
46
+ "research-milestone": ["gsd_summary_save"],
47
+ "plan-milestone": [
48
+ "gsd_milestone_status",
49
+ "gsd_plan_milestone",
50
+ "gsd_plan_slice",
51
+ "gsd_decision_save",
52
+ ],
53
+ "research-slice": ["gsd_summary_save"],
54
+ "plan-slice": ["gsd_reassess_roadmap", "gsd_plan_slice", "gsd_decision_save"],
55
+ "refine-slice": ["gsd_plan_slice", "gsd_decision_save"],
56
+ "replan-slice": ["gsd_replan_slice"],
57
+ "execute-task": ["gsd_task_complete"],
58
+ "reactive-execute": ["gsd_summary_save"],
59
+ "complete-slice": [
60
+ "gsd_exec",
61
+ "gsd_task_reopen",
62
+ "gsd_replan_slice",
63
+ "gsd_requirement_update",
64
+ "capture_thought",
65
+ "gsd_slice_complete",
66
+ "gsd_summary_save",
67
+ ],
68
+ "reassess-roadmap": ["gsd_milestone_status", "gsd_reassess_roadmap"],
69
+ "validate-milestone": ["gsd_milestone_status", "gsd_validate_milestone", "gsd_reassess_roadmap"],
70
+ "run-uat": ["gsd_uat_exec", "gsd_uat_result_save"],
71
+ "gate-evaluate": ["gsd_save_gate_result"],
72
+ "complete-milestone": [
73
+ "gsd_milestone_status",
74
+ "gsd_requirement_update",
75
+ "gsd_summary_save",
76
+ "capture_thought",
77
+ "gsd_complete_milestone",
78
+ ],
79
+ };
80
+
81
+ test("auto phase prompt tool calls are available in scoped tool surfaces", () => {
82
+ for (const [unitType, promptTools] of Object.entries(PHASE_PROMPT_TOOL_CALLS)) {
83
+ const prompt = readPrompt(unitType);
84
+ const activeTools = buildMinimalAutoGsdToolSet(
85
+ registeredPhaseToolNames,
86
+ unitType,
87
+ registeredPhaseToolNames,
88
+ );
89
+
90
+ for (const toolName of promptTools) {
91
+ assert.match(
92
+ prompt,
93
+ new RegExp(`\\b${escapeRegExp(toolName)}\\b`),
94
+ `${unitType} prompt should mention ${toolName}`,
95
+ );
96
+ assert.ok(
97
+ activeTools.includes(toolName),
98
+ `${unitType} prompt mentions ${toolName}, but scoped tools are ${activeTools.join(", ")}`,
99
+ );
100
+
101
+ const scopeResult = shouldBlockAutoUnitToolCall(unitType, toolName);
102
+ assert.equal(
103
+ scopeResult.block,
104
+ false,
105
+ `${unitType} phase gate blocked ${toolName}: ${scopeResult.reason ?? "unknown reason"}`,
106
+ );
107
+ }
108
+ }
109
+ });
110
+
23
111
  test("reactive-execute prompt keeps task summaries with subagents and avoids batch commits", () => {
24
112
  const prompt = readPrompt("reactive-execute");
25
113
  assert.match(prompt, /subagent-written summary as authoritative/i);
@@ -81,6 +169,45 @@ test("run-uat prompt gives the complete UAT result-save presentation contract",
81
169
  );
82
170
  });
83
171
 
172
+ test("browser-executable UAT presentation uses direct browser tools", () => {
173
+ const presentation = buildRunUatPresentationForType("browser-executable");
174
+
175
+ assert.equal(presentation.surface, "hybrid");
176
+ for (const toolName of RUN_UAT_BROWSER_TOOL_NAMES) {
177
+ assert.ok(presentation.presentedTools.includes(toolName), `presentation should include browser tool ${toolName}`);
178
+ }
179
+ assert.ok(!presentation.presentedTools.some((toolName) => toolName.startsWith("mcp__gsd-browser__")));
180
+ });
181
+
182
+ test("live-runtime and mixed UAT presentations also surface browser tools", () => {
183
+ // Regression (M001/S03): the run-uat prompt tells live-runtime and mixed to
184
+ // drive a browser, so the runner must actually receive the browser tools and
185
+ // a hybrid surface — otherwise live checks silently downgrade to NEEDS-HUMAN.
186
+ for (const uatType of ["live-runtime", "mixed", "human-experience"] as const) {
187
+ assert.equal(uatTypeIncludesBrowser(uatType), true, `${uatType} policy should include browser tools`);
188
+ const presentation = buildRunUatPresentationForType(uatType);
189
+ assert.equal(presentation.surface, "hybrid", `${uatType} should use the hybrid surface`);
190
+ for (const toolName of RUN_UAT_BROWSER_TOOL_NAMES) {
191
+ assert.ok(
192
+ presentation.presentedTools.includes(toolName),
193
+ `${uatType} presentation should include browser tool ${toolName}`,
194
+ );
195
+ }
196
+ }
197
+ });
198
+
199
+ test("artifact-driven and runtime-executable UAT presentations stay browser-free", () => {
200
+ for (const uatType of ["artifact-driven", "runtime-executable"] as const) {
201
+ assert.equal(uatTypeIncludesBrowser(uatType), false, `${uatType} policy should stay browser-free`);
202
+ const presentation = buildRunUatPresentationForType(uatType);
203
+ assert.equal(presentation.surface, "mcp", `${uatType} should use the mcp surface`);
204
+ assert.ok(
205
+ !RUN_UAT_BROWSER_TOOL_NAMES.some((toolName) => presentation.presentedTools.includes(toolName)),
206
+ `${uatType} presentation should not include browser tools`,
207
+ );
208
+ }
209
+ });
210
+
84
211
  test("workflow-start prompt defaults to autonomy instead of per-phase confirmation", () => {
85
212
  const prompt = readPrompt("workflow-start");
86
213
  assert.match(prompt, /Keep moving by default/i);
@@ -535,6 +662,12 @@ test("parallel subagent prompts forbid serialized tasks arrays", () => {
535
662
  }
536
663
  });
537
664
 
665
+ test("gate-evaluate prompt requires gate result findings field", () => {
666
+ const prompt = readPrompt("gate-evaluate");
667
+ assert.match(prompt, /`findings`/);
668
+ assert.match(prompt, /empty string if none/i);
669
+ });
670
+
538
671
  // ─── Project-shape classifier + 3-or-4-options-with-Other-hatch contract ──
539
672
 
540
673
  test("guided-discuss-project classifies project shape and persists the verdict to PROJECT.md", () => {
@@ -210,6 +210,61 @@ test("end-to-end: audit event is emitted when an auto trace is active", async ()
210
210
  }
211
211
  });
212
212
 
213
+ test("same-API transform with changes does not fire the observer (no real provider switch)", async () => {
214
+ const { basePath, cleanup } = withTempBasePath();
215
+ try {
216
+ initNotificationStore(basePath);
217
+ installProviderSwitchObserver();
218
+
219
+ // Target api === source api. The conversation ends on an unresolved tool
220
+ // call, so a synthetic tool result IS backfilled (a non-empty report) — but
221
+ // this is a within-provider normalization, not a cross-provider switch.
222
+ // `sourceApi` is omitted (the common case), so fromApi defaults to the
223
+ // target api and equals toApi. The observer must stay silent.
224
+ const sameApiModel = {
225
+ id: "gpt-5",
226
+ name: "GPT-5",
227
+ api: "openai-responses",
228
+ provider: "openai",
229
+ baseUrl: "",
230
+ reasoning: false,
231
+ input: ["text"],
232
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
233
+ contextWindow: 128000,
234
+ maxTokens: 8192,
235
+ } as Parameters<typeof transformMessagesWithReport>[1];
236
+
237
+ const messages = [
238
+ {
239
+ role: "assistant" as const,
240
+ content: [
241
+ { type: "toolCall" as const, id: "call_orphan_1", name: "bash", arguments: {} },
242
+ ],
243
+ api: "openai-responses",
244
+ provider: "openai",
245
+ model: "gpt-5",
246
+ usage: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, totalTokens: 0, cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 } },
247
+ stopReason: "stop" as const,
248
+ timestamp: Date.now(),
249
+ },
250
+ ];
251
+
252
+ transformMessagesWithReport(
253
+ messages as Parameters<typeof transformMessagesWithReport>[0],
254
+ sameApiModel,
255
+ );
256
+
257
+ assert.equal(getProviderSwitchStats().totalSwitches, 0, "same→same transform must not count as a provider switch");
258
+ assert.equal(
259
+ readNotifications(basePath).filter((n) => n.message.includes("Provider switch")).length,
260
+ 0,
261
+ "same→same transform must not emit a provider-switch notification",
262
+ );
263
+ } finally {
264
+ cleanup();
265
+ }
266
+ });
267
+
213
268
  test("empty report does not bump counter or emit a notification", async () => {
214
269
  const { basePath, cleanup } = withTempBasePath();
215
270
  try {
@@ -1,10 +1,16 @@
1
1
  import test from "node:test";
2
2
  import assert from "node:assert/strict";
3
- import { mkdirSync, rmSync } from "node:fs";
3
+ import { existsSync, mkdirSync, readFileSync, rmSync } from "node:fs";
4
4
  import { join } from "node:path";
5
5
  import { tmpdir } from "node:os";
6
6
 
7
7
  import { registerHooks } from "../bootstrap/register-hooks.ts";
8
+ import {
9
+ clearPendingAutoStart,
10
+ setPendingAutoStart,
11
+ } from "../guided-flow.ts";
12
+ import { closeDatabase, getMilestone } from "../gsd-db.ts";
13
+ import { deriveState, invalidateStateCache } from "../state.ts";
8
14
  import {
9
15
  getPendingGate,
10
16
  resetWriteGateState,
@@ -186,6 +192,100 @@ test("register-hooks unlocks milestone depth verification from question id witho
186
192
  );
187
193
  });
188
194
 
195
+ test("register-hooks persists first structured question round for new milestone re-entry", async (t) => {
196
+ const dir = makeTempDir("question-draft");
197
+ mkdirSync(join(dir, ".gsd", "milestones"), { recursive: true });
198
+ const originalCwd = process.cwd();
199
+ process.chdir(dir);
200
+ resetWriteGateState(dir);
201
+ clearPendingAutoStart(dir);
202
+
203
+ t.after(() => {
204
+ try {
205
+ resetWriteGateState(dir);
206
+ clearPendingAutoStart(dir);
207
+ closeDatabase();
208
+ } finally {
209
+ process.chdir(originalCwd);
210
+ rmSync(dir, { recursive: true, force: true });
211
+ }
212
+ });
213
+
214
+ const handlers = new Map<string, Array<(event: any, ctx?: any) => Promise<void> | void>>();
215
+ const pi = {
216
+ on(event: string, handler: (event: any, ctx?: any) => Promise<void> | void) {
217
+ const existing = handlers.get(event) ?? [];
218
+ existing.push(handler);
219
+ handlers.set(event, existing);
220
+ },
221
+ } as any;
222
+ const ctx = { cwd: dir, ui: { notify: () => undefined } } as any;
223
+
224
+ registerHooks(pi, []);
225
+ setPendingAutoStart(dir, {
226
+ basePath: dir,
227
+ milestoneId: "M004",
228
+ ctx,
229
+ pi: { sendMessage: () => undefined } as any,
230
+ });
231
+
232
+ const questions = [
233
+ {
234
+ id: "m004_shape",
235
+ header: "M004 Shape",
236
+ question: "What are you picturing for M004?",
237
+ options: [
238
+ { label: "Planning metadata (Recommended)", description: "Plan the next metadata layer." },
239
+ { label: "Find and organize", description: "Improve searching and organizing." },
240
+ ],
241
+ },
242
+ {
243
+ id: "boundary",
244
+ header: "Boundary",
245
+ question: "Which boundary should I plan around?",
246
+ options: [
247
+ { label: "No new dependencies (Recommended)", description: "Keep implementation vanilla." },
248
+ { label: "Browser APIs OK", description: "Use browser-native capabilities." },
249
+ ],
250
+ },
251
+ ];
252
+
253
+ for (const handler of handlers.get("tool_result") ?? []) {
254
+ await handler({
255
+ toolName: "ask_user_questions",
256
+ input: { questions },
257
+ details: {
258
+ response: {
259
+ answers: {
260
+ m004_shape: { selected: "Planning metadata (Recommended)" },
261
+ boundary: { selected: "No new dependencies (Recommended)" },
262
+ },
263
+ },
264
+ },
265
+ }, ctx);
266
+ }
267
+
268
+ const milestoneDir = join(dir, ".gsd", "milestones", "M004");
269
+ const draftPath = join(milestoneDir, "M004-CONTEXT-DRAFT.md");
270
+ const discussionPath = join(milestoneDir, "M004-DISCUSSION.md");
271
+
272
+ assert.equal(existsSync(draftPath), true, "first answer round should create a resumable context draft");
273
+ assert.equal(existsSync(discussionPath), true, "first answer round should create a discussion log");
274
+
275
+ const draft = readFileSync(draftPath, "utf-8");
276
+ assert.match(draft, /What are you picturing for M004\?/);
277
+ assert.match(draft, /Planning metadata \(Recommended\)/);
278
+ assert.match(draft, /No new dependencies \(Recommended\)/);
279
+
280
+ const row = getMilestone("M004");
281
+ assert.equal(row?.status, "queued", "new milestone shell should be registered in the DB");
282
+
283
+ invalidateStateCache();
284
+ const state = await deriveState(dir);
285
+ assert.equal(state.activeMilestone?.id, "M004");
286
+ assert.equal(state.phase, "needs-discussion");
287
+ });
288
+
189
289
  test("register-hooks clears depth gate when remote (Telegram/Slack/Discord) answer is normalized (#4406)", async (t) => {
190
290
  const dir = makeTempDir("remote");
191
291
  const originalCwd = process.cwd();
@@ -2,7 +2,7 @@
2
2
 
3
3
  import test from "node:test";
4
4
  import assert from "node:assert/strict";
5
- import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
5
+ import { mkdtempSync, mkdirSync, realpathSync, rmSync, writeFileSync } from "node:fs";
6
6
  import { tmpdir } from "node:os";
7
7
  import { join } from "node:path";
8
8
  import { execFileSync } from "node:child_process";
@@ -117,7 +117,7 @@ test("repository registry keeps project root anchored to .gsd project in monorep
117
117
  });
118
118
 
119
119
  test("repository registry uses external-state worktree checkout as project root", (t) => {
120
- const base = mkdtempSync(join(tmpdir(), "gsd-repo-registry-external-"));
120
+ const base = realpathSync(mkdtempSync(join(tmpdir(), "gsd-repo-registry-external-")));
121
121
  t.after(() => rmSync(base, { recursive: true, force: true }));
122
122
  const worktree = join(base, ".gsd", "projects", "abc123", "worktrees", "M001");
123
123
  mkdirSync(worktree, { recursive: true });
@@ -67,6 +67,14 @@ test("Tool Contract compiles known Unit prompt and tool policy", () => {
67
67
  assert.deepEqual(result.ok && result.contract.forbiddenWorkflowTools, []);
68
68
  assert.equal(result.ok && result.contract.toolsPolicy.mode, "all");
69
69
  assert.ok(result.ok && result.contract.validationRules.includes("closeout-tool-present"));
70
+ assert.ok(result.ok && result.contract.validationRules.includes("source-observation-contract-present"));
71
+ assert.deepEqual(result.ok && result.contract.sourceObservations, {
72
+ mode: "whole-file-active-unit",
73
+ seedFields: ["task.files", "task.inputs"],
74
+ excludedFields: ["expectedOutput"],
75
+ maxBytes: 50 * 1024,
76
+ maxLines: 2000,
77
+ });
70
78
  });
71
79
 
72
80
  test("Tool Contract records high-risk cross-phase tool boundaries without single-owning every tool", () => {
@@ -108,6 +116,26 @@ test("auto Unit tool scope blocks complete-slice from saving UAT Assessment", ()
108
116
  assert.match(result.reason ?? "", /Run UAT owns persisted UAT Assessment/);
109
117
  });
110
118
 
119
+ test("auto Unit tool scope allows plan-slice to reassess invalid roadmap assumptions", () => {
120
+ const result = shouldBlockAutoUnitToolCall("plan-slice", "gsd_reassess_roadmap");
121
+
122
+ assert.equal(result.block, false);
123
+ });
124
+
125
+ test("auto Unit tool scope allows status/read helpers named by closeout prompts", () => {
126
+ for (const unitType of ["plan-milestone", "validate-milestone", "complete-milestone", "reassess-roadmap"]) {
127
+ const result = shouldBlockAutoUnitToolCall(unitType, "gsd_milestone_status");
128
+ assert.equal(result.block, false, `${unitType} should be able to call gsd_milestone_status`);
129
+ }
130
+ });
131
+
132
+ test("auto Unit tool scope blocks stale per-task planner in slice planning phases", () => {
133
+ for (const unitType of ["plan-slice", "refine-slice", "replan-slice"]) {
134
+ const result = shouldBlockAutoUnitToolCall(unitType, "gsd_plan_task");
135
+ assert.equal(result.block, true, `${unitType} should not call stale gsd_plan_task`);
136
+ }
137
+ });
138
+
111
139
  test("Recovery Classification covers ADR-015 failure families", () => {
112
140
  const cases = [
113
141
  ["invalid tool schema enum", "tool-schema", "stop"],
@@ -24,6 +24,7 @@ import {
24
24
  openDatabase,
25
25
  closeDatabase,
26
26
  _getAdapter,
27
+ SCHEMA_VERSION,
27
28
  } from '../gsd-db.ts';
28
29
 
29
30
  const _require = createRequire(import.meta.url);
@@ -403,9 +404,10 @@ test('schema v21 migration: upgrading from v20 lands on current SCHEMA_VERSION',
403
404
  // The DB must reach the current SCHEMA_VERSION. If the v21 block was
404
405
  // skipped (as it would be when SCHEMA_VERSION was still 20), this
405
406
  // assertion catches it.
406
- assert.ok(
407
- maxVersion >= 21,
408
- `DB upgraded from v20 must reach at least schema version 21; got ${maxVersion}`,
407
+ assert.equal(
408
+ maxVersion,
409
+ SCHEMA_VERSION,
410
+ `DB upgraded from v20 must reach schema version ${SCHEMA_VERSION}; got ${maxVersion}`,
409
411
  );
410
412
  } finally {
411
413
  cleanup(base);
@@ -1,16 +1,17 @@
1
- // gsd-pi / V27 + V28 schema migration regression tests
1
+ // gsd-pi / V27 + V28 + V29 schema migration regression tests
2
2
  //
3
3
  // Same bug class as #4591 (schema-v21-sequence): a migration block can be
4
4
  // added but the SCHEMA_VERSION constant left unchanged, causing fresh-install
5
- // + upgrade paths to silently skip the column add. This file pins both V27
6
- // (artifacts.content_hash) and V28 (memories.last_hit_at) at the schema and
7
- // write-path level on fresh-install DBs.
5
+ // + upgrade paths to silently skip the column add. This file pins V27
6
+ // (artifacts.content_hash), V28 (memories.last_hit_at), and V29
7
+ // (target_repositories) at the schema/write-path level.
8
8
 
9
9
  import test from "node:test";
10
10
  import assert from "node:assert/strict";
11
11
  import * as fs from "node:fs";
12
12
  import * as path from "node:path";
13
13
  import * as os from "node:os";
14
+ import { createRequire } from "node:module";
14
15
 
15
16
  import {
16
17
  openDatabase,
@@ -22,8 +23,10 @@ import {
22
23
  SCHEMA_VERSION,
23
24
  } from "../gsd-db.ts";
24
25
 
26
+ const _require = createRequire(import.meta.url);
27
+
25
28
  function makeTmp(): string {
26
- return fs.mkdtempSync(path.join(os.tmpdir(), "gsd-v27v28-"));
29
+ return fs.mkdtempSync(path.join(os.tmpdir(), "gsd-v27v29-"));
27
30
  }
28
31
 
29
32
  function cleanup(base: string): void {
@@ -31,10 +34,114 @@ function cleanup(base: string): void {
31
34
  try { fs.rmSync(base, { recursive: true, force: true }); } catch { /* noop */ }
32
35
  }
33
36
 
34
- test("SCHEMA_VERSION constant is at least 28 (V28 migration committed)", () => {
37
+ function columnNames(table: string): Set<string> {
38
+ const db = _getAdapter()!;
39
+ const cols = db.prepare(`PRAGMA table_info(${table})`).all() as Array<Record<string, unknown>>;
40
+ return new Set(cols.map((c) => c["name"] as string));
41
+ }
42
+
43
+ function createV28Db(dbPath: string): void {
44
+ const sqlite = _require("node:sqlite") as {
45
+ DatabaseSync: new (p: string) => {
46
+ exec(sql: string): void;
47
+ close(): void;
48
+ };
49
+ };
50
+ const db = new sqlite.DatabaseSync(dbPath);
51
+ db.exec("PRAGMA journal_mode=WAL");
52
+ db.exec(`
53
+ CREATE TABLE schema_version (
54
+ version INTEGER NOT NULL,
55
+ applied_at TEXT NOT NULL
56
+ );
57
+ INSERT INTO schema_version (version, applied_at) VALUES (28, '2026-01-01T00:00:00.000Z');
58
+
59
+ CREATE TABLE milestones (
60
+ id TEXT PRIMARY KEY,
61
+ title TEXT NOT NULL DEFAULT '',
62
+ status TEXT NOT NULL DEFAULT 'active',
63
+ depends_on TEXT NOT NULL DEFAULT '[]',
64
+ created_at TEXT NOT NULL DEFAULT '',
65
+ completed_at TEXT DEFAULT NULL,
66
+ vision TEXT NOT NULL DEFAULT '',
67
+ success_criteria TEXT NOT NULL DEFAULT '[]',
68
+ key_risks TEXT NOT NULL DEFAULT '[]',
69
+ proof_strategy TEXT NOT NULL DEFAULT '[]',
70
+ verification_contract TEXT NOT NULL DEFAULT '',
71
+ verification_integration TEXT NOT NULL DEFAULT '',
72
+ verification_operational TEXT NOT NULL DEFAULT '',
73
+ verification_uat TEXT NOT NULL DEFAULT '',
74
+ definition_of_done TEXT NOT NULL DEFAULT '[]',
75
+ requirement_coverage TEXT NOT NULL DEFAULT '',
76
+ boundary_map_markdown TEXT NOT NULL DEFAULT '',
77
+ sequence INTEGER DEFAULT 0
78
+ );
79
+ CREATE TABLE slices (
80
+ milestone_id TEXT NOT NULL,
81
+ id TEXT NOT NULL,
82
+ title TEXT NOT NULL DEFAULT '',
83
+ status TEXT NOT NULL DEFAULT 'pending',
84
+ risk TEXT NOT NULL DEFAULT 'medium',
85
+ depends TEXT NOT NULL DEFAULT '[]',
86
+ demo TEXT NOT NULL DEFAULT '',
87
+ created_at TEXT NOT NULL DEFAULT '',
88
+ completed_at TEXT DEFAULT NULL,
89
+ full_summary_md TEXT NOT NULL DEFAULT '',
90
+ full_uat_md TEXT NOT NULL DEFAULT '',
91
+ goal TEXT NOT NULL DEFAULT '',
92
+ success_criteria TEXT NOT NULL DEFAULT '',
93
+ proof_level TEXT NOT NULL DEFAULT '',
94
+ integration_closure TEXT NOT NULL DEFAULT '',
95
+ observability_impact TEXT NOT NULL DEFAULT '',
96
+ sequence INTEGER DEFAULT 0,
97
+ replan_triggered_at TEXT DEFAULT NULL,
98
+ is_sketch INTEGER NOT NULL DEFAULT 0,
99
+ sketch_scope TEXT NOT NULL DEFAULT '',
100
+ PRIMARY KEY (milestone_id, id),
101
+ FOREIGN KEY (milestone_id) REFERENCES milestones(id)
102
+ );
103
+ CREATE TABLE tasks (
104
+ milestone_id TEXT NOT NULL,
105
+ slice_id TEXT NOT NULL,
106
+ id TEXT NOT NULL,
107
+ title TEXT NOT NULL DEFAULT '',
108
+ status TEXT NOT NULL DEFAULT 'pending',
109
+ one_liner TEXT NOT NULL DEFAULT '',
110
+ narrative TEXT NOT NULL DEFAULT '',
111
+ verification_result TEXT NOT NULL DEFAULT '',
112
+ duration TEXT NOT NULL DEFAULT '',
113
+ completed_at TEXT DEFAULT NULL,
114
+ blocker_discovered INTEGER DEFAULT 0,
115
+ blocker_source TEXT NOT NULL DEFAULT '',
116
+ escalation_pending INTEGER NOT NULL DEFAULT 0,
117
+ escalation_awaiting_review INTEGER NOT NULL DEFAULT 0,
118
+ escalation_artifact_path TEXT DEFAULT NULL,
119
+ escalation_override_applied_at TEXT DEFAULT NULL,
120
+ deviations TEXT NOT NULL DEFAULT '',
121
+ known_issues TEXT NOT NULL DEFAULT '',
122
+ key_files TEXT NOT NULL DEFAULT '[]',
123
+ key_decisions TEXT NOT NULL DEFAULT '[]',
124
+ full_summary_md TEXT NOT NULL DEFAULT '',
125
+ description TEXT NOT NULL DEFAULT '',
126
+ estimate TEXT NOT NULL DEFAULT '',
127
+ files TEXT NOT NULL DEFAULT '[]',
128
+ verify TEXT NOT NULL DEFAULT '',
129
+ inputs TEXT NOT NULL DEFAULT '[]',
130
+ expected_output TEXT NOT NULL DEFAULT '[]',
131
+ observability_impact TEXT NOT NULL DEFAULT '',
132
+ full_plan_md TEXT NOT NULL DEFAULT '',
133
+ sequence INTEGER DEFAULT 0,
134
+ PRIMARY KEY (milestone_id, slice_id, id),
135
+ FOREIGN KEY (milestone_id, slice_id) REFERENCES slices(milestone_id, id)
136
+ );
137
+ `);
138
+ db.close();
139
+ }
140
+
141
+ test("SCHEMA_VERSION constant is at least 29 (V29 migration committed)", () => {
35
142
  assert.ok(
36
- SCHEMA_VERSION >= 28,
37
- `SCHEMA_VERSION must be ≥ 28 after V28 migration; got ${SCHEMA_VERSION}`,
143
+ SCHEMA_VERSION >= 29,
144
+ `SCHEMA_VERSION must be ≥ 29 after V29 migration; got ${SCHEMA_VERSION}`,
38
145
  );
39
146
  });
40
147
 
@@ -43,11 +150,8 @@ test("fresh-install DB has artifacts.content_hash column (V27)", () => {
43
150
  const dbPath = path.join(base, "gsd.db");
44
151
  try {
45
152
  openDatabase(dbPath);
46
- const db = _getAdapter()!;
47
- const cols = db.prepare("PRAGMA table_info(artifacts)").all() as Array<Record<string, unknown>>;
48
- const colNames = new Set(cols.map((c) => c["name"] as string));
49
153
  assert.ok(
50
- colNames.has("content_hash"),
154
+ columnNames("artifacts").has("content_hash"),
51
155
  "V27 must add content_hash column to artifacts on fresh install",
52
156
  );
53
157
  } finally {
@@ -60,11 +164,8 @@ test("fresh-install DB has memories.last_hit_at column (V28)", () => {
60
164
  const dbPath = path.join(base, "gsd.db");
61
165
  try {
62
166
  openDatabase(dbPath);
63
- const db = _getAdapter()!;
64
- const cols = db.prepare("PRAGMA table_info(memories)").all() as Array<Record<string, unknown>>;
65
- const colNames = new Set(cols.map((c) => c["name"] as string));
66
167
  assert.ok(
67
- colNames.has("last_hit_at"),
168
+ columnNames("memories").has("last_hit_at"),
68
169
  "V28 must add last_hit_at column to memories on fresh install",
69
170
  );
70
171
  } finally {
@@ -72,15 +173,58 @@ test("fresh-install DB has memories.last_hit_at column (V28)", () => {
72
173
  }
73
174
  });
74
175
 
75
- test("fresh-install DB stamps SCHEMA_VERSION (≥28) in schema_version table", () => {
176
+ test("fresh-install DB has target_repositories columns (V29)", () => {
76
177
  const base = makeTmp();
77
178
  const dbPath = path.join(base, "gsd.db");
78
179
  try {
79
180
  openDatabase(dbPath);
181
+ assert.ok(
182
+ columnNames("slices").has("target_repositories"),
183
+ "V29 must add target_repositories column to slices on fresh install",
184
+ );
185
+ assert.ok(
186
+ columnNames("tasks").has("target_repositories"),
187
+ "V29 must add target_repositories column to tasks on fresh install",
188
+ );
189
+ } finally {
190
+ cleanup(base);
191
+ }
192
+ });
193
+
194
+ test("fresh-install DB stamps current SCHEMA_VERSION in schema_version table", () => {
195
+ const base = makeTmp();
196
+ const dbPath = path.join(base, "gsd.db");
197
+ try {
198
+ openDatabase(dbPath);
199
+ const db = _getAdapter()!;
200
+ const row = db.prepare("SELECT MAX(version) as v FROM schema_version").get() as Record<string, unknown> | undefined;
201
+ const max = (row?.["v"] as number) ?? 0;
202
+ assert.equal(max, SCHEMA_VERSION, `fresh install must record schema_version ${SCHEMA_VERSION}; got ${max}`);
203
+ } finally {
204
+ cleanup(base);
205
+ }
206
+ });
207
+
208
+ test("upgrading from V28 adds target_repositories and stamps V29", () => {
209
+ const base = makeTmp();
210
+ const dbPath = path.join(base, "gsd.db");
211
+ createV28Db(dbPath);
212
+
213
+ try {
214
+ openDatabase(dbPath);
215
+ assert.ok(
216
+ columnNames("slices").has("target_repositories"),
217
+ "V29 migration must add target_repositories column to existing slices table",
218
+ );
219
+ assert.ok(
220
+ columnNames("tasks").has("target_repositories"),
221
+ "V29 migration must add target_repositories column to existing tasks table",
222
+ );
223
+
80
224
  const db = _getAdapter()!;
81
225
  const row = db.prepare("SELECT MAX(version) as v FROM schema_version").get() as Record<string, unknown> | undefined;
82
226
  const max = (row?.["v"] as number) ?? 0;
83
- assert.ok(max >= 28, `fresh install must record schema_version ≥ 28; got ${max}`);
227
+ assert.equal(max, SCHEMA_VERSION, `V28 DB must upgrade to schema_version ${SCHEMA_VERSION}; got ${max}`);
84
228
  } finally {
85
229
  cleanup(base);
86
230
  }