gsd-pi 2.80.0-dev.c5f2443b3 → 2.80.0-dev.e146beb20

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 (342) hide show
  1. package/dist/resources/.managed-resources-content-hash +1 -1
  2. package/dist/resources/GSD-WORKFLOW.md +2 -2
  3. package/dist/resources/extensions/github-sync/templates.js +39 -8
  4. package/dist/resources/extensions/gsd/auto/loop.js +48 -10
  5. package/dist/resources/extensions/gsd/auto/phases.js +37 -30
  6. package/dist/resources/extensions/gsd/auto/run-unit.js +19 -15
  7. package/dist/resources/extensions/gsd/auto-dashboard.js +51 -15
  8. package/dist/resources/extensions/gsd/auto-dispatch.js +10 -0
  9. package/dist/resources/extensions/gsd/auto-post-unit.js +10 -10
  10. package/dist/resources/extensions/gsd/auto-prompts.js +111 -1
  11. package/dist/resources/extensions/gsd/auto-recovery.js +154 -8
  12. package/dist/resources/extensions/gsd/auto-start.js +2 -3
  13. package/dist/resources/extensions/gsd/auto.js +9 -1
  14. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +15 -1
  15. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +11 -1
  16. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +129 -1
  17. package/dist/resources/extensions/gsd/clean-root-preflight.js +42 -4
  18. package/dist/resources/extensions/gsd/commands/dispatcher.js +5 -0
  19. package/dist/resources/extensions/gsd/commands-extract-learnings.js +17 -12
  20. package/dist/resources/extensions/gsd/crash-recovery.js +56 -10
  21. package/dist/resources/extensions/gsd/custom-workflow-engine.js +22 -2
  22. package/dist/resources/extensions/gsd/db-base-schema.js +14 -0
  23. package/dist/resources/extensions/gsd/db-migration-steps.js +16 -0
  24. package/dist/resources/extensions/gsd/detection.js +106 -0
  25. package/dist/resources/extensions/gsd/graph.js +9 -3
  26. package/dist/resources/extensions/gsd/gsd-db.js +102 -2
  27. package/dist/resources/extensions/gsd/guided-flow.js +49 -12
  28. package/dist/resources/extensions/gsd/planning-path-scope.js +26 -0
  29. package/dist/resources/extensions/gsd/pr-evidence.js +57 -16
  30. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +7 -8
  31. package/dist/resources/extensions/gsd/prompts/plan-milestone.md +3 -1
  32. package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  33. package/dist/resources/extensions/gsd/safety/evidence-collector.js +10 -2
  34. package/dist/resources/extensions/gsd/tools/plan-slice.js +9 -0
  35. package/dist/resources/extensions/gsd/tools/plan-task.js +9 -0
  36. package/dist/resources/extensions/gsd/unit-runtime.js +11 -0
  37. package/dist/resources/extensions/gsd/working-output-messages.js +64 -0
  38. package/dist/resources/extensions/gsd/worktree-manager.js +16 -14
  39. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  40. package/dist/web/standalone/.next/BUILD_ID +1 -1
  41. package/dist/web/standalone/.next/app-path-routes-manifest.json +16 -16
  42. package/dist/web/standalone/.next/build-manifest.json +3 -3
  43. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  44. package/dist/web/standalone/.next/react-loadable-manifest.json +1 -1
  45. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  46. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  47. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  48. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  49. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  50. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  51. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  52. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  53. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  54. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  55. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  56. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  57. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  58. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  59. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  60. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  61. package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
  62. package/dist/web/standalone/.next/server/app/index.html +1 -1
  63. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  64. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  65. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  66. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  67. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  68. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  69. package/dist/web/standalone/.next/server/app-paths-manifest.json +16 -16
  70. package/dist/web/standalone/.next/server/chunks/6897.js +3 -3
  71. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  72. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  73. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  74. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  75. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  76. package/dist/web/standalone/.next/static/chunks/{8336.6f6f30e410419aff.js → 8336.631939fb583761fa.js} +1 -1
  77. package/dist/web/standalone/.next/static/chunks/{webpack-d82dbee6356c1733.js → webpack-0481f1221120a7c6.js} +1 -1
  78. package/package.json +10 -6
  79. package/packages/contracts/package.json +1 -1
  80. package/packages/pi-ai/dist/models/fake-model.d.ts +12 -0
  81. package/packages/pi-ai/dist/models/fake-model.d.ts.map +1 -0
  82. package/packages/pi-ai/dist/models/fake-model.js +27 -0
  83. package/packages/pi-ai/dist/models/fake-model.js.map +1 -0
  84. package/packages/pi-ai/dist/models/index.d.ts.map +1 -1
  85. package/packages/pi-ai/dist/models/index.js +8 -0
  86. package/packages/pi-ai/dist/models/index.js.map +1 -1
  87. package/packages/pi-ai/dist/providers/fake.d.ts +42 -0
  88. package/packages/pi-ai/dist/providers/fake.d.ts.map +1 -0
  89. package/packages/pi-ai/dist/providers/fake.js +319 -0
  90. package/packages/pi-ai/dist/providers/fake.js.map +1 -0
  91. package/packages/pi-ai/dist/providers/register-builtins.d.ts.map +1 -1
  92. package/packages/pi-ai/dist/providers/register-builtins.js +24 -0
  93. package/packages/pi-ai/dist/providers/register-builtins.js.map +1 -1
  94. package/packages/pi-ai/src/models/fake-model.ts +30 -0
  95. package/packages/pi-ai/src/models/index.ts +9 -0
  96. package/packages/pi-ai/src/providers/fake.ts +376 -0
  97. package/packages/pi-ai/src/providers/register-builtins.ts +23 -0
  98. package/packages/pi-ai/tsconfig.tsbuildinfo +1 -1
  99. package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js +74 -0
  100. package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js.map +1 -1
  101. package/packages/pi-coding-agent/dist/core/db-snapshot.d.ts +15 -0
  102. package/packages/pi-coding-agent/dist/core/db-snapshot.d.ts.map +1 -0
  103. package/packages/pi-coding-agent/dist/core/db-snapshot.js +66 -0
  104. package/packages/pi-coding-agent/dist/core/db-snapshot.js.map +1 -0
  105. package/packages/pi-coding-agent/dist/core/db-snapshot.test.d.ts +2 -0
  106. package/packages/pi-coding-agent/dist/core/db-snapshot.test.d.ts.map +1 -0
  107. package/packages/pi-coding-agent/dist/core/db-snapshot.test.js +24 -0
  108. package/packages/pi-coding-agent/dist/core/db-snapshot.test.js.map +1 -0
  109. package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts +2 -0
  110. package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts.map +1 -1
  111. package/packages/pi-coding-agent/dist/core/extensions/runner.js +14 -1
  112. package/packages/pi-coding-agent/dist/core/extensions/runner.js.map +1 -1
  113. package/packages/pi-coding-agent/dist/core/extensions/runner.test.js +97 -0
  114. package/packages/pi-coding-agent/dist/core/extensions/runner.test.js.map +1 -1
  115. package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
  116. package/packages/pi-coding-agent/dist/core/model-registry.js +5 -0
  117. package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
  118. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +4 -0
  119. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
  120. package/packages/pi-coding-agent/dist/core/settings-manager.js +8 -0
  121. package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
  122. package/packages/pi-coding-agent/dist/core/slash-commands.d.ts.map +1 -1
  123. package/packages/pi-coding-agent/dist/core/slash-commands.js +1 -0
  124. package/packages/pi-coding-agent/dist/core/slash-commands.js.map +1 -1
  125. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/chat-frame-compaction-tone.test.js +6 -4
  126. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/chat-frame-compaction-tone.test.js.map +1 -1
  127. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js +54 -15
  128. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js.map +1 -1
  129. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.d.ts +26 -0
  130. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.d.ts.map +1 -0
  131. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.js +112 -0
  132. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.js.map +1 -0
  133. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.test.d.ts +2 -0
  134. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.test.d.ts.map +1 -0
  135. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.test.js +51 -0
  136. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.test.js.map +1 -0
  137. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  138. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js.map +1 -1
  139. package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.d.ts.map +1 -1
  140. package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.js +10 -9
  141. package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.js.map +1 -1
  142. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts +3 -0
  143. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  144. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js +11 -0
  145. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js.map +1 -1
  146. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.js +7 -6
  147. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.js.map +1 -1
  148. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts +16 -0
  149. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  150. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +106 -17
  151. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
  152. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  153. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +60 -1
  154. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  155. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.test.js +40 -1
  156. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.test.js.map +1 -1
  157. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js +1 -0
  158. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js.map +1 -1
  159. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts +1 -0
  160. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts.map +1 -1
  161. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.js.map +1 -1
  162. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +3 -0
  163. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  164. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +23 -0
  165. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  166. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.d.ts.map +1 -1
  167. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.js +20 -0
  168. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.js.map +1 -1
  169. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.test.d.ts +2 -0
  170. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.test.d.ts.map +1 -0
  171. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.test.js +79 -0
  172. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.test.js.map +1 -0
  173. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-schema.d.ts +12 -0
  174. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-schema.d.ts.map +1 -1
  175. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-schema.js +13 -0
  176. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-schema.js.map +1 -1
  177. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.d.ts +1 -1
  178. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  179. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js +18 -1
  180. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js.map +1 -1
  181. package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.d.ts.map +1 -1
  182. package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.js +36 -27
  183. package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.js.map +1 -1
  184. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.d.ts +11 -0
  185. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.d.ts.map +1 -0
  186. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.js +18 -0
  187. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.js.map +1 -0
  188. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.test.d.ts +2 -0
  189. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.test.d.ts.map +1 -0
  190. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.test.js +48 -0
  191. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.test.js.map +1 -0
  192. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage-safety-guard.test.d.ts +2 -0
  193. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage-safety-guard.test.d.ts.map +1 -0
  194. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage-safety-guard.test.js +10 -0
  195. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage-safety-guard.test.js.map +1 -0
  196. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.d.ts.map +1 -1
  197. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.js +3 -2
  198. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.js.map +1 -1
  199. package/packages/pi-coding-agent/src/core/chat-controller-ordering.test.ts +87 -0
  200. package/packages/pi-coding-agent/src/core/db-snapshot.test.ts +32 -0
  201. package/packages/pi-coding-agent/src/core/db-snapshot.ts +66 -0
  202. package/packages/pi-coding-agent/src/core/extensions/runner.test.ts +108 -0
  203. package/packages/pi-coding-agent/src/core/extensions/runner.ts +16 -1
  204. package/packages/pi-coding-agent/src/core/model-registry.ts +4 -0
  205. package/packages/pi-coding-agent/src/core/settings-manager.ts +12 -0
  206. package/packages/pi-coding-agent/src/core/slash-commands.ts +1 -0
  207. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/chat-frame-compaction-tone.test.ts +7 -5
  208. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/tool-execution.test.ts +78 -15
  209. package/packages/pi-coding-agent/src/modes/interactive/components/adaptive-layout.test.ts +59 -0
  210. package/packages/pi-coding-agent/src/modes/interactive/components/adaptive-layout.ts +160 -0
  211. package/packages/pi-coding-agent/src/modes/interactive/components/assistant-message.ts +1 -0
  212. package/packages/pi-coding-agent/src/modes/interactive/components/chat-frame.ts +10 -9
  213. package/packages/pi-coding-agent/src/modes/interactive/components/settings-selector.ts +15 -0
  214. package/packages/pi-coding-agent/src/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.ts +10 -9
  215. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +118 -17
  216. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.test.ts +43 -1
  217. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +75 -1
  218. package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.test.ts +1 -0
  219. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode-state.ts +1 -1
  220. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +25 -0
  221. package/packages/pi-coding-agent/src/modes/interactive/slash-command-handlers.test.ts +95 -0
  222. package/packages/pi-coding-agent/src/modes/interactive/slash-command-handlers.ts +24 -1
  223. package/packages/pi-coding-agent/src/modes/interactive/theme/theme-schema.ts +13 -0
  224. package/packages/pi-coding-agent/src/modes/interactive/theme/theme.ts +32 -2
  225. package/packages/pi-coding-agent/src/modes/interactive/theme/themes.ts +36 -27
  226. package/packages/pi-coding-agent/src/modes/interactive/tui-mode.test.ts +65 -0
  227. package/packages/pi-coding-agent/src/modes/interactive/tui-mode.ts +29 -0
  228. package/packages/pi-coding-agent/src/resources/extensions/memory/storage-safety-guard.test.ts +14 -0
  229. package/packages/pi-coding-agent/src/resources/extensions/memory/storage.ts +3 -2
  230. package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
  231. package/packages/pi-tui/dist/__tests__/style.test.d.ts +2 -0
  232. package/packages/pi-tui/dist/__tests__/style.test.d.ts.map +1 -0
  233. package/packages/pi-tui/dist/__tests__/style.test.js +63 -0
  234. package/packages/pi-tui/dist/__tests__/style.test.js.map +1 -0
  235. package/packages/pi-tui/dist/__tests__/tui.test.js +24 -3
  236. package/packages/pi-tui/dist/__tests__/tui.test.js.map +1 -1
  237. package/packages/pi-tui/dist/index.d.ts +1 -0
  238. package/packages/pi-tui/dist/index.d.ts.map +1 -1
  239. package/packages/pi-tui/dist/index.js +2 -0
  240. package/packages/pi-tui/dist/index.js.map +1 -1
  241. package/packages/pi-tui/dist/style.d.ts +41 -0
  242. package/packages/pi-tui/dist/style.d.ts.map +1 -0
  243. package/packages/pi-tui/dist/style.js +158 -0
  244. package/packages/pi-tui/dist/style.js.map +1 -0
  245. package/packages/pi-tui/dist/tui.d.ts +0 -1
  246. package/packages/pi-tui/dist/tui.d.ts.map +1 -1
  247. package/packages/pi-tui/dist/tui.js +3 -8
  248. package/packages/pi-tui/dist/tui.js.map +1 -1
  249. package/packages/pi-tui/src/__tests__/style.test.ts +76 -0
  250. package/packages/pi-tui/src/__tests__/tui.test.ts +29 -3
  251. package/packages/pi-tui/src/index.ts +9 -0
  252. package/packages/pi-tui/src/style.ts +225 -0
  253. package/packages/pi-tui/src/tui.ts +3 -8
  254. package/packages/pi-tui/tsconfig.tsbuildinfo +1 -1
  255. package/pkg/dist/modes/interactive/theme/theme-schema.d.ts +12 -0
  256. package/pkg/dist/modes/interactive/theme/theme-schema.d.ts.map +1 -1
  257. package/pkg/dist/modes/interactive/theme/theme-schema.js +13 -0
  258. package/pkg/dist/modes/interactive/theme/theme-schema.js.map +1 -1
  259. package/pkg/dist/modes/interactive/theme/theme.d.ts +1 -1
  260. package/pkg/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  261. package/pkg/dist/modes/interactive/theme/theme.js +18 -1
  262. package/pkg/dist/modes/interactive/theme/theme.js.map +1 -1
  263. package/pkg/dist/modes/interactive/theme/themes.d.ts.map +1 -1
  264. package/pkg/dist/modes/interactive/theme/themes.js +36 -27
  265. package/pkg/dist/modes/interactive/theme/themes.js.map +1 -1
  266. package/src/resources/GSD-WORKFLOW.md +2 -2
  267. package/src/resources/extensions/github-sync/templates.ts +38 -8
  268. package/src/resources/extensions/github-sync/tests/inline-code.test.ts +66 -0
  269. package/src/resources/extensions/gsd/auto/loop-deps.ts +1 -0
  270. package/src/resources/extensions/gsd/auto/loop.ts +67 -18
  271. package/src/resources/extensions/gsd/auto/phases.ts +42 -28
  272. package/src/resources/extensions/gsd/auto/run-unit.ts +24 -14
  273. package/src/resources/extensions/gsd/auto-dashboard.ts +57 -8
  274. package/src/resources/extensions/gsd/auto-dispatch.ts +17 -0
  275. package/src/resources/extensions/gsd/auto-post-unit.ts +10 -10
  276. package/src/resources/extensions/gsd/auto-prompts.ts +116 -1
  277. package/src/resources/extensions/gsd/auto-recovery.ts +153 -7
  278. package/src/resources/extensions/gsd/auto-start.ts +7 -6
  279. package/src/resources/extensions/gsd/auto.ts +12 -1
  280. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +16 -1
  281. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +17 -1
  282. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +135 -1
  283. package/src/resources/extensions/gsd/clean-root-preflight.ts +41 -3
  284. package/src/resources/extensions/gsd/commands/dispatcher.ts +6 -0
  285. package/src/resources/extensions/gsd/commands-extract-learnings.ts +17 -12
  286. package/src/resources/extensions/gsd/crash-recovery.ts +67 -10
  287. package/src/resources/extensions/gsd/custom-workflow-engine.ts +24 -1
  288. package/src/resources/extensions/gsd/db-base-schema.ts +15 -0
  289. package/src/resources/extensions/gsd/db-migration-steps.ts +17 -0
  290. package/src/resources/extensions/gsd/detection.ts +128 -0
  291. package/src/resources/extensions/gsd/graph.ts +12 -5
  292. package/src/resources/extensions/gsd/gsd-db.ts +119 -1
  293. package/src/resources/extensions/gsd/guided-flow.ts +49 -12
  294. package/src/resources/extensions/gsd/planning-path-scope.ts +35 -0
  295. package/src/resources/extensions/gsd/pr-evidence.ts +63 -5
  296. package/src/resources/extensions/gsd/prompts/complete-milestone.md +7 -8
  297. package/src/resources/extensions/gsd/prompts/plan-milestone.md +3 -1
  298. package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  299. package/src/resources/extensions/gsd/safety/evidence-collector.ts +11 -2
  300. package/src/resources/extensions/gsd/tests/auto-abort-pause-regression.test.ts +7 -1
  301. package/src/resources/extensions/gsd/tests/auto-dashboard.test.ts +33 -0
  302. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +84 -5
  303. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +170 -1
  304. package/src/resources/extensions/gsd/tests/clean-root-preflight.test.ts +88 -2
  305. package/src/resources/extensions/gsd/tests/commands-extract-learnings.test.ts +9 -0
  306. package/src/resources/extensions/gsd/tests/crash-handler-secondary.test.ts +55 -0
  307. package/src/resources/extensions/gsd/tests/crash-recovery-via-db.test.ts +22 -0
  308. package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +112 -6
  309. package/src/resources/extensions/gsd/tests/custom-workflow-engine.test.ts +40 -2
  310. package/src/resources/extensions/gsd/tests/db-migration-steps.integration.test.ts +428 -0
  311. package/src/resources/extensions/gsd/tests/db-schema-metadata.test.ts +2 -2
  312. package/src/resources/extensions/gsd/tests/detection.test.ts +140 -0
  313. package/src/resources/extensions/gsd/tests/fixtures/pr-body/commands-ship-basic.md +52 -0
  314. package/src/resources/extensions/gsd/tests/fixtures/pr-body/commands-ship-empty-optionals.md +42 -0
  315. package/src/resources/extensions/gsd/tests/fixtures/pr-body/swarm-lane-no-blockers.md +55 -0
  316. package/src/resources/extensions/gsd/tests/fixtures/pr-body/swarm-lane-with-blockers.md +60 -0
  317. package/src/resources/extensions/gsd/tests/graph-operations.test.ts +10 -0
  318. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +44 -0
  319. package/src/resources/extensions/gsd/tests/has-pending-deep-stage.test.ts +33 -1
  320. package/src/resources/extensions/gsd/tests/plan-slice.test.ts +50 -0
  321. package/src/resources/extensions/gsd/tests/plan-task.test.ts +21 -0
  322. package/src/resources/extensions/gsd/tests/pr-evidence-equivalence.test.ts +102 -0
  323. package/src/resources/extensions/gsd/tests/pr-evidence-hardening.test.ts +165 -0
  324. package/src/resources/extensions/gsd/tests/right-sized-workflow-prompts.test.ts +192 -0
  325. package/src/resources/extensions/gsd/tests/safety-harness-false-positives.test.ts +29 -0
  326. package/src/resources/extensions/gsd/tests/smart-entry-complete.test.ts +38 -0
  327. package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +46 -2
  328. package/src/resources/extensions/gsd/tests/unit-runtime.test.ts +7 -0
  329. package/src/resources/extensions/gsd/tests/uok-plan-v2-wiring.test.ts +1 -1
  330. package/src/resources/extensions/gsd/tests/working-output-messages.test.ts +93 -0
  331. package/src/resources/extensions/gsd/tests/worktree-health-dispatch.test.ts +37 -6
  332. package/src/resources/extensions/gsd/tests/worktree-manager.test.ts +7 -0
  333. package/src/resources/extensions/gsd/tests/worktree-nested-git-safety.test.ts +9 -2
  334. package/src/resources/extensions/gsd/tests/worktree-write-gate.test.ts +179 -0
  335. package/src/resources/extensions/gsd/tools/plan-slice.ts +13 -0
  336. package/src/resources/extensions/gsd/tools/plan-task.ts +10 -0
  337. package/src/resources/extensions/gsd/unit-runtime.ts +14 -0
  338. package/src/resources/extensions/gsd/working-output-messages.ts +120 -0
  339. package/src/resources/extensions/gsd/worktree-manager.ts +15 -4
  340. package/packages/contracts/tsconfig.tsbuildinfo +0 -1
  341. /package/dist/web/standalone/.next/static/{bQDK5_LtkGVS64AirQgQG → y73quA-XdLo9n41nxphjW}/_buildManifest.js +0 -0
  342. /package/dist/web/standalone/.next/static/{bQDK5_LtkGVS64AirQgQG → y73quA-XdLo9n41nxphjW}/_ssgManifest.js +0 -0
