@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
@@ -24,7 +24,7 @@
24
24
  import { createRequire } from "node:module";
25
25
  import { createHash } from "node:crypto";
26
26
  import { existsSync, copyFileSync, mkdirSync, realpathSync } from "node:fs";
27
- import { dirname } from "node:path";
27
+ import { dirname, join } from "node:path";
28
28
  import { GSDError, GSD_STALE_STATE } from "./errors.js";
29
29
  import { getGateIdsForTurn } from "./gate-registry.js";
30
30
  import { logError, logWarning } from "./workflow-logger.js";
@@ -683,6 +683,29 @@ export function checkpointDatabase() {
683
683
  logWarning("db", `WAL checkpoint failed: ${e.message}`);
684
684
  }
685
685
  }
686
+ /**
687
+ * Copy the live database file to `.gsd/backups/<label>-<timestamp>.db` so a
688
+ * destructive operation (e.g. recover, which clears the hierarchy tables) is
689
+ * reversible. Checkpoints the WAL first so the snapshot is complete. Returns
690
+ * the backup path, or null if no DB is open or the copy failed.
691
+ */
692
+ export function backupDatabaseSnapshot(label) {
693
+ if (!currentPath)
694
+ return null;
695
+ try {
696
+ checkpointDatabase();
697
+ const backupsDir = join(dirname(currentPath), "backups");
698
+ mkdirSync(backupsDir, { recursive: true });
699
+ const stamp = new Date().toISOString().replace(/[:.]/g, "-");
700
+ const dest = join(backupsDir, `${label}-${stamp}.db`);
701
+ copyFileSync(currentPath, dest);
702
+ return dest;
703
+ }
704
+ catch (e) {
705
+ logWarning("db", `database snapshot failed: ${e.message}`);
706
+ return null;
707
+ }
708
+ }
686
709
  const _transactionRunner = createDbTransactionRunner();