@@ -30,6 +30,7 @@ import {
30
30
  isLockProcessAlive,
31
31
  } from "../crash-recovery.ts";
32
32
  import { normalizeRealPath } from "../paths.ts";
33
+ import { writeUnitRuntimeRecord } from "../unit-runtime.ts";
33
34
 
34
35
  function makeBase(): string {
35
36
  const base = mkdtempSync(join(tmpdir(), "gsd-crash-recovery-"));
@@ -102,6 +103,27 @@ test("readCrashLock synthesizes LockData from a stale dead worker (no dispatches
102
103
  assert.ok(lock!.startedAt, "startedAt populated from workers.started_at");
103
104
  });
104
105
 
106
+ test("readCrashLock falls back to latest in-flight runtime record when dispatch claim is missing", (t) => {
107
+ const base = makeBase();
108
+ t.after(() => cleanup(base));
109
+ openDatabase(join(base, ".gsd", "gsd.db"));
110
+ const projectRoot = normalizeRealPath(base);
111
+ const workerId = registerAutoWorker({ projectRootRealpath: projectRoot });
112
+ writeUnitRuntimeRecord(base, "execute-task", "M008/S04/T02", 1778069087937, {
113
+ phase: "dispatched",
114
+ lastProgressAt: 1778069087937,
115
+ lastProgressKind: "dispatch",
116
+ });
117
+ setWorkerPid(workerId, 99999);
118
+ expireWorker(workerId);
119
+
120
+ const lock = readCrashLock(base);
121
+ assert.ok(lock, "stale worker surfaced as a crash lock");
122
+ assert.equal(lock!.unitType, "execute-task");
123
+ assert.equal(lock!.unitId, "M008/S04/T02");
124
+ assert.equal(lock!.unitStartedAt, new Date(1778069087937).toISOString());
125
+ });
126
+
105
127
  test("readCrashLock includes the most recent dispatch as unitType/unitId", (t) => {
106
128
  const base = makeBase();
107
129
  t.after(() => cleanup(base));
@@ -372,7 +372,56 @@ describe("Custom engine loop integration", () => {
372
372
  assert.ok(stopEntry?.includes("Workflow complete"), "Should stop with 'Workflow complete'");
373
373
  });
374
374
 
375
- it("finishes custom-engine skip turns and clears current turn state", async () => {
375
+ it("finalizes custom-engine complete turns and clears current turn state", async () => {
376
+ _resetPendingResolve();
377
+
378
+ const runDir = makeTmpDir();
379
+ const graph = makeGraph([
380
+ makeStep({ id: "step-a", status: "complete" }),
381
+ ], "already-done");
382
+ writeGraph(runDir, graph);
383
+ writeDefinition(runDir, graph.steps, "already-done");
384
+
385
+ const ctx = makeMockCtx();
386
+ const pi = makeMockPi();
387
+ const s = makeLoopSession({
388
+ activeEngineId: "custom",
389
+ activeRunDir: runDir,
390
+ basePath: runDir,
391
+ });
392
+ const turnResults: Array<{ status: string; failureClass: string; error?: string }> = [];
393
+ const deps = makeMockDeps({
394
+ stopAuto: async (_ctx, _pi, reason) => {
395
+ deps.callLog.push(`stopAuto:${reason ?? "no-reason"}`);
396
+ s.active = false;
397
+ },
398
+ uokObserver: {
399
+ onTurnStart: () => {},
400
+ onTurnResult: (result) => {
401
+ deps.callLog.push(`turnResult:${result.status}`);
402
+ turnResults.push({
403
+ status: result.status,
404
+ failureClass: result.failureClass,
405
+ error: result.error,
406
+ });
407
+ },
408
+ onPhaseResult: () => {},
409
+ },
410
+ });
411
+
412
+ await autoLoop(ctx, pi, s, deps);
413
+
414
+ assert.deepEqual(turnResults, [{ status: "completed", failureClass: "none", error: undefined }]);
415
+ assert.ok(
416
+ deps.callLog.indexOf("turnResult:completed") < deps.callLog.indexOf("stopAuto:Workflow complete"),
417
+ `turn should finalize before stopAuto; log=${deps.callLog.join(",")}`,
418
+ );
419
+ assert.equal(s.currentTraceId, null);
420
+ assert.equal(s.currentTurnId, null);
421
+ assert.equal(pi.calls.length, 0, "complete workflow should not dispatch work");
422
+ });
423
+
424
+ it("stops blocked custom workflows and clears current turn state", async () => {
376
425
  _resetPendingResolve();
377
426
 
378
427
  const runDir = makeTmpDir();
@@ -390,13 +439,65 @@ describe("Custom engine loop integration", () => {
390
439
  activeRunDir: runDir,
391
440
  basePath: runDir,
392
441
  });
393
- const turnResults: Array<{ status: string }> = [];
442
+ const turnResults: Array<{ status: string; failureClass: string; error?: string }> = [];
394
443
  const deps = makeMockDeps({
444
+ stopAuto: async (_ctx, _pi, reason) => {
445
+ deps.callLog.push(`stopAuto:${reason ?? "no-reason"}`);
446
+ s.active = false;
447
+ },
448
+ uokObserver: {
449
+ onTurnStart: () => {},
450
+ onTurnResult: (result) => {
451
+ turnResults.push({
452
+ status: result.status,
453
+ failureClass: result.failureClass,
454
+ error: result.error,
455
+ });
456
+ },
457
+ onPhaseResult: () => {},
458
+ },
459
+ });
460
+
461
+ await autoLoop(ctx, pi, s, deps);
462
+
463
+ assert.equal(turnResults.length, 1);
464
+ assert.equal(turnResults[0].status, "stopped");
465
+ assert.equal(turnResults[0].failureClass, "manual-attention");
466
+ assert.match(turnResults[0].error ?? "", /custom-engine-dispatch-stop/);
467
+ assert.equal(s.currentTraceId, null);
468
+ assert.equal(s.currentTurnId, null);
469
+ assert.equal(pi.calls.length, 0, "blocked workflow should not dispatch a custom step");
470
+ assert.match(
471
+ deps.callLog.find((e: string) => e.startsWith("stopAuto:")) ?? "",
472
+ /Workflow blocked: no pending steps are ready/,
473
+ );
474
+ });
475
+
476
+ it("finalizes the active turn when the session lock is lost", async () => {
477
+ _resetPendingResolve();
478
+
479
+ const ctx = makeMockCtx();
480
+ const pi = makeMockPi();
481
+ const s = makeLoopSession();
482
+ const turnResults: Array<{ status: string; failureClass: string; error?: string }> = [];
483
+ const deps = makeMockDeps({
484
+ validateSessionLock: () => ({
485
+ valid: false,
486
+ failureReason: "pid-mismatch",
487
+ expectedPid: 111,
488
+ existingPid: 222,
489
+ } as SessionLockStatus),
490
+ handleLostSessionLock: () => {
491
+ deps.callLog.push("handleLostSessionLock");
492
+ },
395
493
  uokObserver: {
396
494
  onTurnStart: () => {},
397
495
  onTurnResult: (result) => {
398
- turnResults.push({ status: result.status });
399
- s.active = false;
496
+ turnResults.push({
497
+ status: result.status,
498
+ failureClass: result.failureClass,
499
+ error: result.error,
500
+ });
400
501
  },
401
502
  onPhaseResult: () => {},
402
503
  },
@@ -404,10 +505,15 @@ describe("Custom engine loop integration", () => {
404
505
 
405
506
  await autoLoop(ctx, pi, s, deps);
406
507
 
407
- assert.deepEqual(turnResults, [{ status: "skipped" }]);
508
+ assert.deepEqual(turnResults, [{
509
+ status: "stopped",
510
+ failureClass: "manual-attention",
511
+ error: "session-lock-lost",
512
+ }]);
408
513
  assert.equal(s.currentTraceId, null);
409
514
  assert.equal(s.currentTurnId, null);
410
- assert.equal(pi.calls.length, 0, "skip should not dispatch a custom step");
515
+ assert.equal(pi.calls.length, 0, "lost session lock must not dispatch work");
516
+ assert.ok(deps.callLog.includes("handleLostSessionLock"));
411
517
  });
412
518
 
413
519
  it("does not call runPreDispatch or runFinalize on the custom path", async () => {
@@ -193,7 +193,7 @@ describe("CustomWorkflowEngine.resolveDispatch", () => {
193
193
  }
194
194
  });
195
195
 
196
- it("returns skip when pending steps are blocked by incomplete dependencies", async () => {
196
+ it("returns stop with dependency details when pending steps are blocked", async () => {
197
197
  const { engine } = setupEngine([
198
198
  makeStep({ id: "a", dependsOn: ["b"] }),
199
199
  makeStep({ id: "b", dependsOn: ["a"] }),
@@ -202,7 +202,45 @@ describe("CustomWorkflowEngine.resolveDispatch", () => {
202
202
  const state = await engine.deriveState("/unused");
203
203
  const dispatch = await engine.resolveDispatch(state, { basePath: "/unused" });
204
204
 
205
- assert.deepEqual(dispatch, { action: "skip" });
205
+ assert.equal(dispatch.action, "stop");
206
+ if (dispatch.action === "stop") {
207
+ assert.equal(dispatch.level, "error");
208
+ assert.match(dispatch.reason, /Workflow blocked/);
209
+ assert.match(dispatch.reason, /a waiting on b \(pending\)/);
210
+ assert.match(dispatch.reason, /b waiting on a \(pending\)/);
211
+ }
212
+ });
213
+
214
+ it("reports missing dependencies when no pending step can run", async () => {
215
+ const { engine } = setupEngine([
216
+ makeStep({ id: "a", dependsOn: ["missing-step"] }),
217
+ ]);
218
+
219
+ const state = await engine.deriveState("/unused");
220
+ const dispatch = await engine.resolveDispatch(state, { basePath: "/unused" });
221
+
222
+ assert.equal(dispatch.action, "stop");
223
+ if (dispatch.action === "stop") {
224
+ assert.equal(dispatch.level, "error");
225
+ assert.match(dispatch.reason, /a waiting on missing-step \(missing\)/);
226
+ }
227
+ });
228
+
229
+ it("does not report expanded dependencies as blockers", async () => {
230
+ const { engine } = setupEngine([
231
+ makeStep({ id: "iter", status: "expanded" }),
232
+ makeStep({ id: "after", dependsOn: ["iter", "missing-step"] }),
233
+ ]);
234
+
235
+ const state = await engine.deriveState("/unused");
236
+ const dispatch = await engine.resolveDispatch(state, { basePath: "/unused" });
237
+
238
+ assert.equal(dispatch.action, "stop");
239
+ if (dispatch.action === "stop") {
240
+ assert.equal(dispatch.level, "error");
241
+ assert.match(dispatch.reason, /after waiting on missing-step \(missing\)/);
242
+ assert.doesNotMatch(dispatch.reason, /iter \(expanded\)/);
243
+ }
206
244
  });
207
245
 
208
246
  it("respects dependency ordering", async () => {
@@ -0,0 +1,428 @@
1
+ // gsd-2 (GSD2) + db migration `:memory:` integration tests
2
+ //
3
+ // Covers the gap left by the FakeAdapter unit tests for the gsd-db split
4
+ // (PR #5308): those assert SQL strings, not that DDL actually executes.
5
+ // These tests open a real node:sqlite `:memory:` database, run the schema
6
+ // helpers, and verify the resulting schema via PRAGMA introspection.
7
+
8
+ import { describe, test } from "node:test";
9
+ import assert from "node:assert/strict";
10
+ import { createRequire } from "node:module";
11
+
12
+ import { createDbAdapter, type DbAdapter } from "../db-adapter.ts";
13
+ import { createBaseSchemaObjects } from "../db-base-schema.ts";
14
+ import { columnExists } from "../db-schema-metadata.ts";
15
+ import {
16
+ applyMigrationV2Artifacts,
17
+ applyMigrationV3Memories,
18
+ applyMigrationV4DecisionMadeBy,
19
+ applyMigrationV5HierarchyTables,
20
+ applyMigrationV6SliceSummaries,
21
+ applyMigrationV7Dependencies,
22
+ applyMigrationV8PlanningFields,
23
+ applyMigrationV9Ordering,
24
+ applyMigrationV10ReplanTrigger,
25
+ applyMigrationV11TaskPlanning,
26
+ applyMigrationV12QualityGates,
27
+ applyMigrationV13HotPathIndexes,
28
+ applyMigrationV22QualityGateRepair,
29
+ } from "../db-migration-steps.ts";
30
+
31
+ const _require = createRequire(import.meta.url);
32
+
33
+ interface ColumnInfo {
34
+ name: string;
35
+ type: string;
36
+ notnull: number;
37
+ dflt_value: unknown;
38
+ pk: number;
39
+ }
40
+
41
+ function openMemoryAdapter(): { adapter: DbAdapter; close: () => void } {
42
+ const sqlite = _require("node:sqlite") as { DatabaseSync: new (path: string) => unknown };
43
+ const raw = new sqlite.DatabaseSync(":memory:");
44
+ const adapter = createDbAdapter(raw);
45
+ return {
46
+ adapter,
47
+ close: () => adapter.close(),
48
+ };
49
+ }
50
+
51
+ function tableInfo(db: DbAdapter, table: string): ColumnInfo[] {
52
+ return db.prepare(`PRAGMA table_info(${table})`).all() as unknown as ColumnInfo[];
53
+ }
54
+
55
+ function columnNames(db: DbAdapter, table: string): string[] {
56
+ return tableInfo(db, table).map((c) => c.name);
57
+ }
58
+
59
+ function tableExists(db: DbAdapter, table: string): boolean {
60
+ return !!db
61
+ .prepare("SELECT 1 as present FROM sqlite_master WHERE type='table' AND name=?")
62
+ .get(table);
63
+ }
64
+
65
+ function indexExists(db: DbAdapter, name: string): boolean {
66
+ return !!db
67
+ .prepare("SELECT 1 as present FROM sqlite_master WHERE type='index' AND name=?")
68
+ .get(name);
69
+ }
70
+
71
+ function viewExists(db: DbAdapter, name: string): boolean {
72
+ return !!db
73
+ .prepare("SELECT 1 as present FROM sqlite_master WHERE type='view' AND name=?")
74
+ .get(name);
75
+ }
76
+
77
+ describe("db base schema bring-up against :memory: sqlite", () => {
78
+ test("createBaseSchemaObjects executes all DDL without throwing", () => {
79
+ const { adapter, close } = openMemoryAdapter();
80
+ try {
81
+ assert.doesNotThrow(() => {
82
+ createBaseSchemaObjects(adapter, {
83
+ tryCreateMemoriesFts: () => true,
84
+ ensureVerificationEvidenceDedupIndex: () => {},
85
+ });
86
+ });
87
+ } finally {
88
+ close();
89
+ }
90
+ });
91
+
92
+ test("base schema produces all expected tables, indexes, and views", () => {
93
+ const { adapter, close } = openMemoryAdapter();
94
+ try {
95
+ createBaseSchemaObjects(adapter, {
96
+ tryCreateMemoriesFts: () => true,
97
+ ensureVerificationEvidenceDedupIndex: () => {},
98
+ });
99
+
100
+ const expectedTables = [
101
+ "schema_version",
102
+ "decisions",
103
+ "requirements",
104
+ "artifacts",
105
+ "memories",
106
+ "memory_processed_units",
107
+ "memory_sources",
108
+ "memory_embeddings",
109
+ "memory_relations",
110
+ "milestones",
111
+ "slices",
112
+ "tasks",
113
+ "verification_evidence",
114
+ "replan_history",
115
+ "assessments",
116
+ "quality_gates",
117
+ "slice_dependencies",
118
+ "gate_runs",
119
+ "turn_git_transactions",
120
+ "audit_events",
121
+ "audit_turn_index",
122
+ ];
123
+ for (const t of expectedTables) {
124
+ assert.ok(tableExists(adapter, t), `expected table ${t} to exist`);
125
+ }
126
+
127
+ const expectedIndexes = [
128
+ "idx_memories_active",
129
+ "idx_replan_history_milestone",
130
+ "idx_tasks_active",
131
+ "idx_slices_active",
132
+ "idx_milestones_status",
133
+ "idx_quality_gates_pending",
134
+ "idx_verification_evidence_task",
135
+ "idx_slice_deps_target",
136
+ "idx_gate_runs_turn",
137
+ "idx_gate_runs_lookup",
138
+ "idx_turn_git_tx_turn",
139
+ "idx_audit_events_trace",
140
+ "idx_audit_events_turn",
141
+ ];
142
+ for (const i of expectedIndexes) {
143
+ assert.ok(indexExists(adapter, i), `expected index ${i}`);
144
+ }
145
+
146
+ for (const v of ["active_decisions", "active_requirements", "active_memories"]) {
147
+ assert.ok(viewExists(adapter, v), `expected view ${v}`);
148
+ }
149
+ } finally {
150
+ close();
151
+ }
152
+ });
153
+
154
+ test("base schema decisions table has the documented column shape", () => {
155
+ const { adapter, close } = openMemoryAdapter();
156
+ try {
157
+ createBaseSchemaObjects(adapter, {
158
+ tryCreateMemoriesFts: () => true,
159
+ ensureVerificationEvidenceDedupIndex: () => {},
160
+ });
161
+
162
+ const cols = tableInfo(adapter, "decisions");
163
+ const byName = new Map(cols.map((c) => [c.name, c]));
164
+
165
+ const seq = byName.get("seq");
166
+ assert.ok(seq, "decisions.seq column missing");
167
+ assert.equal(seq!.type, "INTEGER");
168
+ assert.equal(seq!.pk, 1, "seq should be primary key");
169
+
170
+ const id = byName.get("id");
171
+ assert.ok(id);
172
+ assert.equal(id!.type, "TEXT");
173
+ assert.equal(id!.notnull, 1);
174
+
175
+ const madeBy = byName.get("made_by");
176
+ assert.ok(madeBy, "decisions.made_by missing (V4 migration column)");
177
+ assert.equal(madeBy!.notnull, 1);
178
+
179
+ const source = byName.get("source");
180
+ assert.ok(source, "decisions.source missing (V16 migration column)");
181
+ assert.equal(source!.notnull, 1);
182
+ } finally {
183
+ close();
184
+ }
185
+ });
186
+
187
+ test("base schema tasks table promotes composite primary key correctly", () => {
188
+ const { adapter, close } = openMemoryAdapter();
189
+ try {
190
+ createBaseSchemaObjects(adapter, {
191
+ tryCreateMemoriesFts: () => true,
192
+ ensureVerificationEvidenceDedupIndex: () => {},
193
+ });
194
+
195
+ const cols = tableInfo(adapter, "tasks");
196
+ const pkCols = cols.filter((c) => c.pk > 0).sort((a, b) => a.pk - b.pk).map((c) => c.name);
197
+ assert.deepEqual(pkCols, ["milestone_id", "slice_id", "id"]);
198
+ } finally {
199
+ close();
200
+ }
201
+ });
202
+ });
203
+
204
+ describe("db migration steps end-to-end against :memory: sqlite", () => {
205
+ // Drive a fresh DB from V1 baseline up to V13 so each high-risk migration
206
+ // sees a realistic schema (not the FakeAdapter no-op surface).
207
+ function runUpToV13(adapter: DbAdapter): void {
208
+ adapter.exec(`
209
+ CREATE TABLE schema_version (
210
+ version INTEGER NOT NULL,
211
+ applied_at TEXT NOT NULL
212
+ )
213
+ `);
214
+ adapter.exec(`
215
+ CREATE TABLE decisions (
216
+ seq INTEGER PRIMARY KEY AUTOINCREMENT,
217
+ id TEXT NOT NULL UNIQUE,
218
+ when_context TEXT NOT NULL DEFAULT '',
219
+ scope TEXT NOT NULL DEFAULT '',
220
+ decision TEXT NOT NULL DEFAULT '',
221
+ choice TEXT NOT NULL DEFAULT '',
222
+ rationale TEXT NOT NULL DEFAULT '',
223
+ revisable TEXT NOT NULL DEFAULT '',
224
+ superseded_by TEXT DEFAULT NULL
225
+ )
226
+ `);
227
+ adapter.exec(`
228
+ CREATE TABLE requirements (
229
+ id TEXT PRIMARY KEY,
230
+ class TEXT NOT NULL DEFAULT '',
231
+ status TEXT NOT NULL DEFAULT '',
232
+ description TEXT NOT NULL DEFAULT '',
233
+ why TEXT NOT NULL DEFAULT '',
234
+ source TEXT NOT NULL DEFAULT '',
235
+ primary_owner TEXT NOT NULL DEFAULT '',
236
+ supporting_slices TEXT NOT NULL DEFAULT '',
237
+ validation TEXT NOT NULL DEFAULT '',
238
+ notes TEXT NOT NULL DEFAULT '',
239
+ full_content TEXT NOT NULL DEFAULT '',
240
+ superseded_by TEXT DEFAULT NULL
241
+ )
242
+ `);
243
+ adapter.exec("CREATE VIEW active_decisions AS SELECT * FROM decisions WHERE superseded_by IS NULL");
244
+ adapter.exec("CREATE VIEW active_requirements AS SELECT * FROM requirements WHERE superseded_by IS NULL");
245
+
246
+ applyMigrationV2Artifacts(adapter);
247
+ applyMigrationV3Memories(adapter);
248
+ applyMigrationV4DecisionMadeBy(adapter);
249
+ applyMigrationV5HierarchyTables(adapter);
250
+ applyMigrationV6SliceSummaries(adapter);
251
+ applyMigrationV7Dependencies(adapter);
252
+ applyMigrationV8PlanningFields(adapter);
253
+ applyMigrationV9Ordering(adapter);
254
+ applyMigrationV10ReplanTrigger(adapter);
255
+ applyMigrationV11TaskPlanning(adapter);
256
+ applyMigrationV12QualityGates(adapter);
257
+ applyMigrationV13HotPathIndexes(adapter, () => {});
258
+ }
259
+
260
+ test("V8 PlanningFields adds every promised ALTER column without throwing", () => {
261
+ const { adapter, close } = openMemoryAdapter();
262
+ try {
263
+ runUpToV13(adapter);
264
+
265
+ assert.ok(columnExists(adapter, "milestones", "vision"));
266
+ assert.ok(columnExists(adapter, "milestones", "verification_uat"));
267
+ assert.ok(columnExists(adapter, "milestones", "definition_of_done"));
268
+ assert.ok(columnExists(adapter, "milestones", "boundary_map_markdown"));
269
+
270
+ assert.ok(columnExists(adapter, "slices", "goal"));
271
+ assert.ok(columnExists(adapter, "slices", "proof_level"));
272
+ assert.ok(columnExists(adapter, "slices", "observability_impact"));
273
+
274
+ assert.ok(columnExists(adapter, "tasks", "estimate"));
275
+ assert.ok(columnExists(adapter, "tasks", "files"));
276
+ assert.ok(columnExists(adapter, "tasks", "expected_output"));
277
+
278
+ assert.ok(tableExists(adapter, "replan_history"));
279
+ assert.ok(tableExists(adapter, "assessments"));
280
+ } finally {
281
+ close();
282
+ }
283
+ });
284
+
285
+ test("V13 HotPathIndexes succeeds when prior migrations have run (ordering)", () => {
286
+ const { adapter, close } = openMemoryAdapter();
287
+ try {
288
+ runUpToV13(adapter);
289
+
290
+ for (const i of [
291
+ "idx_tasks_active",
292
+ "idx_slices_active",
293
+ "idx_milestones_status",
294
+ "idx_quality_gates_pending",
295
+ "idx_verification_evidence_task",
296
+ ]) {
297
+ assert.ok(indexExists(adapter, i), `expected index ${i} after V13`);
298
+ }
299
+ } finally {
300
+ close();
301
+ }
302
+ });
303
+
304
+ test("V13 HotPathIndexes throws if quality_gates table does not yet exist (ordering guard)", () => {
305
+ const { adapter, close } = openMemoryAdapter();
306
+ try {
307
+ adapter.exec("CREATE TABLE schema_version (version INTEGER NOT NULL, applied_at TEXT NOT NULL)");
308
+ assert.throws(
309
+ () => applyMigrationV13HotPathIndexes(adapter, () => {}),
310
+ /no such table/i,
311
+ );
312
+ } finally {
313
+ close();
314
+ }
315
+ });
316
+
317
+ test("V22 QualityGateRepair rebuilds quality_gates with task_id NOT NULL and preserves indexes", () => {
318
+ const { adapter, close } = openMemoryAdapter();
319
+ try {
320
+ runUpToV13(adapter);
321
+
322
+ adapter.exec("DROP INDEX IF EXISTS idx_quality_gates_pending");
323
+ adapter.exec("DROP TABLE quality_gates");
324
+ adapter.exec(`
325
+ CREATE TABLE quality_gates (
326
+ milestone_id TEXT NOT NULL,
327
+ slice_id TEXT NOT NULL,
328
+ gate_id TEXT NOT NULL,
329
+ task_id TEXT DEFAULT '',
330
+ status TEXT NOT NULL DEFAULT 'pending',
331
+ verdict TEXT NOT NULL DEFAULT '',
332
+ rationale TEXT NOT NULL DEFAULT '',
333
+ findings TEXT NOT NULL DEFAULT '',
334
+ evaluated_at TEXT DEFAULT NULL,
335
+ PRIMARY KEY (milestone_id, slice_id, gate_id, task_id),
336
+ FOREIGN KEY (milestone_id, slice_id) REFERENCES slices(milestone_id, id)
337
+ )
338
+ `);
339
+
340
+ const before = tableInfo(adapter, "quality_gates").find((c) => c.name === "task_id");
341
+ assert.ok(before);
342
+ assert.equal(before!.notnull, 0, "pre-repair fixture should have nullable task_id");
343
+
344
+ let copyCalled = 0;
345
+ applyMigrationV22QualityGateRepair(adapter, {
346
+ copyQualityGateRowsToRepairedTable: () => {
347
+ copyCalled += 1;
348
+ },
349
+ });
350
+
351
+ assert.equal(copyCalled, 1, "repair branch should invoke the row-copy hook exactly once");
352
+
353
+ const after = tableInfo(adapter, "quality_gates").find((c) => c.name === "task_id");
354
+ assert.ok(after);
355
+ assert.equal(after!.notnull, 1, "post-repair task_id must be NOT NULL");
356
+
357
+ assert.ok(columnExists(adapter, "quality_gates", "scope"));
358
+ assert.ok(columnExists(adapter, "assessments", "scope"));
359
+
360
+ assert.ok(indexExists(adapter, "idx_quality_gates_pending"));
361
+
362
+ assert.equal(tableExists(adapter, "quality_gates_new"), false);
363
+
364
+ assert.deepEqual(columnNames(adapter, "quality_gates"), [
365
+ "milestone_id",
366
+ "slice_id",
367
+ "gate_id",
368
+ "scope",
369
+ "task_id",
370
+ "status",
371
+ "verdict",
372
+ "rationale",
373
+ "findings",
374
+ "evaluated_at",
375
+ ]);
376
+ } finally {
377
+ close();
378
+ }
379
+ });
380
+
381
+ test("V22 is a no-op on already-repaired quality_gates", () => {
382
+ const { adapter, close } = openMemoryAdapter();
383
+ try {
384
+ runUpToV13(adapter);
385
+
386
+ let copyCalled = 0;
387
+ applyMigrationV22QualityGateRepair(adapter, {
388
+ copyQualityGateRowsToRepairedTable: () => {
389
+ copyCalled += 1;
390
+ },
391
+ });
392
+
393
+ assert.equal(copyCalled, 0, "no-op when task_id is already NOT NULL");
394
+ assert.ok(columnExists(adapter, "quality_gates", "scope"));
395
+ } finally {
396
+ close();
397
+ }
398
+ });
399
+ });
400
+
401
+ describe("db provider happy path against :memory: sqlite", () => {
402
+ test("createDbAdapter wraps node:sqlite and supports exec/prepare/close", () => {
403
+ const sqlite = _require("node:sqlite") as { DatabaseSync: new (path: string) => unknown };
404
+ const raw = new sqlite.DatabaseSync(":memory:");
405
+ const adapter = createDbAdapter(raw);
406
+
407
+ assert.doesNotThrow(() => adapter.exec("CREATE TABLE t (id INTEGER PRIMARY KEY, v TEXT)"));
408
+
409
+ const insert = adapter.prepare("INSERT INTO t (id, v) VALUES (?, ?)");
410
+ insert.run(1, "alpha");
411
+ insert.run(2, "beta");
412
+
413
+ const selectOne = adapter.prepare("SELECT v FROM t WHERE id = ?");
414
+ const row = selectOne.get(1);
415
+ assert.deepEqual(row, { v: "alpha" });
416
+
417
+ const selectAll = adapter.prepare("SELECT id, v FROM t ORDER BY id");
418
+ const rows = selectAll.all();
419
+ assert.equal(rows.length, 2);
420
+ assert.equal(rows[0]["v"], "alpha");
421
+ assert.equal(rows[1]["v"], "beta");
422
+
423
+ const selectAgain = adapter.prepare("SELECT v FROM t WHERE id = ?");
424
+ assert.deepEqual(selectAgain.get(2), { v: "beta" });
425
+
426
+ assert.doesNotThrow(() => adapter.close());
427
+ });
428
+ });
@@ -106,10 +106,10 @@ describe("db-schema-metadata", () => {
106
106
  test("records schema version rows with timestamps", () => {
107
107
  const db = new FakeAdapter();
108
108
 
109
- recordSchemaVersion(db, 25);
109
+ recordSchemaVersion(db, 26);
110
110
 
111
111
  assert.equal(db.runCalls.length, 1);
112
- assert.equal((db.runCalls[0][0] as Record<string, unknown>)[":version"], 25);
112
+ assert.equal((db.runCalls[0][0] as Record<string, unknown>)[":version"], 26);
113
113
  assert.equal(typeof (db.runCalls[0][0] as Record<string, unknown>)[":applied_at"], "string");
114
114
  });
115
115
  });