687
710
  function createTransactionControls(db) {
688
711
  return {
@@ -1083,14 +1106,14 @@ export function insertTask(t) {
1083
1106
  milestone_id, slice_id, id, title, status, one_liner, narrative,
1084
1107
  verification_result, duration, completed_at, blocker_discovered,
1085
1108
  deviations, known_issues, key_files, key_decisions, full_summary_md,
1086
- description, estimate, files, verify, inputs, expected_output, observability_impact, sequence
1087
- , target_repositories
1109
+ description, estimate, files, verify, inputs, expected_output,
1110
+ observability_impact, full_plan_md, target_repositories, sequence
1088
1111
  ) VALUES (
1089
1112
  :milestone_id, :slice_id, :id, :title, :status, :one_liner, :narrative,
1090
1113
  :verification_result, :duration, :completed_at, :blocker_discovered,
1091
1114
  :deviations, :known_issues, :key_files, :key_decisions, :full_summary_md,
1092
- :description, :estimate, :files, :verify, :inputs, :expected_output, :observability_impact, :sequence
1093
- , :target_repositories
1115
+ :description, :estimate, :files, :verify, :inputs, :expected_output,
1116
+ :observability_impact, :full_plan_md, :target_repositories, :sequence
1094
1117
  )
1095
1118
  ON CONFLICT(milestone_id, slice_id, id) DO UPDATE SET
1096
1119
  title = CASE WHEN NULLIF(:title, '') IS NOT NULL THEN :title ELSE tasks.title END,
@@ -1113,6 +1136,7 @@ export function insertTask(t) {
1113
1136
  inputs = CASE WHEN NULLIF(:inputs, '[]') IS NOT NULL THEN :inputs ELSE tasks.inputs END,
1114
1137
  expected_output = CASE WHEN NULLIF(:expected_output, '[]') IS NOT NULL THEN :expected_output ELSE tasks.expected_output END,
1115
1138
  observability_impact = CASE WHEN NULLIF(:observability_impact, '') IS NOT NULL THEN :observability_impact ELSE tasks.observability_impact END,
1139
+ full_plan_md = CASE WHEN NULLIF(:full_plan_md, '') IS NOT NULL THEN :full_plan_md ELSE tasks.full_plan_md END,
1116
1140
  sequence = :sequence,
1117
1141
  target_repositories = CASE
1118
1142
  WHEN :raw_target_repositories IS NOT NULL THEN :target_repositories
@@ -1141,6 +1165,7 @@ export function insertTask(t) {
1141
1165
  ":inputs": JSON.stringify(t.planning?.inputs ?? []),
1142
1166
  ":expected_output": JSON.stringify(t.planning?.expectedOutput ?? []),
1143
1167
  ":observability_impact": t.planning?.observabilityImpact ?? "",
1168
+ ":full_plan_md": t.planning?.fullPlanMd ?? "",
1144
1169
  ":sequence": t.sequence ?? 0,
1145
1170
  ":target_repositories": JSON.stringify(t.planning?.targetRepositories ?? []),
1146
1171
  ":raw_target_repositories": t.planning && "targetRepositories" in t.planning
@@ -1581,7 +1606,23 @@ export function copyWorktreeDb(srcDbPath, destDbPath) {
1581
1606
  }
1582
1607
  }
1583
1608
  export function reconcileWorktreeDb(mainDbPath, worktreeDbPath) {
1584
- const zero = { decisions: 0, requirements: 0, artifacts: 0, milestones: 0, slices: 0, tasks: 0, memories: 0, verification_evidence: 0, conflicts: [] };
1609
+ const zero = {
1610
+ decisions: 0,
1611
+ requirements: 0,
1612
+ artifacts: 0,
1613
+ milestones: 0,
1614
+ slices: 0,
1615
+ tasks: 0,
1616
+ memories: 0,
1617
+ replan_history: 0,
1618
+ assessments: 0,
1619
+ quality_gates: 0,
1620
+ slice_dependencies: 0,
1621
+ verification_evidence: 0,
1622
+ gate_runs: 0,
1623
+ milestone_commit_attributions: 0,
1624
+ conflicts: [],
1625
+ };
1585
1626
  if (!existsSync(worktreeDbPath))
1586
1627
  return zero;
1587
1628
  // Guard: bail when both paths resolve to the same physical file.
@@ -1612,204 +1653,362 @@ export function reconcileWorktreeDb(mainDbPath, worktreeDbPath) {
1612
1653
  try {
1613
1654
  adapter.exec(`ATTACH DATABASE '${worktreeDbPath}' AS wt`);
1614
1655
  try {
1615
- const wtInfo = adapter.prepare("PRAGMA wt.table_info('decisions')").all();
1656
+ function countChanges(result) {
1657
+ return typeof result === "object" && result !== null ? (result.changes ?? 0) : 0;
1658
+ }
1659
+ function wtTableInfo(tableName) {
1660
+ return adapter.prepare(`PRAGMA wt.table_info('${tableName}')`).all();
1661
+ }
1662
+ const wtInfo = wtTableInfo("decisions");
1663
+ const hasWtDecisions = wtInfo.length > 0;
1616
1664
  const hasMadeBy = wtInfo.some((col) => col["name"] === "made_by");
1617
1665
  // ADR-011: worktree may predate schema v16/v17. For missing columns we
1618
1666
  // fall through to the main DB's existing value (not a literal default)
1619
1667
  // so reconcile never silently clears state the main tree has recorded.
1620
1668
  const hasDecisionSource = wtInfo.some((col) => col["name"] === "source");
1621
- const wtMilestoneInfo = adapter.prepare("PRAGMA wt.table_info('milestones')").all();
1669
+ const wtRequirementInfo = wtTableInfo("requirements");
1670
+ const hasWtRequirements = wtRequirementInfo.length > 0;
1671
+ const wtMilestoneInfo = wtTableInfo("milestones");
1672
+ const hasWtMilestones = wtMilestoneInfo.length > 0;
1622
1673
  const hasMilestoneSequence = wtMilestoneInfo.some((col) => col["name"] === "sequence");
1623
- const wtSliceInfo = adapter.prepare("PRAGMA wt.table_info('slices')").all();
1674
+ const wtSliceInfo = wtTableInfo("slices");
1675
+ const hasWtSlices = wtSliceInfo.length > 0;
1624
1676
  const hasIsSketch = wtSliceInfo.some((col) => col["name"] === "is_sketch");
1625
1677
  const hasSketchScope = wtSliceInfo.some((col) => col["name"] === "sketch_scope");
1626
1678
  const hasSliceTargetRepositories = wtSliceInfo.some((col) => col["name"] === "target_repositories");
1627
- const wtTaskInfo = adapter.prepare("PRAGMA wt.table_info('tasks')").all();
1679
+ const wtTaskInfo = wtTableInfo("tasks");
1680
+ const hasWtTasks = wtTaskInfo.length > 0;
1628
1681
  const hasTaskTargetRepositories = wtTaskInfo.some((col) => col["name"] === "target_repositories");
1629
1682
  const hasBlockerSource = wtTaskInfo.some((col) => col["name"] === "blocker_source");
1630
1683
  const hasEscalationPending = wtTaskInfo.some((col) => col["name"] === "escalation_pending");
1631
1684
  const hasEscalationAwaiting = wtTaskInfo.some((col) => col["name"] === "escalation_awaiting_review");
1632
1685
  const hasEscalationArtifact = wtTaskInfo.some((col) => col["name"] === "escalation_artifact_path");
1633
1686
  const hasEscalationOverride = wtTaskInfo.some((col) => col["name"] === "escalation_override_applied_at");
1634
- const wtArtifactInfo = adapter.prepare("PRAGMA wt.table_info('artifacts')").all();
1635
- const hasArtifactContentHash = wtArtifactInfo.some((col) => col["name"] === "content_hash");
1636
- const wtMemoryInfo = adapter.prepare("PRAGMA wt.table_info('memories')").all();
1687
+ const wtArtifactInfo = wtTableInfo("artifacts");
1688
+ const hasWtArtifacts = wtArtifactInfo.length > 0;
1689
+ const wtMemoryInfo = wtTableInfo("memories");
1690
+ const hasWtMemories = wtMemoryInfo.length > 0;
1637
1691
  const hasMemoryScope = wtMemoryInfo.some((col) => col["name"] === "scope");
1638
1692
  const hasMemoryTags = wtMemoryInfo.some((col) => col["name"] === "tags");
1639
1693
  const hasMemoryStructuredFields = wtMemoryInfo.some((col) => col["name"] === "structured_fields");
1640
1694
  const hasMemoryLastHitAt = wtMemoryInfo.some((col) => col["name"] === "last_hit_at");
1641
- const decConf = adapter.prepare(`SELECT m.id FROM decisions m INNER JOIN wt.decisions w ON m.id = w.id WHERE m.decision != w.decision OR m.choice != w.choice OR m.rationale != w.rationale OR ${hasMadeBy ? "m.made_by != w.made_by" : "'agent' != 'agent'"} OR m.superseded_by IS NOT w.superseded_by`).all();
1642
- for (const row of decConf)
1643
- conflicts.push(`decision ${row["id"]}: modified in both`);
1644
- const reqConf = adapter.prepare(`SELECT m.id FROM requirements m INNER JOIN wt.requirements w ON m.id = w.id WHERE m.description != w.description OR m.status != w.status OR m.notes != w.notes OR m.superseded_by IS NOT w.superseded_by`).all();
1645
- for (const row of reqConf)
1646
- conflicts.push(`requirement ${row["id"]}: modified in both`);
1647
- const merged = { decisions: 0, requirements: 0, artifacts: 0, milestones: 0, slices: 0, tasks: 0, memories: 0, verification_evidence: 0 };
1648
- function countChanges(result) {
1649
- return typeof result === "object" && result !== null ? (result.changes ?? 0) : 0;
1695
+ const hasWtReplanHistory = wtTableInfo("replan_history").length > 0;
1696
+ const hasWtAssessments = wtTableInfo("assessments").length > 0;
1697
+ const hasWtQualityGates = wtTableInfo("quality_gates").length > 0;
1698
+ const hasWtSliceDependencies = wtTableInfo("slice_dependencies").length > 0;
1699
+ const hasWtVerificationEvidence = wtTableInfo("verification_evidence").length > 0;
1700
+ const hasWtGateRuns = wtTableInfo("gate_runs").length > 0;
1701
+ const hasWtMilestoneCommitAttributions = wtTableInfo("milestone_commit_attributions").length > 0;
1702
+ if (hasWtDecisions) {
1703
+ const decConf = adapter.prepare(`SELECT m.id FROM decisions m INNER JOIN wt.decisions w ON m.id = w.id WHERE m.decision != w.decision OR m.choice != w.choice OR m.rationale != w.rationale OR ${hasMadeBy ? "m.made_by != w.made_by" : "'agent' != 'agent'"} OR m.superseded_by IS NOT w.superseded_by`).all();
1704
+ for (const row of decConf)
1705
+ conflicts.push(`decision ${row["id"]}: modified in both`);
1706
+ }
1707
+ if (hasWtRequirements) {
1708
+ const reqConf = adapter.prepare(`SELECT m.id FROM requirements m INNER JOIN wt.requirements w ON m.id = w.id WHERE m.description != w.description OR m.status != w.status OR m.notes != w.notes OR m.superseded_by IS NOT w.superseded_by`).all();
1709
+ for (const row of reqConf)
1710
+ conflicts.push(`requirement ${row["id"]}: modified in both`);
1650
1711
  }
1712
+ const merged = {
1713
+ decisions: 0,
1714
+ requirements: 0,
1715
+ artifacts: 0,
1716
+ milestones: 0,
1717
+ slices: 0,
1718
+ tasks: 0,
1719
+ memories: 0,
1720
+ replan_history: 0,
1721
+ assessments: 0,
1722
+ quality_gates: 0,
1723
+ slice_dependencies: 0,
1724
+ verification_evidence: 0,
1725
+ gate_runs: 0,
1726
+ milestone_commit_attributions: 0,
1727
+ };
1728
+ const sliceTargetRepositoriesSql = hasSliceTargetRepositories
1729
+ ? `CASE
1730
+ WHEN w.target_repositories = '[]' AND COALESCE(m.target_repositories, '[]') <> '[]'
1731
+ THEN m.target_repositories
1732
+ ELSE COALESCE(w.target_repositories, m.target_repositories, '[]')
1733
+ END`
1734
+ : "COALESCE(m.target_repositories, '[]')";
1735
+ const taskTargetRepositoriesSql = hasTaskTargetRepositories
1736
+ ? `CASE
1737
+ WHEN w.target_repositories = '[]' AND COALESCE(m.target_repositories, '[]') <> '[]'
1738
+ THEN m.target_repositories
1739
+ ELSE COALESCE(w.target_repositories, m.target_repositories, '[]')
1740
+ END`
1741
+ : "COALESCE(m.target_repositories, '[]')";
1651
1742
  adapter.exec("BEGIN");
1652
1743
  try {
1653
1744
  // Join the target decisions so we can prefer an existing main.source
1654
1745
  // when the worktree predates v16 — otherwise a write-through reconcile
1655
1746
  // would clobber 'escalation'-sourced decisions with the literal default.
1656
- merged.decisions = countChanges(adapter.prepare(`
1657
- INSERT OR REPLACE INTO decisions (
1658
- id, when_context, scope, decision, choice, rationale, revisable, made_by, source, superseded_by
1659
- )
1660
- SELECT w.id, w.when_context, w.scope, w.decision, w.choice, w.rationale, w.revisable, ${hasMadeBy ? "w.made_by" : "COALESCE(m.made_by, 'agent')"}, ${hasDecisionSource ? "w.source" : "COALESCE(m.source, 'discussion')"}, w.superseded_by
1661
- FROM wt.decisions w
1662
- LEFT JOIN decisions m ON m.id = w.id
1663
- `).run());
1664
- merged.requirements = countChanges(adapter.prepare(`
1665
- INSERT OR REPLACE INTO requirements (
1666
- id, class, status, description, why, source, primary_owner,
1667
- supporting_slices, validation, notes, full_content, superseded_by
1668
- )
1669
- SELECT id, class, status, description, why, source, primary_owner,
1670
- supporting_slices, validation, notes, full_content, superseded_by
1671
- FROM wt.requirements
1672
- `).run());
1673
- // V27: preserve content_hash. If the worktree predates V27 (no column),
1674
- // fall back to the main DB's existing hash so reconcile doesn't null
1675
- // out integrity fingerprints on artifacts that were unchanged in wt.
1676
- merged.artifacts = countChanges(adapter.prepare(`
1677
- INSERT OR REPLACE INTO artifacts (
1678
- path, artifact_type, milestone_id, slice_id, task_id, full_content, imported_at, content_hash
1679
- )
1680
- SELECT w.path, w.artifact_type, w.milestone_id, w.slice_id, w.task_id, w.full_content, w.imported_at,
1681
- ${hasArtifactContentHash ? "w.content_hash" : "m.content_hash"}
1682
- FROM wt.artifacts w
1683
- LEFT JOIN artifacts m ON m.path = w.path
1684
- `).run());
1747
+ if (hasWtDecisions) {
1748
+ merged.decisions = countChanges(adapter.prepare(`
1749
+ INSERT INTO decisions (
1750
+ id, when_context, scope, decision, choice, rationale, revisable, made_by, source, superseded_by
1751
+ )
1752
+ SELECT w.id, w.when_context, w.scope, w.decision, w.choice, w.rationale, w.revisable, ${hasMadeBy ? "w.made_by" : "COALESCE(m.made_by, 'agent')"}, ${hasDecisionSource ? "w.source" : "COALESCE(m.source, 'discussion')"}, w.superseded_by
1753
+ FROM wt.decisions w
1754
+ LEFT JOIN decisions m ON m.id = w.id
1755
+ WHERE true
1756
+ ON CONFLICT(id) DO UPDATE SET
1757
+ when_context = excluded.when_context,
1758
+ scope = excluded.scope,
1759
+ decision = excluded.decision,
1760
+ choice = excluded.choice,
1761
+ rationale = excluded.rationale,
1762
+ revisable = excluded.revisable,
1763
+ made_by = excluded.made_by,
1764
+ source = excluded.source,
1765
+ superseded_by = excluded.superseded_by
1766
+ `).run());
1767
+ }
1768
+ if (hasWtRequirements) {
1769
+ merged.requirements = countChanges(adapter.prepare(`
1770
+ INSERT OR REPLACE INTO requirements (
1771
+ id, class, status, description, why, source, primary_owner,
1772
+ supporting_slices, validation, notes, full_content, superseded_by
1773
+ )
1774
+ SELECT id, class, status, description, why, source, primary_owner,
1775
+ supporting_slices, validation, notes, full_content, superseded_by
1776
+ FROM wt.requirements
1777
+ `).run());
1778
+ }
1779
+ // Always recompute artifact hashes from the content being merged. Older
1780
+ // worktree DBs may not have content_hash at all, and migrated old DBs can
1781
+ // carry stale default/null hashes after their content changed.
1782
+ if (hasWtArtifacts) {
1783
+ const artifactRows = adapter.prepare(`
1784
+ SELECT path, artifact_type, milestone_id, slice_id, task_id, full_content, imported_at
1785
+ FROM wt.artifacts
1786
+ `).all();
1787
+ const artifactStmt = adapter.prepare(`
1788
+ INSERT OR REPLACE INTO artifacts (
1789
+ path, artifact_type, milestone_id, slice_id, task_id, full_content, imported_at, content_hash
1790
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
1791
+ `);
1792
+ for (const row of artifactRows) {
1793
+ const fullContent = String(row["full_content"] ?? "");
1794
+ merged.artifacts += countChanges(artifactStmt.run(row["path"], row["artifact_type"], row["milestone_id"] ?? null, row["slice_id"] ?? null, row["task_id"] ?? null, fullContent, row["imported_at"], createHash("sha256").update(fullContent).digest("hex")));
1795
+ }
1796
+ }
1685
1797
  // Merge milestones — worktree may have updated status/planning fields.
1686
1798
  // Never downgrade status: complete > active > pre-planning (#4372).
1687
1799
  // A stale worktree may carry an older 'active' status for a milestone
1688
1800
  // that the main DB has already marked 'complete'; preserve the higher status.
1689
- merged.milestones = countChanges(adapter.prepare(`
1690
- INSERT OR REPLACE INTO milestones (
1691
- id, title, status, depends_on, created_at, completed_at,
1692
- vision, success_criteria, key_risks, proof_strategy,
1693
- verification_contract, verification_integration, verification_operational, verification_uat,
1694
- definition_of_done, requirement_coverage, boundary_map_markdown, sequence
1695
- )
1696
- SELECT w.id, w.title,
1697
- CASE
1698
- WHEN m.status IN (${TERMINAL_STATUS_SQL}) AND w.status NOT IN (${TERMINAL_STATUS_SQL})
1699
- THEN m.status ELSE w.status
1700
- END,
1701
- w.depends_on,
1702
- CASE
1703
- WHEN m.status IN (${TERMINAL_STATUS_SQL}) AND w.status NOT IN (${TERMINAL_STATUS_SQL})
1704
- THEN m.created_at ELSE w.created_at
1705
- END,
1706
- CASE
1707
- WHEN m.status IN (${TERMINAL_STATUS_SQL}) AND w.status NOT IN (${TERMINAL_STATUS_SQL})
1708
- THEN m.completed_at ELSE w.completed_at
1709
- END,
1710
- w.vision, w.success_criteria, w.key_risks, w.proof_strategy,
1711
- w.verification_contract, w.verification_integration, w.verification_operational, w.verification_uat,
1712
- w.definition_of_done, w.requirement_coverage, w.boundary_map_markdown,
1713
- ${hasMilestoneSequence ? "COALESCE(w.sequence, 0)" : "COALESCE(m.sequence, 0)"}
1714
- FROM wt.milestones w
1715
- LEFT JOIN milestones m ON m.id = w.id
1716
- `).run());
1801
+ if (hasWtMilestones) {
1802
+ merged.milestones = countChanges(adapter.prepare(`
1803
+ INSERT OR REPLACE INTO milestones (
1804
+ id, title, status, depends_on, created_at, completed_at,
1805
+ vision, success_criteria, key_risks, proof_strategy,
1806
+ verification_contract, verification_integration, verification_operational, verification_uat,
1807
+ definition_of_done, requirement_coverage, boundary_map_markdown, sequence
1808
+ )
1809
+ SELECT w.id, w.title,
1810
+ CASE
1811
+ WHEN m.status IN (${TERMINAL_STATUS_SQL}) AND w.status NOT IN (${TERMINAL_STATUS_SQL})
1812
+ THEN m.status ELSE w.status
1813
+ END,
1814
+ w.depends_on,
1815
+ CASE
1816
+ WHEN m.status IN (${TERMINAL_STATUS_SQL}) AND w.status NOT IN (${TERMINAL_STATUS_SQL})
1817
+ THEN m.created_at ELSE w.created_at
1818
+ END,
1819
+ CASE
1820
+ WHEN m.status IN (${TERMINAL_STATUS_SQL}) AND w.status NOT IN (${TERMINAL_STATUS_SQL})
1821
+ THEN m.completed_at ELSE w.completed_at
1822
+ END,
1823
+ w.vision, w.success_criteria, w.key_risks, w.proof_strategy,
1824
+ w.verification_contract, w.verification_integration, w.verification_operational, w.verification_uat,
1825
+ w.definition_of_done, w.requirement_coverage, w.boundary_map_markdown,
1826
+ ${hasMilestoneSequence ? "COALESCE(w.sequence, 0)" : "COALESCE(m.sequence, 0)"}
1827
+ FROM wt.milestones w
1828
+ LEFT JOIN milestones m ON m.id = w.id
1829
+ `).run());
1830
+ }
1717
1831
  // Merge slices — preserve worktree progress but never downgrade completed status (#2558).
1718
1832
  // ADR-011 Phase 1: carry is_sketch + sketch_scope so reconcile doesn't
1719
1833
  // silently clear sketch metadata. When the worktree predates v16,
1720
1834
  // fall back to the main DB's existing value rather than a literal 0/''.
1721
- merged.slices = countChanges(adapter.prepare(`
1722
- INSERT OR REPLACE INTO slices (
1723
- milestone_id, id, title, status, risk, depends, demo, created_at, completed_at,
1724
- full_summary_md, full_uat_md, goal, success_criteria, proof_level,
1725
- integration_closure, observability_impact, target_repositories, sequence, replan_triggered_at,
1726
- is_sketch, sketch_scope
1727
- )
1728
- SELECT w.milestone_id, w.id, w.title,
1729
- CASE
1730
- WHEN m.status IN (${TERMINAL_STATUS_SQL}) AND w.status NOT IN (${TERMINAL_STATUS_SQL})
1731
- THEN m.status ELSE w.status
1732
- END,
1733
- w.risk, w.depends, w.demo, w.created_at,
1734
- CASE
1735
- WHEN m.status IN (${TERMINAL_STATUS_SQL}) AND w.status NOT IN (${TERMINAL_STATUS_SQL})
1736
- THEN m.completed_at ELSE w.completed_at
1737
- END,
1738
- w.full_summary_md, w.full_uat_md, w.goal, w.success_criteria, w.proof_level,
1739
- w.integration_closure, w.observability_impact,
1740
- ${hasSliceTargetRepositories ? "COALESCE(w.target_repositories, m.target_repositories, '[]')" : "COALESCE(m.target_repositories, '[]')"},
1741
- w.sequence, w.replan_triggered_at,
1742
- ${hasIsSketch ? "w.is_sketch" : "COALESCE(m.is_sketch, 0)"},
1743
- ${hasSketchScope ? "w.sketch_scope" : "COALESCE(m.sketch_scope, '')"}
1744
- FROM wt.slices w
1745
- LEFT JOIN slices m ON m.milestone_id = w.milestone_id AND m.id = w.id
1746
- `).run());
1835
+ if (hasWtSlices) {
1836
+ merged.slices = countChanges(adapter.prepare(`
1837
+ INSERT OR REPLACE INTO slices (
1838
+ milestone_id, id, title, status, risk, depends, demo, created_at, completed_at,
1839
+ full_summary_md, full_uat_md, goal, success_criteria, proof_level,
1840
+ integration_closure, observability_impact, target_repositories, sequence, replan_triggered_at,
1841
+ is_sketch, sketch_scope
1842
+ )
1843
+ SELECT w.milestone_id, w.id, w.title,
1844
+ CASE
1845
+ WHEN m.status IN (${TERMINAL_STATUS_SQL}) AND w.status NOT IN (${TERMINAL_STATUS_SQL})
1846
+ THEN m.status ELSE w.status
1847
+ END,
1848
+ w.risk, w.depends, w.demo, w.created_at,
1849
+ CASE
1850
+ WHEN m.status IN (${TERMINAL_STATUS_SQL}) AND w.status NOT IN (${TERMINAL_STATUS_SQL})
1851
+ THEN m.completed_at ELSE w.completed_at
1852
+ END,
1853
+ w.full_summary_md, w.full_uat_md, w.goal, w.success_criteria, w.proof_level,
1854
+ w.integration_closure, w.observability_impact,
1855
+ ${sliceTargetRepositoriesSql},
1856
+ w.sequence, w.replan_triggered_at,
1857
+ ${hasIsSketch ? "w.is_sketch" : "COALESCE(m.is_sketch, 0)"},
1858
+ ${hasSketchScope ? "w.sketch_scope" : "COALESCE(m.sketch_scope, '')"}
1859
+ FROM wt.slices w
1860
+ LEFT JOIN slices m ON m.milestone_id = w.milestone_id AND m.id = w.id
1861
+ `).run());
1862
+ }
1747
1863
  // Merge tasks — preserve execution results, never downgrade completed status (#2558).
1748
1864
  // ADR-011 P2: carry blocker_source + escalation_* columns so worktree reconcile
1749
1865
  // doesn't silently clear escalation state back to defaults.
1750
- merged.tasks = countChanges(adapter.prepare(`
1751
- INSERT OR REPLACE INTO tasks (
1752
- milestone_id, slice_id, id, title, status, one_liner, narrative,
1753
- verification_result, duration, completed_at, blocker_discovered,
1754
- deviations, known_issues, key_files, key_decisions, full_summary_md,
1755
- description, estimate, files, verify, inputs, expected_output,
1756
- observability_impact, full_plan_md, target_repositories, sequence,
1757
- blocker_source, escalation_pending, escalation_awaiting_review,
1758
- escalation_artifact_path, escalation_override_applied_at
1759
- )
1760
- SELECT w.milestone_id, w.slice_id, w.id, w.title,
1761
- CASE
1762
- WHEN m.status IN (${TERMINAL_STATUS_SQL}) AND w.status NOT IN (${TERMINAL_STATUS_SQL})
1763
- THEN m.status ELSE w.status
1764
- END,
1765
- w.one_liner, w.narrative,
1766
- w.verification_result, w.duration,
1767
- CASE
1768
- WHEN m.status IN (${TERMINAL_STATUS_SQL}) AND w.status NOT IN (${TERMINAL_STATUS_SQL})
1769
- THEN m.completed_at ELSE w.completed_at
1770
- END,
1771
- w.blocker_discovered,
1772
- w.deviations, w.known_issues, w.key_files, w.key_decisions, w.full_summary_md,
1773
- w.description, w.estimate, w.files, w.verify, w.inputs, w.expected_output,
1774
- w.observability_impact, w.full_plan_md,
1775
- ${hasTaskTargetRepositories ? "COALESCE(w.target_repositories, m.target_repositories, '[]')" : "COALESCE(m.target_repositories, '[]')"},
1776
- w.sequence,
1777
- ${hasBlockerSource ? "w.blocker_source" : "COALESCE(m.blocker_source, '')"},
1778
- ${hasEscalationPending ? "w.escalation_pending" : "COALESCE(m.escalation_pending, 0)"},
1779
- ${hasEscalationAwaiting ? "w.escalation_awaiting_review" : "COALESCE(m.escalation_awaiting_review, 0)"},
1780
- ${hasEscalationArtifact ? "w.escalation_artifact_path" : "m.escalation_artifact_path"},
1781
- ${hasEscalationOverride ? "w.escalation_override_applied_at" : "m.escalation_override_applied_at"}
1782
- FROM wt.tasks w
1783
- LEFT JOIN tasks m ON m.milestone_id = w.milestone_id AND m.slice_id = w.slice_id AND m.id = w.id
1784
- `).run());
1866
+ if (hasWtTasks) {
1867
+ merged.tasks = countChanges(adapter.prepare(`
1868
+ INSERT OR REPLACE INTO tasks (
1869
+ milestone_id, slice_id, id, title, status, one_liner, narrative,
1870
+ verification_result, duration, completed_at, blocker_discovered,
1871
+ deviations, known_issues, key_files, key_decisions, full_summary_md,
1872
+ description, estimate, files, verify, inputs, expected_output,
1873
+ observability_impact, full_plan_md, target_repositories, sequence,
1874
+ blocker_source, escalation_pending, escalation_awaiting_review,
1875
+ escalation_artifact_path, escalation_override_applied_at
1876
+ )
1877
+ SELECT w.milestone_id, w.slice_id, w.id, w.title,
1878
+ CASE
1879
+ WHEN m.status IN (${TERMINAL_STATUS_SQL}) AND w.status NOT IN (${TERMINAL_STATUS_SQL})
1880
+ THEN m.status ELSE w.status
1881
+ END,
1882
+ w.one_liner, w.narrative,
1883
+ w.verification_result, w.duration,
1884
+ CASE
1885
+ WHEN m.status IN (${TERMINAL_STATUS_SQL}) AND w.status NOT IN (${TERMINAL_STATUS_SQL})
1886
+ THEN m.completed_at ELSE w.completed_at
1887
+ END,
1888
+ w.blocker_discovered,
1889
+ w.deviations, w.known_issues, w.key_files, w.key_decisions, w.full_summary_md,
1890
+ w.description, w.estimate, w.files, w.verify, w.inputs, w.expected_output,
1891
+ w.observability_impact, w.full_plan_md,
1892
+ ${taskTargetRepositoriesSql},
1893
+ w.sequence,
1894
+ ${hasBlockerSource ? "w.blocker_source" : "COALESCE(m.blocker_source, '')"},
1895
+ ${hasEscalationPending ? "w.escalation_pending" : "COALESCE(m.escalation_pending, 0)"},
1896
+ ${hasEscalationAwaiting ? "w.escalation_awaiting_review" : "COALESCE(m.escalation_awaiting_review, 0)"},
1897
+ ${hasEscalationArtifact ? "w.escalation_artifact_path" : "m.escalation_artifact_path"},
1898
+ ${hasEscalationOverride ? "w.escalation_override_applied_at" : "m.escalation_override_applied_at"}
1899
+ FROM wt.tasks w
1900
+ LEFT JOIN tasks m ON m.milestone_id = w.milestone_id AND m.slice_id = w.slice_id AND m.id = w.id
1901
+ `).run());
1902
+ }
1785
1903
  // Merge memories — keep worktree-learned insights.
1786
1904
  // V18 (scope, tags), V21 (structured_fields), V28 (last_hit_at): for each
1787
1905
  // column the wt may not yet have (older worktree DB), fall back to the
1788
1906
  // main DB's existing value via LEFT JOIN so reconcile never silently
1789
1907
  // resets these fields to defaults on rows that already had them.
1790
- merged.memories = countChanges(adapter.prepare(`
1791
- INSERT OR REPLACE INTO memories (
1792
- seq, id, category, content, confidence, source_unit_type, source_unit_id,
1793
- created_at, updated_at, superseded_by, hit_count,
1794
- scope, tags, structured_fields, last_hit_at
1795
- )
1796
- SELECT w.seq, w.id, w.category, w.content, w.confidence, w.source_unit_type, w.source_unit_id,
1797
- w.created_at, w.updated_at, w.superseded_by, w.hit_count,
1798
- ${hasMemoryScope ? "w.scope" : "COALESCE(m.scope, 'project')"},
1799
- ${hasMemoryTags ? "w.tags" : "COALESCE(m.tags, '[]')"},
1800
- ${hasMemoryStructuredFields ? "w.structured_fields" : "m.structured_fields"},
1801
- ${hasMemoryLastHitAt ? "w.last_hit_at" : "m.last_hit_at"}
1802
- FROM wt.memories w
1803
- LEFT JOIN memories m ON m.id = w.id
1804
- `).run());
1908
+ if (hasWtMemories) {
1909
+ merged.memories = countChanges(adapter.prepare(`
1910
+ INSERT OR REPLACE INTO memories (
1911
+ seq, id, category, content, confidence, source_unit_type, source_unit_id,
1912
+ created_at, updated_at, superseded_by, hit_count,
1913
+ scope, tags, structured_fields, last_hit_at
1914
+ )
1915
+ SELECT w.seq, w.id, w.category, w.content, w.confidence, w.source_unit_type, w.source_unit_id,
1916
+ w.created_at, w.updated_at, w.superseded_by, w.hit_count,
1917
+ ${hasMemoryScope ? "w.scope" : "COALESCE(m.scope, 'project')"},
1918
+ ${hasMemoryTags ? "w.tags" : "COALESCE(m.tags, '[]')"},
1919
+ ${hasMemoryStructuredFields ? "w.structured_fields" : "m.structured_fields"},
1920
+ ${hasMemoryLastHitAt ? "w.last_hit_at" : "m.last_hit_at"}
1921
+ FROM wt.memories w
1922
+ LEFT JOIN memories m ON m.id = w.id
1923
+ `).run());
1924
+ }
1925
+ if (hasWtReplanHistory) {
1926
+ merged.replan_history = countChanges(adapter.prepare(`
1927
+ INSERT INTO replan_history (
1928
+ milestone_id, slice_id, task_id, summary, previous_artifact_path, replacement_artifact_path, created_at
1929
+ )
1930
+ SELECT w.milestone_id, w.slice_id, w.task_id, w.summary, w.previous_artifact_path, w.replacement_artifact_path, w.created_at
1931
+ FROM wt.replan_history w
1932
+ WHERE EXISTS (SELECT 1 FROM milestones m WHERE m.id = w.milestone_id)
1933
+ AND NOT EXISTS (
1934
+ SELECT 1 FROM replan_history m
1935
+ WHERE m.milestone_id = w.milestone_id
1936
+ AND m.slice_id IS w.slice_id
1937
+ AND m.task_id IS w.task_id
1938
+ AND m.summary = w.summary
1939
+ AND m.previous_artifact_path IS w.previous_artifact_path
1940
+ AND m.replacement_artifact_path IS w.replacement_artifact_path
1941
+ )
1942
+ `).run());
1943
+ }
1944
+ if (hasWtAssessments) {
1945
+ merged.assessments = countChanges(adapter.prepare(`
1946
+ INSERT OR REPLACE INTO assessments (
1947
+ path, milestone_id, slice_id, task_id, status, scope, full_content, created_at
1948
+ )
1949
+ SELECT w.path, w.milestone_id, w.slice_id, w.task_id, w.status, w.scope, w.full_content, w.created_at
1950
+ FROM wt.assessments w
1951
+ WHERE EXISTS (SELECT 1 FROM milestones m WHERE m.id = w.milestone_id)
1952
+ `).run());
1953
+ }
1954
+ if (hasWtQualityGates) {
1955
+ merged.quality_gates = countChanges(adapter.prepare(`
1956
+ INSERT OR REPLACE INTO quality_gates (
1957
+ milestone_id, slice_id, gate_id, scope, task_id, status, verdict, rationale, findings, evaluated_at
1958
+ )
1959
+ SELECT w.milestone_id, w.slice_id, w.gate_id, w.scope, COALESCE(w.task_id, ''), w.status, w.verdict, w.rationale, w.findings, w.evaluated_at
1960
+ FROM wt.quality_gates w
1961
+ WHERE EXISTS (SELECT 1 FROM slices s WHERE s.milestone_id = w.milestone_id AND s.id = w.slice_id)
1962
+ `).run());
1963
+ }
1964
+ if (hasWtSliceDependencies) {
1965
+ merged.slice_dependencies = countChanges(adapter.prepare(`
1966
+ INSERT OR IGNORE INTO slice_dependencies (milestone_id, slice_id, depends_on_slice_id)
1967
+ SELECT w.milestone_id, w.slice_id, w.depends_on_slice_id
1968
+ FROM wt.slice_dependencies w
1969
+ WHERE EXISTS (SELECT 1 FROM slices s WHERE s.milestone_id = w.milestone_id AND s.id = w.slice_id)
1970
+ AND EXISTS (SELECT 1 FROM slices d WHERE d.milestone_id = w.milestone_id AND d.id = w.depends_on_slice_id)
1971
+ `).run());
1972
+ }
1805
1973
  // Merge verification evidence — append-only, use INSERT OR IGNORE to avoid duplicates
1806
- merged.verification_evidence = countChanges(adapter.prepare(`
1807
- INSERT OR IGNORE INTO verification_evidence (
1808
- task_id, slice_id, milestone_id, command, exit_code, verdict, duration_ms, created_at
1809
- )
1810
- SELECT task_id, slice_id, milestone_id, command, exit_code, verdict, duration_ms, created_at
1811
- FROM wt.verification_evidence
1812
- `).run());
1974
+ if (hasWtVerificationEvidence) {
1975
+ merged.verification_evidence = countChanges(adapter.prepare(`
1976
+ INSERT OR IGNORE INTO verification_evidence (
1977
+ task_id, slice_id, milestone_id, command, exit_code, verdict, duration_ms, created_at
1978
+ )
1979
+ SELECT task_id, slice_id, milestone_id, command, exit_code, verdict, duration_ms, created_at
1980
+ FROM wt.verification_evidence
1981
+ `).run());
1982
+ }
1983
+ if (hasWtGateRuns) {
1984
+ merged.gate_runs = countChanges(adapter.prepare(`
1985
+ INSERT INTO gate_runs (
1986
+ trace_id, turn_id, gate_id, gate_type, unit_type, unit_id, milestone_id, slice_id, task_id,
1987
+ outcome, failure_class, rationale, findings, attempt, max_attempts, retryable, evaluated_at
1988
+ )
1989
+ SELECT w.trace_id, w.turn_id, w.gate_id, w.gate_type, w.unit_type, w.unit_id, w.milestone_id, w.slice_id, w.task_id,
1990
+ w.outcome, w.failure_class, w.rationale, w.findings, w.attempt, w.max_attempts, w.retryable, w.evaluated_at
1991
+ FROM wt.gate_runs w
1992
+ WHERE NOT EXISTS (
1993
+ SELECT 1 FROM gate_runs m
1994
+ WHERE m.trace_id = w.trace_id
1995
+ AND m.turn_id = w.turn_id
1996
+ AND m.gate_id = w.gate_id
1997
+ AND m.attempt = w.attempt
1998
+ AND m.evaluated_at = w.evaluated_at
1999
+ )
2000
+ `).run());
2001
+ }
2002
+ if (hasWtMilestoneCommitAttributions) {
2003
+ merged.milestone_commit_attributions = countChanges(adapter.prepare(`
2004
+ INSERT OR REPLACE INTO milestone_commit_attributions (
2005
+ commit_sha, milestone_id, slice_id, task_id, source, confidence, files_json, created_at
2006
+ )
2007
+ SELECT w.commit_sha, w.milestone_id, w.slice_id, w.task_id, w.source, w.confidence, w.files_json, w.created_at
2008
+ FROM wt.milestone_commit_attributions w
2009
+ WHERE EXISTS (SELECT 1 FROM milestones m WHERE m.id = w.milestone_id)
2010
+ `).run());
2011
+ }
1813
2012
  adapter.exec("COMMIT");
1814
2013
  }
1815
2014
  catch (txErr) {
@@ -1979,11 +2178,12 @@ export function insertGateRow(g) {
1979
2178
  export function saveGateResult(g) {
1980
2179
  if (!currentDb)
1981
2180
  throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
1982
- currentDb.prepare(`UPDATE quality_gates
2181
+ const evaluatedAt = new Date().toISOString();
2182
+ const result = currentDb.prepare(`UPDATE quality_gates
1983
2183
  SET status = 'complete', verdict = :verdict, rationale = :rationale,
1984
2184
  findings = :findings, evaluated_at = :evaluated_at
1985
2185
  WHERE milestone_id = :mid AND slice_id = :sid AND gate_id = :gid
1986
- AND task_id = :tid`).run({
2186
+ AND (task_id = :tid OR (:tid = '' AND task_id IS NULL))`).run({
1987
2187
  ":mid": g.milestoneId,
1988
2188
  ":sid": g.sliceId,
1989
2189
  ":gid": g.gateId,
@@ -1991,8 +2191,11 @@ export function saveGateResult(g) {
1991
2191
  ":verdict": g.verdict,
1992
2192
  ":rationale": g.rationale,
1993
2193
  ":findings": g.findings,
1994
- ":evaluated_at": new Date().toISOString(),
2194
+ ":evaluated_at": evaluatedAt,
1995
2195
  });
2196
+ if ((result.changes ?? 0) === 0) {
2197
+ throw new GSDError(GSD_STALE_STATE, `quality gate row not found for ${g.milestoneId}/${g.sliceId}/${g.gateId}${g.taskId ? `/${g.taskId}` : ""}`);
2198
+ }
1996
2199
  const outcome = g.verdict === "pass"
1997
2200
  ? "pass"
1998
2201
  : g.verdict === "omitted"
@@ -2013,7 +2216,7 @@ export function saveGateResult(g) {
2013
2216
  attempt: 1,
2014
2217
  maxAttempts: 1,
2015
2218
  retryable: false,
2016
- evaluatedAt: new Date().toISOString(),
2219
+ evaluatedAt,
2017
2220
  });
2018
2221
  }
2019
2222
  export function getPendingGates(milestoneId, sliceId, scope) {
@@ -2048,6 +2251,25 @@ export function markAllGatesOmitted(milestoneId, sliceId) {
2048
2251
  ":now": new Date().toISOString(),
2049
2252
  });
2050
2253
  }
2254
+ export function markPendingGatesOmittedForTurn(milestoneId, sliceId, turn) {
2255
+ if (!currentDb)
2256
+ return;
2257
+ const gateIds = [...getGateIdsForTurn(turn)];
2258
+ if (gateIds.length === 0)
2259
+ return;
2260
+ const placeholders = gateIds.map((_, i) => `:gid${i}`).join(",");
2261
+ const params = {
2262
+ ":mid": milestoneId,
2263
+ ":sid": sliceId,
2264
+ ":now": new Date().toISOString(),
2265
+ };
2266
+ gateIds.forEach((id, index) => {
2267
+ params[`:gid${index}`] = id;
2268
+ });
2269
+ currentDb.prepare(`UPDATE quality_gates SET status = 'complete', verdict = 'omitted', evaluated_at = :now
2270
+ WHERE milestone_id = :mid AND slice_id = :sid AND status = 'pending'
2271
+ AND gate_id IN (${placeholders})`).run(params);
2272
+ }
2051
2273
  export function getPendingSliceGateCount(milestoneId, sliceId) {
2052
2274
  if (!currentDb)
2053
2275
  return 0;
@@ -2358,21 +2580,64 @@ export function upsertQualityGate(g) {
2358
2580
  }
2359
2581
  /**
2360
2582
  * Atomically replace all workflow state from a manifest. Lifted verbatim from
2361
- * workflow-manifest.ts so the single-writer invariant holds. Only touches
2362
- * engine tables + decisions. Does NOT modify artifacts or memories.
2583
+ * workflow-manifest.ts so the single-writer invariant holds. Restores
2584
+ * correctness-bearing workflow tables; runtime soft state and append-only audit
2585
+ * streams stay outside this recovery path.
2363
2586
  */
2364
2587
  export function restoreManifest(manifest) {
2365
2588
  if (!currentDb)
2366
2589
  throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
2367
2590
  const db = currentDb;
2368
2591
  transaction(() => {
2369
- // Clear engine tables (order matters for foreign-key-like consistency)
2592
+ const restoredMilestoneIds = new Set(manifest.milestones.map((m) => m.id));
2593
+ const restoredSliceKeys = new Set(manifest.slices.map((s) => JSON.stringify([s.milestone_id, s.id])));
2594
+ const preservedReplanHistory = manifest.replan_history === undefined
2595
+ ? db.prepare("SELECT * FROM replan_history ORDER BY id").all()
2596
+ : [];
2597
+ const preservedAssessments = manifest.assessments === undefined
2598
+ ? db.prepare("SELECT * FROM assessments ORDER BY path").all()
2599
+ : [];
2600
+ const preservedQualityGates = manifest.quality_gates === undefined
2601
+ ? db.prepare("SELECT * FROM quality_gates ORDER BY milestone_id, slice_id, gate_id, task_id").all()
2602
+ : [];
2603
+ const preservedCommitAttributions = manifest.milestone_commit_attributions === undefined
2604
+ ? db.prepare("SELECT * FROM milestone_commit_attributions ORDER BY milestone_id, commit_sha").all()
2605
+ : [];
2606
+ // Clear workflow tables in dependency order.
2370
2607
  db.exec("DELETE FROM verification_evidence");
2608
+ db.exec("DELETE FROM quality_gates");
2609
+ db.exec("DELETE FROM slice_dependencies");
2610
+ db.exec("DELETE FROM assessments");
2611
+ db.exec("DELETE FROM replan_history");
2612
+ db.exec("DELETE FROM milestone_commit_attributions");
2371
2613
  db.exec("DELETE FROM tasks");
2372
2614
  db.exec("DELETE FROM slices");
2373
2615
  db.exec("DELETE FROM milestone_leases");
2374
2616
  db.exec("DELETE FROM milestones");
2375
2617
  db.exec("DELETE FROM decisions WHERE 1=1");
2618
+ db.exec(`DELETE FROM memories WHERE category = 'architecture' AND structured_fields LIKE '%"sourceDecisionId":"%'`);
2619
+ if (manifest.artifacts !== undefined)
2620
+ db.exec("DELETE FROM artifacts");
2621
+ if (manifest.requirements !== undefined)
2622
+ db.exec("DELETE FROM requirements");
2623
+ if (manifest.requirements !== undefined) {
2624
+ const reqStmt = db.prepare(`INSERT INTO requirements (
2625
+ id, class, status, description, why, source, primary_owner,
2626
+ supporting_slices, validation, notes, full_content, superseded_by
2627
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);
2628
+ for (const r of manifest.requirements) {
2629
+ reqStmt.run(r.id, r.class, r.status, r.description, r.why, r.source, r.primary_owner, r.supporting_slices, r.validation, r.notes, r.full_content, r.superseded_by);
2630
+ }
2631
+ }
2632
+ if (manifest.artifacts !== undefined) {
2633
+ const artStmt = db.prepare(`INSERT INTO artifacts (
2634
+ path, artifact_type, milestone_id, slice_id, task_id, full_content, imported_at, content_hash
2635
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`);
2636
+ for (const a of manifest.artifacts) {
2637
+ const fullContent = a.full_content ?? "";
2638
+ artStmt.run(a.path, a.artifact_type, a.milestone_id, a.slice_id, a.task_id, fullContent, a.imported_at, a.content_hash ?? createHash("sha256").update(fullContent).digest("hex"));
2639
+ }
2640
+ }
2376
2641
  // Restore milestones
2377
2642
  const msStmt = db.prepare(`INSERT INTO milestones (id, title, status, depends_on, created_at, completed_at,
2378
2643
  vision, success_criteria, key_risks, proof_strategy,
@@ -2391,17 +2656,23 @@ export function restoreManifest(manifest) {
2391
2656
  for (const s of manifest.slices) {
2392
2657
  slStmt.run(s.milestone_id, s.id, s.title, s.status, s.risk, JSON.stringify(s.depends), s.demo, s.created_at, s.completed_at, s.full_summary_md, s.full_uat_md, s.goal, s.success_criteria, s.proof_level, s.integration_closure, s.observability_impact, JSON.stringify(s.target_repositories ?? []), s.sequence, s.replan_triggered_at, s.is_sketch ?? 0, s.sketch_scope ?? "");
2393
2658
  }
2659
+ const depStmt = db.prepare("INSERT OR IGNORE INTO slice_dependencies (milestone_id, slice_id, depends_on_slice_id) VALUES (?, ?, ?)");
2660
+ for (const s of manifest.slices) {
2661
+ for (const dep of s.depends ?? []) {
2662
+ depStmt.run(s.milestone_id, s.id, dep);
2663
+ }
2664
+ }
2394
2665
  // Restore tasks (ADR-011 P2: includes blocker_source + escalation_* columns)
2395
2666
  const tkStmt = db.prepare(`INSERT INTO tasks (milestone_id, slice_id, id, title, status,
2396
2667
  one_liner, narrative, verification_result, duration, completed_at,
2397
2668
  blocker_discovered, deviations, known_issues, key_files, key_decisions,
2398
2669
  full_summary_md, description, estimate, files, verify,
2399
- inputs, expected_output, observability_impact, target_repositories, sequence,
2670
+ inputs, expected_output, observability_impact, full_plan_md, target_repositories, sequence,
2400
2671
  blocker_source, escalation_pending, escalation_awaiting_review,
2401
2672
  escalation_artifact_path, escalation_override_applied_at)
2402
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);
2673
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);
2403
2674
  for (const t of manifest.tasks) {
2404
- tkStmt.run(t.milestone_id, t.slice_id, t.id, t.title, t.status, t.one_liner, t.narrative, t.verification_result, t.duration, t.completed_at, t.blocker_discovered ? 1 : 0, t.deviations, t.known_issues, JSON.stringify(t.key_files), JSON.stringify(t.key_decisions), t.full_summary_md, t.description, t.estimate, JSON.stringify(t.files), t.verify, JSON.stringify(t.inputs), JSON.stringify(t.expected_output), t.observability_impact, JSON.stringify(t.target_repositories ?? []), t.sequence, t.blocker_source ?? "", t.escalation_pending ?? 0, t.escalation_awaiting_review ?? 0, t.escalation_artifact_path ?? null, t.escalation_override_applied_at ?? null);
2675
+ tkStmt.run(t.milestone_id, t.slice_id, t.id, t.title, t.status, t.one_liner, t.narrative, t.verification_result, t.duration, t.completed_at, t.blocker_discovered ? 1 : 0, t.deviations, t.known_issues, JSON.stringify(t.key_files), JSON.stringify(t.key_decisions), t.full_summary_md, t.description, t.estimate, JSON.stringify(t.files), t.verify, JSON.stringify(t.inputs), JSON.stringify(t.expected_output), t.observability_impact, t.full_plan_md ?? "", JSON.stringify(t.target_repositories ?? []), t.sequence, t.blocker_source ?? "", t.escalation_pending ?? 0, t.escalation_awaiting_review ?? 0, t.escalation_artifact_path ?? null, t.escalation_override_applied_at ?? null);
2405
2676
  }
2406
2677
  // Restore decisions (ADR-011 P2: include source so escalation decisions survive)
2407
2678
  const dcStmt = db.prepare(`INSERT INTO decisions (seq, id, when_context, scope, decision, choice, rationale, revisable, made_by, source, superseded_by)
@@ -2409,6 +2680,43 @@ export function restoreManifest(manifest) {
2409
2680
  for (const d of manifest.decisions) {
2410
2681
  dcStmt.run(d.seq, d.id, d.when_context, d.scope, d.decision, d.choice, d.rationale, d.revisable, d.made_by, d.source ?? "discussion", d.superseded_by);
2411
2682
  }
2683
+ const replanHistoryRows = manifest.replan_history ?? preservedReplanHistory.filter((r) => restoredMilestoneIds.has(r.milestone_id));
2684
+ if (replanHistoryRows.length > 0) {
2685
+ const replStmt = db.prepare(`INSERT INTO replan_history (
2686
+ id, milestone_id, slice_id, task_id, summary, previous_artifact_path, replacement_artifact_path, created_at
2687
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`);
2688
+ for (const r of replanHistoryRows) {
2689
+ replStmt.run(r.id, r.milestone_id, r.slice_id, r.task_id, r.summary, r.previous_artifact_path, r.replacement_artifact_path, r.created_at);
2690
+ }
2691
+ }
2692
+ const assessmentRows = manifest.assessments ?? preservedAssessments.filter((a) => restoredMilestoneIds.has(a.milestone_id));
2693
+ if (assessmentRows.length > 0) {
2694
+ const assessStmt = db.prepare(`INSERT INTO assessments (
2695
+ path, milestone_id, slice_id, task_id, status, scope, full_content, created_at
2696
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`);
2697
+ for (const a of assessmentRows) {
2698
+ assessStmt.run(a.path, a.milestone_id, a.slice_id, a.task_id, a.status, a.scope, a.full_content, a.created_at);
2699
+ }
2700
+ }
2701
+ const qualityGateRows = manifest.quality_gates ?? preservedQualityGates.filter((g) => (restoredSliceKeys.has(JSON.stringify([g.milestone_id, g.slice_id]))));
2702
+ if (qualityGateRows.length > 0) {
2703
+ const gateStmt = db.prepare(`INSERT INTO quality_gates (
2704
+ milestone_id, slice_id, gate_id, scope, task_id, status, verdict, rationale, findings, evaluated_at
2705
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);
2706
+ for (const g of qualityGateRows) {
2707
+ gateStmt.run(g.milestone_id, g.slice_id, g.gate_id, g.scope, g.task_id, g.status, g.verdict ?? "", g.rationale, g.findings, g.evaluated_at);
2708
+ }
2709
+ }
2710
+ const commitAttributionRows = manifest.milestone_commit_attributions ??
2711
+ preservedCommitAttributions.filter((a) => restoredMilestoneIds.has(a.milestone_id));
2712
+ if (commitAttributionRows.length > 0) {
2713
+ const attrStmt = db.prepare(`INSERT OR REPLACE INTO milestone_commit_attributions (
2714
+ commit_sha, milestone_id, slice_id, task_id, source, confidence, files_json, created_at
2715
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`);
2716
+ for (const a of commitAttributionRows) {
2717
+ attrStmt.run(a.commit_sha, a.milestone_id, a.slice_id, a.task_id, a.source, a.confidence, a.files_json, a.created_at);
2718
+ }
2719
+ }
2412
2720
  // Restore verification evidence
2413
2721
  const evStmt = db.prepare(`INSERT INTO verification_evidence (task_id, slice_id, milestone_id, command, exit_code, verdict, duration_ms, created_at)
2414
2722
  VALUES (?, ?, ?, ?, ?, ?, ?, ?)`);