@opengsd/gsd-pi 1.5.0 → 1.6.0

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 (499) hide show
  1. package/README.md +10 -10
  2. package/dist/resources/.managed-resources-content-hash +1 -1
  3. package/dist/resources/extensions/ask-user-questions.js +1 -1
  4. package/dist/resources/extensions/browser-tools/engine/managed-gsd-browser.js +114 -36
  5. package/dist/resources/extensions/gsd/activity-log.js +1 -1
  6. package/dist/resources/extensions/gsd/auto/closeout.js +97 -5
  7. package/dist/resources/extensions/gsd/auto/orchestrator.js +20 -13
  8. package/dist/resources/extensions/gsd/auto/pre-dispatch.js +6 -3
  9. package/dist/resources/extensions/gsd/auto/unit-phase.js +5 -9
  10. package/dist/resources/extensions/gsd/auto-artifact-paths.js +14 -5
  11. package/dist/resources/extensions/gsd/auto-direct-dispatch.js +11 -7
  12. package/dist/resources/extensions/gsd/auto-dispatch.js +69 -17
  13. package/dist/resources/extensions/gsd/auto-model-selection.js +28 -1
  14. package/dist/resources/extensions/gsd/auto-post-unit.js +4 -3
  15. package/dist/resources/extensions/gsd/auto-prompts.js +29 -117
  16. package/dist/resources/extensions/gsd/auto-recovery.js +50 -6
  17. package/dist/resources/extensions/gsd/auto-start.js +57 -33
  18. package/dist/resources/extensions/gsd/auto-timeout-recovery.js +12 -2
  19. package/dist/resources/extensions/gsd/auto-timers.js +9 -2
  20. package/dist/resources/extensions/gsd/auto-worktree-branch-lifecycle.js +189 -0
  21. package/dist/resources/extensions/gsd/auto-worktree-cleanup.js +133 -0
  22. package/dist/resources/extensions/gsd/auto-worktree-conflict-auto-resolve.js +35 -0
  23. package/dist/resources/extensions/gsd/auto-worktree-creation.js +130 -0
  24. package/dist/resources/extensions/gsd/auto-worktree-entry.js +118 -0
  25. package/dist/resources/extensions/gsd/auto-worktree-merge-already-merged.js +98 -0
  26. package/dist/resources/extensions/gsd/auto-worktree-merge-branch-head.js +52 -0
  27. package/dist/resources/extensions/gsd/auto-worktree-merge-cleanup.js +87 -0
  28. package/dist/resources/extensions/gsd/auto-worktree-merge-code-changes.js +46 -0
  29. package/dist/resources/extensions/gsd/auto-worktree-merge-db-ready.js +68 -0
  30. package/dist/resources/extensions/gsd/auto-worktree-merge-integration-branch.js +51 -0
  31. package/dist/resources/extensions/gsd/auto-worktree-merge-message.js +106 -0
  32. package/dist/resources/extensions/gsd/auto-worktree-merge-pre-teardown.js +64 -0
  33. package/dist/resources/extensions/gsd/auto-worktree-merge-stash.js +168 -0
  34. package/dist/resources/extensions/gsd/auto-worktree-merge.js +302 -0
  35. package/dist/resources/extensions/gsd/auto-worktree-milestone-shelter.js +119 -0
  36. package/dist/resources/extensions/gsd/auto-worktree-path-resolution.js +5 -0
  37. package/dist/resources/extensions/gsd/auto-worktree-path-utils.js +5 -0
  38. package/dist/resources/extensions/gsd/auto-worktree-resource-version.js +41 -0
  39. package/dist/resources/extensions/gsd/auto-worktree-runtime-cleanup.js +99 -0
  40. package/dist/resources/extensions/gsd/auto-worktree-session-registry.js +57 -0
  41. package/dist/resources/extensions/gsd/auto-worktree-sync.js +249 -0
  42. package/dist/resources/extensions/gsd/auto-worktree-teardown.js +120 -0
  43. package/dist/resources/extensions/gsd/auto-worktree.js +20 -2073
  44. package/dist/resources/extensions/gsd/auto.js +106 -34
  45. package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +1 -1
  46. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +20 -2
  47. package/dist/resources/extensions/gsd/bootstrap/system-context.js +1 -1
  48. package/dist/resources/extensions/gsd/bootstrap/tool-call-loop-guard.js +185 -27
  49. package/dist/resources/extensions/gsd/codebase-generator.js +7 -1
  50. package/dist/resources/extensions/gsd/commands-handlers.js +1 -1
  51. package/dist/resources/extensions/gsd/commands-workflow-templates.js +3 -3
  52. package/dist/resources/extensions/gsd/compat/compat-marker.js +44 -0
  53. package/dist/resources/extensions/gsd/config-overlay.js +7 -2
  54. package/dist/resources/extensions/gsd/db/engine.js +33 -0
  55. package/dist/resources/extensions/gsd/db/queries.js +9 -9
  56. package/dist/resources/extensions/gsd/db/writers/cascades.js +20 -2
  57. package/dist/resources/extensions/gsd/db-adapter.js +30 -3
  58. package/dist/resources/extensions/gsd/docs/preferences-reference.md +3 -3
  59. package/dist/resources/extensions/gsd/doctor-engine-checks.js +93 -11
  60. package/dist/resources/extensions/gsd/flat-phase-migration.js +32 -3
  61. package/dist/resources/extensions/gsd/forensics.js +1 -1
  62. package/dist/resources/extensions/gsd/git-conflict-resolve.js +2 -2
  63. package/dist/resources/extensions/gsd/git-service.js +45 -0
  64. package/dist/resources/extensions/gsd/gsd-db.js +23 -11
  65. package/dist/resources/extensions/gsd/guided-flow.js +22 -17
  66. package/dist/resources/extensions/gsd/journal.js +1 -1
  67. package/dist/resources/extensions/gsd/markdown-renderer.js +34 -79
  68. package/dist/resources/extensions/gsd/md-importer.js +89 -23
  69. package/dist/resources/extensions/gsd/metrics.js +2 -2
  70. package/dist/resources/extensions/gsd/migration-auto-check.js +30 -3
  71. package/dist/resources/extensions/gsd/milestone-merge-transaction.js +11 -0
  72. package/dist/resources/extensions/gsd/milestone-settlement.js +1 -1
  73. package/dist/resources/extensions/gsd/parallel-merge.js +1 -1
  74. package/dist/resources/extensions/gsd/parallel-orchestrator.js +3 -1
  75. package/dist/resources/extensions/gsd/paths.js +87 -52
  76. package/dist/resources/extensions/gsd/post-unit-hooks.js +27 -0
  77. package/dist/resources/extensions/gsd/preferences-models.js +66 -15
  78. package/dist/resources/extensions/gsd/preferences-skills.js +10 -16
  79. package/dist/resources/extensions/gsd/preferences-types.js +1 -0
  80. package/dist/resources/extensions/gsd/preferences-validation.js +148 -10
  81. package/dist/resources/extensions/gsd/preferences.js +20 -0
  82. package/dist/resources/extensions/gsd/prompts/system.md +6 -6
  83. package/dist/resources/extensions/gsd/quick.js +12 -3
  84. package/dist/resources/extensions/gsd/rule-registry.js +106 -10
  85. package/dist/resources/extensions/gsd/skill-discovery.js +36 -23
  86. package/dist/resources/extensions/gsd/slice-parallel-orchestrator.js +2 -1
  87. package/dist/resources/extensions/gsd/state/derive/from-db.js +88 -153
  88. package/dist/resources/extensions/gsd/state-reconciliation/drift/artifact-db.js +1 -10
  89. package/dist/resources/extensions/gsd/state-reconciliation/drift/completion.js +3 -24
  90. package/dist/resources/extensions/gsd/state-reconciliation/index.js +10 -0
  91. package/dist/resources/extensions/gsd/tool-contract.js +14 -0
  92. package/dist/resources/extensions/gsd/tools/complete-milestone.js +3 -5
  93. package/dist/resources/extensions/gsd/tools/reassess-roadmap.js +16 -10
  94. package/dist/resources/extensions/gsd/tools/reopen-slice.js +5 -0
  95. package/dist/resources/extensions/gsd/tools/reopen-task.js +13 -5
  96. package/dist/resources/extensions/gsd/tools/validate-milestone.js +3 -8
  97. package/dist/resources/extensions/gsd/uat-dispatch.js +94 -0
  98. package/dist/resources/extensions/gsd/unit-registry.js +12 -2
  99. package/dist/resources/extensions/gsd/unmerged-milestone-guard.js +1 -1
  100. package/dist/resources/extensions/gsd/uok/audit-toggle.js +31 -2
  101. package/dist/resources/extensions/gsd/uok/audit.js +3 -0
  102. package/dist/resources/extensions/gsd/uok/gate-runner.js +22 -30
  103. package/dist/resources/extensions/gsd/uok/gitops.js +18 -15
  104. package/dist/resources/extensions/gsd/uok/kernel.js +120 -46
  105. package/dist/resources/extensions/gsd/uok/model-policy.js +17 -14
  106. package/dist/resources/extensions/gsd/validation-block-guard.js +2 -1
  107. package/dist/resources/extensions/gsd/workflow-event-ledger.js +1 -1
  108. package/dist/resources/extensions/gsd/workflow-logger.js +1 -1
  109. package/dist/resources/extensions/gsd/workflow-projections.js +9 -26
  110. package/dist/resources/extensions/gsd/workspace-git-preflight.js +2 -2
  111. package/dist/resources/extensions/gsd/workspace.js +5 -14
  112. package/dist/resources/extensions/gsd/worktree-command.js +1 -1
  113. package/dist/resources/extensions/gsd/worktree-lifecycle.js +14 -9
  114. package/dist/resources/extensions/gsd/worktree-reentry.js +2 -1
  115. package/dist/resources/extensions/shared/gsd-browser-cli.js +29 -0
  116. package/dist/resources/extensions/shared/interview-ui.js +62 -9
  117. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  118. package/dist/web/standalone/.next/BUILD_ID +1 -1
  119. package/dist/web/standalone/.next/app-path-routes-manifest.json +9 -9
  120. package/dist/web/standalone/.next/build-manifest.json +2 -2
  121. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  122. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  123. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  124. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  125. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  126. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  127. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  128. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  129. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  130. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  131. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  132. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  133. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  134. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  135. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  136. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  137. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  138. package/dist/web/standalone/.next/server/app/api/boot/route.js.nft.json +1 -1
  139. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js.nft.json +1 -1
  140. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js.nft.json +1 -1
  141. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js.nft.json +1 -1
  142. package/dist/web/standalone/.next/server/app/api/captures/route.js.nft.json +1 -1
  143. package/dist/web/standalone/.next/server/app/api/cleanup/route.js.nft.json +1 -1
  144. package/dist/web/standalone/.next/server/app/api/doctor/route.js.nft.json +1 -1
  145. package/dist/web/standalone/.next/server/app/api/export-data/route.js.nft.json +1 -1
  146. package/dist/web/standalone/.next/server/app/api/files/route.js.nft.json +1 -1
  147. package/dist/web/standalone/.next/server/app/api/forensics/route.js.nft.json +1 -1
  148. package/dist/web/standalone/.next/server/app/api/git/route.js.nft.json +1 -1
  149. package/dist/web/standalone/.next/server/app/api/history/route.js.nft.json +1 -1
  150. package/dist/web/standalone/.next/server/app/api/hooks/route.js.nft.json +1 -1
  151. package/dist/web/standalone/.next/server/app/api/inspect/route.js.nft.json +1 -1
  152. package/dist/web/standalone/.next/server/app/api/knowledge/route.js.nft.json +1 -1
  153. package/dist/web/standalone/.next/server/app/api/live-state/route.js.nft.json +1 -1
  154. package/dist/web/standalone/.next/server/app/api/mcp-connections/route.js.nft.json +1 -1
  155. package/dist/web/standalone/.next/server/app/api/notifications/route.js.nft.json +1 -1
  156. package/dist/web/standalone/.next/server/app/api/onboarding/route.js.nft.json +1 -1
  157. package/dist/web/standalone/.next/server/app/api/projects/route.js.nft.json +1 -1
  158. package/dist/web/standalone/.next/server/app/api/recovery/route.js.nft.json +1 -1
  159. package/dist/web/standalone/.next/server/app/api/session/browser/route.js.nft.json +1 -1
  160. package/dist/web/standalone/.next/server/app/api/session/command/route.js.nft.json +1 -1
  161. package/dist/web/standalone/.next/server/app/api/session/events/route.js.nft.json +1 -1
  162. package/dist/web/standalone/.next/server/app/api/session/manage/route.js.nft.json +1 -1
  163. package/dist/web/standalone/.next/server/app/api/settings-data/route.js.nft.json +1 -1
  164. package/dist/web/standalone/.next/server/app/api/shutdown/route.js.nft.json +1 -1
  165. package/dist/web/standalone/.next/server/app/api/skill-health/route.js.nft.json +1 -1
  166. package/dist/web/standalone/.next/server/app/api/steer/route.js.nft.json +1 -1
  167. package/dist/web/standalone/.next/server/app/api/switch-root/route.js.nft.json +1 -1
  168. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js.nft.json +1 -1
  169. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js.nft.json +1 -1
  170. package/dist/web/standalone/.next/server/app/api/undo/route.js.nft.json +1 -1
  171. package/dist/web/standalone/.next/server/app/api/visualizer/route.js.nft.json +1 -1
  172. package/dist/web/standalone/.next/server/app/index.html +1 -1
  173. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  174. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  175. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  176. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  177. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  178. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  179. package/dist/web/standalone/.next/server/app-paths-manifest.json +9 -9
  180. package/dist/web/standalone/.next/server/chunks/{1128.js → 1991.js} +1 -1
  181. package/dist/web/standalone/.next/server/chunks/8357.js +1 -1
  182. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  183. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  184. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  185. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  186. package/dist/web/standalone/node_modules/@gsd/native/package.json +1 -1
  187. package/dist/web/standalone/node_modules/node-pty/build/Makefile +1 -1
  188. package/dist/worktree-cli-create.d.ts +10 -0
  189. package/dist/worktree-cli-create.js +11 -0
  190. package/dist/worktree-cli-format.d.ts +12 -0
  191. package/dist/worktree-cli-format.js +38 -0
  192. package/dist/worktree-cli-plan.d.ts +16 -0
  193. package/dist/worktree-cli-plan.js +19 -0
  194. package/dist/worktree-cli-session.d.ts +13 -0
  195. package/dist/worktree-cli-session.js +18 -0
  196. package/dist/worktree-cli-status.d.ts +35 -0
  197. package/dist/worktree-cli-status.js +54 -0
  198. package/dist/worktree-cli.d.ts +1 -16
  199. package/dist/worktree-cli.js +28 -122
  200. package/integrations/hermes/open_gsd_hermes/gsd_client.py +25 -36
  201. package/integrations/hermes/pyproject.toml +1 -1
  202. package/integrations/hermes/tests/test_gsd_client_timeout.py +4 -4
  203. package/package.json +6 -6
  204. package/packages/cloud-mcp-gateway/package.json +2 -2
  205. package/packages/contracts/package.json +1 -1
  206. package/packages/daemon/dist/orchestrator-agent.d.ts +21 -0
  207. package/packages/daemon/dist/orchestrator-agent.d.ts.map +1 -0
  208. package/packages/daemon/dist/orchestrator-agent.js +113 -0
  209. package/packages/daemon/dist/orchestrator-agent.js.map +1 -0
  210. package/packages/daemon/dist/orchestrator-tools.d.ts +12 -0
  211. package/packages/daemon/dist/orchestrator-tools.d.ts.map +1 -0
  212. package/packages/daemon/dist/orchestrator-tools.js +137 -0
  213. package/packages/daemon/dist/orchestrator-tools.js.map +1 -0
  214. package/packages/daemon/dist/orchestrator.d.ts +4 -56
  215. package/packages/daemon/dist/orchestrator.d.ts.map +1 -1
  216. package/packages/daemon/dist/orchestrator.js +13 -303
  217. package/packages/daemon/dist/orchestrator.js.map +1 -1
  218. package/packages/daemon/dist/orchestrator.test.js +11 -1
  219. package/packages/daemon/dist/orchestrator.test.js.map +1 -1
  220. package/packages/daemon/package.json +4 -4
  221. package/packages/gsd-agent-core/dist/export-html/template.js +53 -4
  222. package/packages/gsd-agent-core/package.json +5 -5
  223. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  224. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js +3 -2
  225. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  226. package/packages/gsd-agent-modes/package.json +7 -7
  227. package/packages/mcp-server/package.json +4 -4
  228. package/packages/native/package.json +1 -1
  229. package/packages/pi-agent-core/dist/agent-loop.d.ts.map +1 -1
  230. package/packages/pi-agent-core/dist/agent-loop.js +14 -9
  231. package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
  232. package/packages/pi-agent-core/package.json +1 -1
  233. package/packages/pi-ai/dist/index.d.ts +1 -0
  234. package/packages/pi-ai/dist/index.d.ts.map +1 -1
  235. package/packages/pi-ai/dist/index.js +1 -0
  236. package/packages/pi-ai/dist/index.js.map +1 -1
  237. package/packages/pi-ai/dist/providers/transform-messages.d.ts.map +1 -1
  238. package/packages/pi-ai/dist/providers/transform-messages.js +7 -3
  239. package/packages/pi-ai/dist/providers/transform-messages.js.map +1 -1
  240. package/packages/pi-ai/dist/tool-result-content.d.ts +3 -0
  241. package/packages/pi-ai/dist/tool-result-content.d.ts.map +1 -0
  242. package/packages/pi-ai/dist/tool-result-content.js +62 -0
  243. package/packages/pi-ai/dist/tool-result-content.js.map +1 -0
  244. package/packages/pi-ai/dist/types.d.ts +8 -20
  245. package/packages/pi-ai/dist/types.d.ts.map +1 -1
  246. package/packages/pi-ai/dist/types.js.map +1 -1
  247. package/packages/pi-ai/package.json +1 -1
  248. package/packages/pi-coding-agent/dist/core/package-manager.d.ts +9 -0
  249. package/packages/pi-coding-agent/dist/core/package-manager.d.ts.map +1 -1
  250. package/packages/pi-coding-agent/dist/core/package-manager.js +74 -53
  251. package/packages/pi-coding-agent/dist/core/package-manager.js.map +1 -1
  252. package/packages/pi-coding-agent/dist/core/resource-loader.d.ts +6 -0
  253. package/packages/pi-coding-agent/dist/core/resource-loader.d.ts.map +1 -1
  254. package/packages/pi-coding-agent/dist/core/resource-loader.js +30 -2
  255. package/packages/pi-coding-agent/dist/core/resource-loader.js.map +1 -1
  256. package/packages/pi-coding-agent/dist/core/skill-directories.d.ts +66 -0
  257. package/packages/pi-coding-agent/dist/core/skill-directories.d.ts.map +1 -0
  258. package/packages/pi-coding-agent/dist/core/skill-directories.js +138 -0
  259. package/packages/pi-coding-agent/dist/core/skill-directories.js.map +1 -0
  260. package/packages/pi-coding-agent/dist/core/skills.d.ts +4 -1
  261. package/packages/pi-coding-agent/dist/core/skills.d.ts.map +1 -1
  262. package/packages/pi-coding-agent/dist/core/skills.js +4 -1
  263. package/packages/pi-coding-agent/dist/core/skills.js.map +1 -1
  264. package/packages/pi-coding-agent/dist/index.d.ts +1 -0
  265. package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
  266. package/packages/pi-coding-agent/dist/index.js +1 -0
  267. package/packages/pi-coding-agent/dist/index.js.map +1 -1
  268. package/packages/pi-coding-agent/package.json +7 -7
  269. package/packages/pi-tui/package.json +2 -2
  270. package/packages/rpc-client/package.json +2 -2
  271. package/pkg/dist/core/export-html/template.js +53 -4
  272. package/pkg/package.json +1 -1
  273. package/src/resources/extensions/ask-user-questions.ts +1 -1
  274. package/src/resources/extensions/browser-tools/engine/managed-gsd-browser.ts +146 -33
  275. package/src/resources/extensions/browser-tools/tests/gsd-browser-launch-config.test.mjs +25 -0
  276. package/src/resources/extensions/browser-tools/tests/managed-gsd-browser-tools.test.mjs +93 -0
  277. package/src/resources/extensions/gsd/activity-log.ts +1 -1
  278. package/src/resources/extensions/gsd/auto/closeout.ts +114 -7
  279. package/src/resources/extensions/gsd/auto/orchestrator.ts +21 -14
  280. package/src/resources/extensions/gsd/auto/pre-dispatch.ts +6 -3
  281. package/src/resources/extensions/gsd/auto/unit-phase.ts +5 -9
  282. package/src/resources/extensions/gsd/auto-artifact-paths.ts +14 -4
  283. package/src/resources/extensions/gsd/auto-direct-dispatch.ts +13 -7
  284. package/src/resources/extensions/gsd/auto-dispatch.ts +77 -16
  285. package/src/resources/extensions/gsd/auto-model-selection.ts +33 -1
  286. package/src/resources/extensions/gsd/auto-post-unit.ts +4 -2
  287. package/src/resources/extensions/gsd/auto-prompts.ts +53 -113
  288. package/src/resources/extensions/gsd/auto-recovery.ts +50 -6
  289. package/src/resources/extensions/gsd/auto-start.ts +62 -31
  290. package/src/resources/extensions/gsd/auto-timeout-recovery.ts +12 -2
  291. package/src/resources/extensions/gsd/auto-timers.ts +9 -2
  292. package/src/resources/extensions/gsd/auto-worktree-branch-lifecycle.ts +237 -0
  293. package/src/resources/extensions/gsd/auto-worktree-cleanup.ts +166 -0
  294. package/src/resources/extensions/gsd/auto-worktree-conflict-auto-resolve.ts +37 -0
  295. package/src/resources/extensions/gsd/auto-worktree-creation.ts +171 -0
  296. package/src/resources/extensions/gsd/auto-worktree-entry.ts +154 -0
  297. package/src/resources/extensions/gsd/auto-worktree-merge-already-merged.ts +161 -0
  298. package/src/resources/extensions/gsd/auto-worktree-merge-branch-head.ts +83 -0
  299. package/src/resources/extensions/gsd/auto-worktree-merge-cleanup.ts +132 -0
  300. package/src/resources/extensions/gsd/auto-worktree-merge-code-changes.ts +71 -0
  301. package/src/resources/extensions/gsd/auto-worktree-merge-db-ready.ts +112 -0
  302. package/src/resources/extensions/gsd/auto-worktree-merge-integration-branch.ts +99 -0
  303. package/src/resources/extensions/gsd/auto-worktree-merge-message.ts +166 -0
  304. package/src/resources/extensions/gsd/auto-worktree-merge-pre-teardown.ts +93 -0
  305. package/src/resources/extensions/gsd/auto-worktree-merge-stash.ts +213 -0
  306. package/src/resources/extensions/gsd/auto-worktree-merge.ts +391 -0
  307. package/src/resources/extensions/gsd/auto-worktree-milestone-shelter.ts +159 -0
  308. package/src/resources/extensions/gsd/auto-worktree-path-resolution.ts +6 -0
  309. package/src/resources/extensions/gsd/auto-worktree-path-utils.ts +10 -0
  310. package/src/resources/extensions/gsd/auto-worktree-resource-version.ts +44 -0
  311. package/src/resources/extensions/gsd/auto-worktree-runtime-cleanup.ts +119 -0
  312. package/src/resources/extensions/gsd/auto-worktree-session-registry.ts +71 -0
  313. package/src/resources/extensions/gsd/auto-worktree-sync.ts +370 -0
  314. package/src/resources/extensions/gsd/auto-worktree-teardown.ts +161 -0
  315. package/src/resources/extensions/gsd/auto-worktree.ts +69 -2429
  316. package/src/resources/extensions/gsd/auto.ts +114 -47
  317. package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +1 -1
  318. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +20 -2
  319. package/src/resources/extensions/gsd/bootstrap/system-context.ts +1 -1
  320. package/src/resources/extensions/gsd/bootstrap/tool-call-loop-guard.ts +218 -27
  321. package/src/resources/extensions/gsd/codebase-generator.ts +7 -1
  322. package/src/resources/extensions/gsd/commands-handlers.ts +1 -1
  323. package/src/resources/extensions/gsd/commands-workflow-templates.ts +3 -3
  324. package/src/resources/extensions/gsd/compat/compat-marker.ts +47 -0
  325. package/src/resources/extensions/gsd/config-overlay.ts +7 -1
  326. package/src/resources/extensions/gsd/db/engine.ts +36 -0
  327. package/src/resources/extensions/gsd/db/queries.ts +9 -9
  328. package/src/resources/extensions/gsd/db/writers/cascades.ts +23 -1
  329. package/src/resources/extensions/gsd/db-adapter.ts +29 -3
  330. package/src/resources/extensions/gsd/docs/preferences-reference.md +3 -3
  331. package/src/resources/extensions/gsd/doctor-engine-checks.ts +109 -10
  332. package/src/resources/extensions/gsd/flat-phase-migration.ts +34 -3
  333. package/src/resources/extensions/gsd/forensics.ts +1 -1
  334. package/src/resources/extensions/gsd/git-conflict-resolve.ts +2 -2
  335. package/src/resources/extensions/gsd/git-service.ts +53 -0
  336. package/src/resources/extensions/gsd/gsd-db.ts +33 -11
  337. package/src/resources/extensions/gsd/guided-flow.ts +26 -18
  338. package/src/resources/extensions/gsd/journal.ts +1 -1
  339. package/src/resources/extensions/gsd/markdown-renderer.ts +52 -91
  340. package/src/resources/extensions/gsd/md-importer.ts +93 -22
  341. package/src/resources/extensions/gsd/metrics.ts +2 -2
  342. package/src/resources/extensions/gsd/migration-auto-check.ts +32 -3
  343. package/src/resources/extensions/gsd/milestone-merge-transaction.ts +15 -0
  344. package/src/resources/extensions/gsd/milestone-settlement.ts +1 -1
  345. package/src/resources/extensions/gsd/parallel-merge.ts +1 -1
  346. package/src/resources/extensions/gsd/parallel-orchestrator.ts +3 -1
  347. package/src/resources/extensions/gsd/paths.ts +108 -51
  348. package/src/resources/extensions/gsd/post-unit-hooks.ts +32 -0
  349. package/src/resources/extensions/gsd/preferences-models.ts +74 -20
  350. package/src/resources/extensions/gsd/preferences-skills.ts +10 -16
  351. package/src/resources/extensions/gsd/preferences-types.ts +54 -1
  352. package/src/resources/extensions/gsd/preferences-validation.ts +146 -9
  353. package/src/resources/extensions/gsd/preferences.ts +20 -0
  354. package/src/resources/extensions/gsd/prompts/system.md +6 -6
  355. package/src/resources/extensions/gsd/quick.ts +11 -3
  356. package/src/resources/extensions/gsd/rule-registry.ts +111 -9
  357. package/src/resources/extensions/gsd/skill-discovery.ts +47 -23
  358. package/src/resources/extensions/gsd/slice-parallel-orchestrator.ts +3 -1
  359. package/src/resources/extensions/gsd/state/derive/from-db.ts +201 -157
  360. package/src/resources/extensions/gsd/state-reconciliation/drift/artifact-db.ts +1 -9
  361. package/src/resources/extensions/gsd/state-reconciliation/drift/completion.ts +2 -22
  362. package/src/resources/extensions/gsd/state-reconciliation/index.ts +14 -0
  363. package/src/resources/extensions/gsd/tests/auto-artifact-paths.test.ts +169 -1
  364. package/src/resources/extensions/gsd/tests/auto-loop-no-copy-artifacts.test.ts +2 -1
  365. package/src/resources/extensions/gsd/tests/auto-orchestrator.test.ts +41 -2
  366. package/src/resources/extensions/gsd/tests/auto-post-unit-artifact-diagnostic.test.ts +48 -1
  367. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +57 -0
  368. package/src/resources/extensions/gsd/tests/auto-start-clean-runtime-db-gated.test.ts +1 -1
  369. package/src/resources/extensions/gsd/tests/auto-start-project-milestone-reconcile.test.ts +29 -1
  370. package/src/resources/extensions/gsd/tests/auto-timeout-recovery-supervisor-inflight-guard.test.ts +159 -0
  371. package/src/resources/extensions/gsd/tests/auto-warning-noise-regression.test.ts +2 -1
  372. package/src/resources/extensions/gsd/tests/auto-worktree-auto-resolve.test.ts +1 -1
  373. package/src/resources/extensions/gsd/tests/auto-worktree-merge-already-merged.test.ts +177 -0
  374. package/src/resources/extensions/gsd/tests/auto-worktree-merge-branch-head.test.ts +118 -0
  375. package/src/resources/extensions/gsd/tests/auto-worktree-merge-cleanup.test.ts +115 -0
  376. package/src/resources/extensions/gsd/tests/auto-worktree-merge-code-changes.test.ts +76 -0
  377. package/src/resources/extensions/gsd/tests/auto-worktree-merge-db-ready.test.ts +140 -0
  378. package/src/resources/extensions/gsd/tests/auto-worktree-merge-integration-branch.test.ts +123 -0
  379. package/src/resources/extensions/gsd/tests/auto-worktree-merge-message.test.ts +114 -0
  380. package/src/resources/extensions/gsd/tests/auto-worktree-merge-pre-teardown.test.ts +84 -0
  381. package/src/resources/extensions/gsd/tests/auto-worktree-merge-stash.test.ts +70 -0
  382. package/src/resources/extensions/gsd/tests/auto-worktree-milestone-shelter.test.ts +52 -0
  383. package/src/resources/extensions/gsd/tests/auto-worktree-registry.test.ts +6 -6
  384. package/src/resources/extensions/gsd/tests/auto-worktree-resource-version.test.ts +59 -0
  385. package/src/resources/extensions/gsd/tests/auto-worktree-untracked-content.test.ts +3 -1
  386. package/src/resources/extensions/gsd/tests/codebase-generator.test.ts +22 -0
  387. package/src/resources/extensions/gsd/tests/commands-verdict.test.ts +3 -1
  388. package/src/resources/extensions/gsd/tests/compat-marker.test.ts +85 -0
  389. package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +45 -0
  390. package/src/resources/extensions/gsd/tests/completed-units-metrics-sync.test.ts +1 -1
  391. package/src/resources/extensions/gsd/tests/db-adapter.test.ts +39 -0
  392. package/src/resources/extensions/gsd/tests/db-writable-probe.test.ts +66 -0
  393. package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +4 -0
  394. package/src/resources/extensions/gsd/tests/doctor-scope-db-unavailable.test.ts +174 -0
  395. package/src/resources/extensions/gsd/tests/fast-forward-reused-milestone-branch.test.ts +1 -1
  396. package/src/resources/extensions/gsd/tests/flat-phase-migration.test.ts +71 -1
  397. package/src/resources/extensions/gsd/tests/flat-phase-renderer.test.ts +46 -1
  398. package/src/resources/extensions/gsd/tests/flat-phase-validation-path.test.ts +72 -32
  399. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +40 -0
  400. package/src/resources/extensions/gsd/tests/import-completion-lost-update.test.ts +196 -0
  401. package/src/resources/extensions/gsd/tests/integration/all-milestones-complete-merge.test.ts +4 -6
  402. package/src/resources/extensions/gsd/tests/integration/auto-stash-merge.test.ts +2 -1
  403. package/src/resources/extensions/gsd/tests/integration/auto-worktree-milestone-merge.test.ts +3 -3
  404. package/src/resources/extensions/gsd/tests/integration/auto-worktree.test.ts +7 -8
  405. package/src/resources/extensions/gsd/tests/integration/feature-branch-lifecycle-integration.test.ts +2 -5
  406. package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +76 -0
  407. package/src/resources/extensions/gsd/tests/integration/merge-cwd-restore.test.ts +1 -1
  408. package/src/resources/extensions/gsd/tests/integration/merge-preserve-worktree-on-preteardown-error.test.ts +1 -1
  409. package/src/resources/extensions/gsd/tests/integration/merge-strategy-regular.test.ts +2 -1
  410. package/src/resources/extensions/gsd/tests/integration/milestone-transition-worktree.test.ts +5 -7
  411. package/src/resources/extensions/gsd/tests/integration/paths.test.ts +88 -1
  412. package/src/resources/extensions/gsd/tests/integration/workspace-collapse-integration.test.ts +2 -4
  413. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +16 -15
  414. package/src/resources/extensions/gsd/tests/merge-closeout-consistency-gate.test.ts +1 -1
  415. package/src/resources/extensions/gsd/tests/merge-db-cycle.test.ts +1 -1
  416. package/src/resources/extensions/gsd/tests/merge-self-branch-guard.test.ts +1 -1
  417. package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +91 -0
  418. package/src/resources/extensions/gsd/tests/milestone-closeout.test.ts +2 -1
  419. package/src/resources/extensions/gsd/tests/milestone-merge-stash-restore.test.ts +439 -0
  420. package/src/resources/extensions/gsd/tests/milestone-merge-transaction.test.ts +24 -0
  421. package/src/resources/extensions/gsd/tests/none-mode-gates.test.ts +1 -1
  422. package/src/resources/extensions/gsd/tests/originalbase-path-comparison.test.ts +3 -3
  423. package/src/resources/extensions/gsd/tests/parallel-worker-lock-contention.test.ts +1 -1
  424. package/src/resources/extensions/gsd/tests/parsers-legacy-importers.test.ts +0 -1
  425. package/src/resources/extensions/gsd/tests/post-unit-hooks.test.ts +269 -0
  426. package/src/resources/extensions/gsd/tests/post-unit-retry-on-orchestrator-bridge.test.ts +8 -1
  427. package/src/resources/extensions/gsd/tests/preferences-worktree-sync.test.ts +1 -4
  428. package/src/resources/extensions/gsd/tests/preferences.test.ts +19 -0
  429. package/src/resources/extensions/gsd/tests/prefs-wizard-coverage.test.ts +5 -0
  430. package/src/resources/extensions/gsd/tests/read-uat-gate-verdict.test.ts +54 -0
  431. package/src/resources/extensions/gsd/tests/reassess-handler.test.ts +49 -1
  432. package/src/resources/extensions/gsd/tests/reopen-slice.test.ts +52 -0
  433. package/src/resources/extensions/gsd/tests/rule-registry.test.ts +95 -1
  434. package/src/resources/extensions/gsd/tests/runtime-invariant-modules.test.ts +20 -0
  435. package/src/resources/extensions/gsd/tests/sibling-model-fallbacks.test.ts +189 -0
  436. package/src/resources/extensions/gsd/tests/single-field-model-thinking.test.ts +156 -0
  437. package/src/resources/extensions/gsd/tests/skill-discovery.test.ts +46 -0
  438. package/src/resources/extensions/gsd/tests/skipped-validation-db-atomicity.test.ts +6 -1
  439. package/src/resources/extensions/gsd/tests/stale-worktree-cwd.test.ts +3 -5
  440. package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +32 -0
  441. package/src/resources/extensions/gsd/tests/stash-pop-gsd-conflict.test.ts +2 -1
  442. package/src/resources/extensions/gsd/tests/stash-queued-context-files.test.ts +2 -1
  443. package/src/resources/extensions/gsd/tests/steer-worktree-path.test.ts +1 -1
  444. package/src/resources/extensions/gsd/tests/supervisor-model-swap-inflight-guard.test.ts +181 -0
  445. package/src/resources/extensions/gsd/tests/supervisor-model-swap-thinking-clamp.test.ts +122 -0
  446. package/src/resources/extensions/gsd/tests/sync-worktree-skip-current.test.ts +1 -1
  447. package/src/resources/extensions/gsd/tests/system-flat-phase-layout.test.ts +33 -0
  448. package/src/resources/extensions/gsd/tests/teardown-chdir-failure-clears-registry.test.ts +4 -4
  449. package/src/resources/extensions/gsd/tests/teardown-cleanup-parity.test.ts +2 -1
  450. package/src/resources/extensions/gsd/tests/teardown-failure-clears-registry.test.ts +4 -4
  451. package/src/resources/extensions/gsd/tests/tool-call-loop-guard.test.ts +196 -0
  452. package/src/resources/extensions/gsd/tests/uat-dispatch.test.ts +211 -0
  453. package/src/resources/extensions/gsd/tests/uat-stuck-loop-orphaned-worktree.test.ts +1 -1
  454. package/src/resources/extensions/gsd/tests/unit-registry.test.ts +26 -1
  455. package/src/resources/extensions/gsd/tests/uok-gate-runner.test.ts +39 -0
  456. package/src/resources/extensions/gsd/tests/uok-kernel-path.test.ts +226 -2
  457. package/src/resources/extensions/gsd/tests/uok-model-policy.test.ts +6 -0
  458. package/src/resources/extensions/gsd/tests/workflow-projections.test.ts +213 -2
  459. package/src/resources/extensions/gsd/tests/worktree-db-integration.test.ts +1 -1
  460. package/src/resources/extensions/gsd/tests/worktree-db-respawn-truncation.test.ts +1 -1
  461. package/src/resources/extensions/gsd/tests/worktree-db-same-file.test.ts +1 -3
  462. package/src/resources/extensions/gsd/tests/worktree-expected-warnings.test.ts +1 -1
  463. package/src/resources/extensions/gsd/tests/worktree-git-pathspec.test.ts +1 -1
  464. package/src/resources/extensions/gsd/tests/worktree-main-branch.test.ts +1 -1
  465. package/src/resources/extensions/gsd/tests/worktree-preferences-sync.test.ts +1 -1
  466. package/src/resources/extensions/gsd/tests/worktree-reentry.test.ts +2 -1
  467. package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +5 -2
  468. package/src/resources/extensions/gsd/tests/worktree-sync-overwrite-loop.test.ts +1 -1
  469. package/src/resources/extensions/gsd/tests/worktree-sync-tasks.test.ts +1 -1
  470. package/src/resources/extensions/gsd/tool-contract.ts +29 -0
  471. package/src/resources/extensions/gsd/tools/complete-milestone.ts +9 -5
  472. package/src/resources/extensions/gsd/tools/reassess-roadmap.ts +19 -8
  473. package/src/resources/extensions/gsd/tools/reopen-slice.ts +5 -0
  474. package/src/resources/extensions/gsd/tools/reopen-task.ts +13 -5
  475. package/src/resources/extensions/gsd/tools/validate-milestone.ts +13 -9
  476. package/src/resources/extensions/gsd/types.ts +23 -4
  477. package/src/resources/extensions/gsd/uat-dispatch.ts +176 -0
  478. package/src/resources/extensions/gsd/unit-registry.ts +16 -2
  479. package/src/resources/extensions/gsd/unmerged-milestone-guard.ts +1 -1
  480. package/src/resources/extensions/gsd/uok/audit-toggle.ts +32 -2
  481. package/src/resources/extensions/gsd/uok/audit.ts +2 -0
  482. package/src/resources/extensions/gsd/uok/gate-runner.ts +26 -36
  483. package/src/resources/extensions/gsd/uok/gitops.ts +21 -18
  484. package/src/resources/extensions/gsd/uok/kernel.ts +169 -48
  485. package/src/resources/extensions/gsd/uok/model-policy.ts +20 -17
  486. package/src/resources/extensions/gsd/validation-block-guard.ts +2 -1
  487. package/src/resources/extensions/gsd/workflow-event-ledger.ts +1 -1
  488. package/src/resources/extensions/gsd/workflow-logger.ts +1 -1
  489. package/src/resources/extensions/gsd/workflow-projections.ts +9 -24
  490. package/src/resources/extensions/gsd/workspace-git-preflight.ts +2 -2
  491. package/src/resources/extensions/gsd/workspace.ts +5 -21
  492. package/src/resources/extensions/gsd/worktree-command.ts +1 -1
  493. package/src/resources/extensions/gsd/worktree-lifecycle.ts +19 -15
  494. package/src/resources/extensions/gsd/worktree-reentry.ts +2 -1
  495. package/src/resources/extensions/shared/gsd-browser-cli.ts +29 -0
  496. package/src/resources/extensions/shared/interview-ui.ts +58 -11
  497. package/src/resources/extensions/shared/tests/interview-ui-border.test.ts +32 -0
  498. /package/dist/web/standalone/.next/static/{bCg3aYrFN1DKZr2dqgE7L → 6xYMOzxVhsQgryneJ5KRc}/_buildManifest.js +0 -0
  499. /package/dist/web/standalone/.next/static/{bCg3aYrFN1DKZr2dqgE7L → 6xYMOzxVhsQgryneJ5KRc}/_ssgManifest.js +0 -0
@@ -98,6 +98,50 @@ export function writeCompatMarker(basePath, marker) {
98
98
  writeFileSync(tmp, JSON.stringify(marker, null, 2), "utf-8");
99
99
  renameSync(tmp, path);
100
100
  }
101
+ /**
102
+ * Remove projection entries whose backing file no longer exists on disk.
103
+ *
104
+ * gsd-pi never deletes marker entries on its own: both drift detectors skip
105
+ * files missing from disk (`if (!existsSync(abs)) continue;`) and the write-time
106
+ * flush only ever adds or refreshes entries. So when a phase directory is
107
+ * renamed or removed (e.g. `phases/29-new-milestone-m029/` →
108
+ * `phases/29-frontend-code-debt-cleanup/`), the old projection paths linger in
109
+ * `.compat.json` forever as phantom entries pointing at directories that no
110
+ * longer exist (#1257). They never reconcile, and `.compat.json` keeps drifting
111
+ * from disk reality so `git status` stops being a reliable proxy for "did the
112
+ * engine touch my plans?".
113
+ *
114
+ * This prunes those orphaned entries across the `.gsd/` projections and the
115
+ * `.planning/` projections/passthrough maps. It is safe: a missing-file entry is
116
+ * inert (every detector already ignores it), and if the file is later
117
+ * re-projected the write-time flush / unseeded-file detection re-seeds an
118
+ * accurate baseline.
119
+ *
120
+ * Returns the number of entries removed; when nothing is orphaned the marker is
121
+ * left untouched (no needless write, no `lastProjectedAt` churn).
122
+ */
123
+ export function pruneOrphanedProjectionEntries(basePath) {
124
+ if (!existsSync(compatMarkerPath(basePath)))
125
+ return 0;
126
+ const marker = readCompatMarker(basePath);
127
+ let removed = 0;
128
+ const pruneMap = (map, root) => {
129
+ for (const relPath of Object.keys(map)) {
130
+ if (!existsSync(join(basePath, root, relPath))) {
131
+ delete map[relPath];
132
+ removed++;
133
+ }
134
+ }
135
+ };
136
+ pruneMap(marker.projections, ".gsd");
137
+ if (marker.planning) {
138
+ pruneMap(marker.planning.projections, ".planning");
139
+ pruneMap(marker.planning.passthrough, ".planning");
140
+ }
141
+ if (removed > 0)
142
+ writeCompatMarker(basePath, marker);
143
+ return removed;
144
+ }
101
145
  function quarantine(basePath, raw) {
102
146
  const path = compatMarkerPath(basePath);
103
147
  const badPath = `${path}.bad-${Date.now()}`;
@@ -121,8 +121,13 @@ function collectConfigSections(options) {
121
121
  if (prefs?.auto_supervisor) {
122
122
  const sup = resolveAutoSupervisorConfig();
123
123
  const supRows = [];
124
- if (sup.model)
125
- supRows.push({ label: "Model", value: sup.model });
124
+ if (sup.model) {
125
+ let modelVal = sup.model;
126
+ if (sup.modelFallbacks && sup.modelFallbacks.length > 0) {
127
+ modelVal += ` \u2192 ${sup.modelFallbacks.join(" \u2192 ")}`;
128
+ }
129
+ supRows.push({ label: "Model", value: modelVal });
130
+ }
126
131
  supRows.push({ label: "Soft timeout", value: `${sup.soft_timeout_minutes}m` });
127
132
  supRows.push({ label: "Idle timeout", value: `${sup.idle_timeout_minutes}m` });
128
133
  supRows.push({ label: "Stalled tool timeout", value: `${sup.stalled_tool_timeout_minutes}m` });
@@ -801,3 +801,36 @@ export function getDb() {
801
801
  export function getDbOrNull() {
802
802
  return currentDb;
803
803
  }
804
+ /**
805
+ * Confirm the open handle can actually write, not just that it opened.
806
+ *
807
+ * A schema-current database performs zero writes during open, so a handle that
808
+ * opened successfully but is not writable (read-only file, WAL/SHM permission
809
+ * mismatch, or a stale/moved handle → SQLITE_READONLY_DBMOVED) otherwise passes
810
+ * the open-only availability check and only fails much later at the first real
811
+ * write. The probe forces a genuine page write by re-writing the current
812
+ * `PRAGMA user_version` value back inside an IMMEDIATE transaction: a bare
813
+ * `BEGIN IMMEDIATE; ROLLBACK` is not sufficient because a moved handle does not
814
+ * fail until a page is actually dirtied. The value is unchanged, so the probe
815
+ * is idempotent, and the transaction is rolled back so nothing persists.
816
+ */
817
+ export function probeDbWritable() {
818
+ const db = currentDb;
819
+ if (!db)
820
+ return { ok: false, detail: "No database is open." };
821
+ try {
822
+ const row = db.prepare("PRAGMA user_version").get();
823
+ const current = typeof row?.user_version === "number" ? row.user_version : 0;
824
+ db.exec("BEGIN IMMEDIATE");
825
+ try {
826
+ db.exec(`PRAGMA user_version = ${current}`);
827
+ }
828
+ finally {
829
+ db.exec("ROLLBACK");
830
+ }
831
+ return { ok: true };
832
+ }
833
+ catch (err) {
834
+ return { ok: false, detail: err.message };
835
+ }
836
+ }
@@ -218,7 +218,7 @@ export function getMilestone(id) {
218
218
  export function getActiveMilestoneFromDb() {
219
219
  if (!getDbOrNull())
220
220
  return null;
221
- const row = getDbOrNull().prepare("SELECT * FROM milestones WHERE status NOT IN ('complete', 'done', 'skipped', 'closed', 'parked') ORDER BY id LIMIT 1").get();
221
+ const row = getDbOrNull().prepare(`SELECT * FROM milestones WHERE status NOT IN (${TERMINAL_STATUS_SQL}, 'parked') ORDER BY id LIMIT 1`).get();
222
222
  if (!row)
223
223
  return null;
224
224
  return rowToMilestone(row);
@@ -230,11 +230,11 @@ export function getActiveSliceFromDb(milestoneId) {
230
230
  // Uses json_each() to expand the JSON depends array and checks each dep is complete.
231
231
  const row = getDbOrNull().prepare(`SELECT s.* FROM slices s
232
232
  WHERE s.milestone_id = :mid
233
- AND s.status NOT IN ('complete', 'done', 'skipped')
233
+ AND s.status NOT IN (${TERMINAL_STATUS_SQL})
234
234
  AND NOT EXISTS (
235
235
  SELECT 1 FROM json_each(s.depends) AS dep
236
236
  WHERE dep.value NOT IN (
237
- SELECT id FROM slices WHERE milestone_id = :mid AND status IN ('complete', 'done', 'skipped')
237
+ SELECT id FROM slices WHERE milestone_id = :mid AND status IN (${TERMINAL_STATUS_SQL})
238
238
  )
239
239
  )
240
240
  ORDER BY s.sequence, s.id
@@ -246,7 +246,7 @@ export function getActiveSliceFromDb(milestoneId) {
246
246
  export function getActiveTaskFromDb(milestoneId, sliceId) {
247
247
  if (!getDbOrNull())
248
248
  return null;
249
- const row = getDbOrNull().prepare("SELECT * FROM tasks WHERE milestone_id = :mid AND slice_id = :sid AND status NOT IN ('complete', 'done') ORDER BY sequence, id LIMIT 1").get({ ":mid": milestoneId, ":sid": sliceId });
249
+ const row = getDbOrNull().prepare(`SELECT * FROM tasks WHERE milestone_id = :mid AND slice_id = :sid AND status NOT IN (${TERMINAL_STATUS_SQL}) ORDER BY sequence, id LIMIT 1`).get({ ":mid": milestoneId, ":sid": sliceId });
250
250
  if (!row)
251
251
  return null;
252
252
  return rowToTask(row);
@@ -265,7 +265,7 @@ export function getParallelMonitorSliceProgress(milestoneId) {
265
265
  s.id AS id,
266
266
  s.status AS status,
267
267
  COUNT(t.id) AS total,
268
- COALESCE(SUM(CASE WHEN t.status='complete' THEN 1 ELSE 0 END), 0) AS done
268
+ COALESCE(SUM(CASE WHEN t.status IN (${TERMINAL_STATUS_SQL}) THEN 1 ELSE 0 END), 0) AS done
269
269
  FROM slices s
270
270
  LEFT JOIN tasks t ON s.milestone_id=t.milestone_id AND s.id=t.slice_id
271
271
  WHERE s.milestone_id=:mid
@@ -417,7 +417,7 @@ export function getSliceScopedArtifacts(milestoneId, sliceId) {
417
417
  export function getActiveMilestoneIdFromDb() {
418
418
  if (!getDbOrNull())
419
419
  return null;
420
- const row = getDbOrNull().prepare("SELECT id, status FROM milestones WHERE status NOT IN ('complete', 'done', 'skipped', 'closed', 'parked') ORDER BY id LIMIT 1").get();
420
+ const row = getDbOrNull().prepare(`SELECT id, status FROM milestones WHERE status NOT IN (${TERMINAL_STATUS_SQL}, 'parked') ORDER BY id LIMIT 1`).get();
421
421
  if (!row)
422
422
  return null;
423
423
  return rowToIdStatusSummary(row);
@@ -432,7 +432,7 @@ export function getSliceStatusSummary(milestoneId) {
432
432
  export function getActiveTaskIdFromDb(milestoneId, sliceId) {
433
433
  if (!getDbOrNull())
434
434
  return null;
435
- const row = getDbOrNull().prepare("SELECT id, status, title FROM tasks WHERE milestone_id = :mid AND slice_id = :sid AND status NOT IN ('complete', 'done') ORDER BY sequence, id LIMIT 1").get({ ":mid": milestoneId, ":sid": sliceId });
435
+ const row = getDbOrNull().prepare(`SELECT id, status, title FROM tasks WHERE milestone_id = :mid AND slice_id = :sid AND status NOT IN (${TERMINAL_STATUS_SQL}) ORDER BY sequence, id LIMIT 1`).get({ ":mid": milestoneId, ":sid": sliceId });
436
436
  if (!row)
437
437
  return null;
438
438
  return rowToActiveTaskSummary(row);
@@ -443,8 +443,8 @@ export function getSliceTaskCounts(milestoneId, sliceId) {
443
443
  return emptyTaskStatusCounts();
444
444
  const row = getDbOrNull().prepare(`SELECT
445
445
  COUNT(*) as total,
446
- SUM(CASE WHEN status IN ('complete', 'done') THEN 1 ELSE 0 END) as done,
447
- SUM(CASE WHEN status NOT IN ('complete', 'done') THEN 1 ELSE 0 END) as pending
446
+ SUM(CASE WHEN status IN (${TERMINAL_STATUS_SQL}) THEN 1 ELSE 0 END) as done,
447
+ SUM(CASE WHEN status NOT IN (${TERMINAL_STATUS_SQL}) THEN 1 ELSE 0 END) as pending
448
448
  FROM tasks WHERE milestone_id = :mid AND slice_id = :sid`).get({ ":mid": milestoneId, ":sid": sliceId });
449
449
  return rowToTaskStatusCounts(row);
450
450
  }
@@ -24,6 +24,17 @@ function requireDb() {
24
24
  * completion timestamps cleared, in one commit. Folds the hand-rolled
25
25
  * transaction-plus-cascade previously in tools/reopen-slice.ts. Guards run
26
26
  * inside the transaction (TOCTOU-safe).
27
+ *
28
+ * A closed slice is always reopenable. An open slice is only reopenable when it
29
+ * carries a completed-task state desync (#1205): a UAT→planning fallback can
30
+ * leave the slice "pending" while *all* its tasks stay "complete", which
31
+ * deadlocks every recovery path (plan-slice/replan-slice/complete-task all
32
+ * reject the still-closed tasks, and slice_reopen historically rejected the
33
+ * still-open slice). Resetting those tasks to pending gives the planner the
34
+ * clean slate it needs. An open slice that still has any unfinished
35
+ * (pending/in-progress) task is a normal in-flight slice, not the desync, and
36
+ * stays rejected as "slice-not-complete" so a full reset can't wipe its
37
+ * legitimate progress.
27
38
  */
28
39
  export function reopenSliceCascade(milestoneId, sliceId) {
29
40
  requireDb();
@@ -43,11 +54,18 @@ export function reopenSliceCascade(milestoneId, sliceId) {
43
54
  outcome = { ok: false, reason: "slice-not-found" };
44
55
  return;
45
56
  }
46
- if (!isClosedStatus(slice.status)) {
57
+ const tasks = getSliceTasks(milestoneId, sliceId);
58
+ // A closed slice is always reopenable. An open slice is only the #1205
59
+ // desync when EVERY task is already closed (a UAT→planning fallback left the
60
+ // slice open with all tasks still "complete"). Requiring *all* tasks closed
61
+ // (not merely one) means a normal in-flight slice — which keeps at least one
62
+ // pending/in-progress task — is rejected, so a full reset can't wipe its
63
+ // legitimate progress.
64
+ const allTasksClosed = tasks.length > 0 && tasks.every((t) => isClosedStatus(t.status));
65
+ if (!isClosedStatus(slice.status) && !allTasksClosed) {
47
66
  outcome = { ok: false, reason: "slice-not-complete", status: slice.status };
48
67
  return;
49
68
  }
50
- const tasks = getSliceTasks(milestoneId, sliceId);
51
69
  getDbOrNull().prepare(`UPDATE slices SET status = 'in_progress', completed_at = NULL WHERE milestone_id = :mid AND id = :sid`).run({ ":mid": milestoneId, ":sid": sliceId });
52
70
  getDbOrNull().prepare(`UPDATE tasks SET status = 'pending', completed_at = NULL WHERE milestone_id = :mid AND slice_id = :sid`).run({ ":mid": milestoneId, ":sid": sliceId });
53
71
  outcome = { ok: true, tasksReset: tasks.length };
@@ -11,19 +11,46 @@ export function normalizeDbRow(row) {
11
11
  export function normalizeDbRows(rows) {
12
12
  return rows.map((row) => normalizeDbRow(row));
13
13
  }
14
+ function isPlainObject(value) {
15
+ if (value === null || typeof value !== "object")
16
+ return false;
17
+ const proto = Object.getPrototypeOf(value);
18
+ return proto === Object.prototype || proto === null;
19
+ }
20
+ // node:sqlite accepts binding-object keys with or without the ':'/'@'/'$'
21
+ // prefix, but better-sqlite3 accepts only bare keys — strip the prefix so
22
+ // call sites written against node:sqlite also work on the fallback provider.
23
+ function normalizeBindParams(params) {
24
+ return params.map((param) => {
25
+ if (!isPlainObject(param))
26
+ return param;
27
+ let changed = false;
28
+ const normalized = {};
29
+ for (const [key, value] of Object.entries(param)) {
30
+ if (key.length > 1 && (key[0] === ":" || key[0] === "@" || key[0] === "$")) {
31
+ normalized[key.slice(1)] = value;
32
+ changed = true;
33
+ }
34
+ else {
35
+ normalized[key] = value;
36
+ }
37
+ }
38
+ return changed ? normalized : param;
39
+ });
40
+ }
14
41
  export function createDbAdapter(rawDb) {
15
42
  const db = rawDb;
16
43
  const stmtCache = new Map();
17
44
  function wrapStmt(raw) {
18
45
  return {
19
46
  run(...params) {
20
- return raw.run(...params);
47
+ return raw.run(...normalizeBindParams(params));
21
48
  },
22
49
  get(...params) {
23
- return normalizeDbRow(raw.get(...params));
50
+ return normalizeDbRow(raw.get(...normalizeBindParams(params)));
24
51
  },
25
52
  all(...params) {
26
- return normalizeDbRows(raw.all(...params));
53
+ return normalizeDbRows(raw.all(...normalizeBindParams(params)));
27
54
  },
28
55
  };
29
56
  }
@@ -138,7 +138,7 @@ Diagnostics record the file path, scope (global/project), severity (error/warnin
138
138
  - `off` — skill discovery is disabled entirely.
139
139
 
140
140
  - `auto_supervisor`: configures the auto-mode supervisor that monitors agent progress and enforces timeouts. Keys:
141
- - `model`: model ID to use for the supervisor process (defaults to the currently active model).
141
+ - `model`: model to use for the supervisor process (defaults to the currently active model). Accepts a bare model ID **or** the same `{ model, provider?, fallbacks? }` object form as `models.<phase>`, so it honors `fallbacks[]` on a transient provider trip.
142
142
  - `soft_timeout_minutes`: minutes before the supervisor issues a soft warning (default: 20).
143
143
  - `idle_timeout_minutes`: minutes of inactivity before the supervisor intervenes (default: 10).
144
144
  - `hard_timeout_minutes`: minutes before the supervisor forces termination (default: 30).
@@ -228,7 +228,7 @@ In `"parent"` mode, slice/task `targetRepositories` default to the declared chil
228
228
  - `enabled`: boolean — set `false` to force sequential task execution. Default: `true`.
229
229
  - `max_parallel`: number — maximum tasks to dispatch in one batch, range `1`-`8`. Default: `2`.
230
230
  - `isolation_mode`: `"same-tree"` — currently the only supported value.
231
- - `subagent_model`: string — optional model override for reactive task subagents. Falls back to the `models.subagent` routing when omitted.
231
+ - `subagent_model`: optional model override for reactive task subagents. Accepts a bare model ID **or** the `{ model, provider?, fallbacks? }` object form (parity with `models.<phase>`). Falls back to the `models.subagent` routing when omitted.
232
232
 
233
233
  - `remote_questions`: route interactive questions to Slack/Discord for headless auto-mode. Keys:
234
234
  - `channel`: `"slack"` or `"discord"` — channel type.
@@ -326,7 +326,7 @@ In `"parent"` mode, slice/task `targetRepositories` default to the declared chil
326
326
  - `after`: string[] — unit types that trigger this hook (e.g., `["execute-task"]`).
327
327
  - `prompt`: string — prompt sent to the LLM. Supports `{milestoneId}`, `{sliceId}`, `{taskId}` substitutions.
328
328
  - `max_cycles`: number — max times this hook fires per trigger (default: 1, max: 10).
329
- - `model`: string — optional model override.
329
+ - `model`: optional model override. Accepts a bare model ID **or** the same `{ model, provider?, fallbacks? }` object form as `models.<phase>`. With `fallbacks[]`, a transient trip of the primary provider falls back to the next entry instead of hard-failing the hook — which, for a `blocking` hook with `on_block: { action: pause }`, would otherwise pause the whole run.
330
330
  - `artifact`: string — expected output file name (relative to task/slice dir). Hook is skipped if file already exists (idempotent).
331
331
  - `criticality`: `"advisory"` or `"blocking"` — advisory preserves current best-effort behavior; blocking requires clean hook completion plus a valid outcome verdict before auto-mode advances. Default: `"advisory"`.
332
332
  - `retry_on`: string — if this file is produced instead of the artifact, re-run the trigger unit then re-run hooks.
@@ -3,7 +3,7 @@ import { isAbsolute, join, relative, sep } from "node:path";
3
3
  import { deleteArtifactByPath, getAllMilestones, getMilestoneSlices, getSliceTasks, isDbAvailable, isMemoriesFtsAvailable, repairTaskCompletionFromSummary, _getAdapter, } from "./gsd-db.js";
4
4
  import { MEMORIES_FTS_REBUILT_KEY } from "./db-memory-fts-schema.js";
5
5
  import { isAfter, latestExplicitReopenAt } from "./milestone-reopen-events.js";
6
- import { gsdProjectionRoot, gsdRoot, resolveGsdPathContract, resolveMilestoneFile, resolveSliceFile } from "./paths.js";
6
+ import { buildTaskFileName, gsdProjectionRoot, gsdRoot, resolveGsdPathContract, resolveMilestoneFile, resolveMilestonePath, resolveSliceFile, } from "./paths.js";
7
7
  import { deriveState } from "./state.js";
8
8
  import { isClosedStatus } from "./status-guards.js";
9
9
  import { workflowEventLogPath } from "./workflow-event-ledger.js";
@@ -12,6 +12,7 @@ import { flushWorkflowProjections } from "./projection-flush.js";
12
12
  import { parseRoadmapSlices } from "./roadmap-slices.js";
13
13
  import { parsePlan } from "./parsers-legacy.js";
14
14
  import { parseSummary } from "./files.js";
15
+ import { LAYOUT_SEGMENTS } from "./layout-policy.js";
15
16
  const USER_AUTHORED_ARTIFACT_TYPES = new Set(["CONTEXT", "RESEARCH"]);
16
17
  function escapeRegExp(value) {
17
18
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
@@ -113,10 +114,10 @@ function isClearedByMilestoneShellProjectionFlush(basePath, issue, reRenderedMil
113
114
  return false;
114
115
  return issue.file === relativeFile(basePath, roadmapPath);
115
116
  }
116
- function artifactExistsOnDisk(basePath, artifactPath) {
117
- return resolveArtifactDiskPath(basePath, artifactPath) !== null;
117
+ function artifactExistsOnDisk(basePath, artifactPath, row) {
118
+ return resolveArtifactDiskPath(basePath, artifactPath, row) !== null;
118
119
  }
119
- function resolveArtifactDiskPath(basePath, artifactPath) {
120
+ function resolveLiteralArtifactDiskPath(basePath, artifactPath) {
120
121
  const relativeArtifactPath = artifactPathRelativeToGsd(artifactPath);
121
122
  if (isAbsolute(relativeArtifactPath)) {
122
123
  return existsSync(relativeArtifactPath) ? relativeArtifactPath : null;
@@ -128,6 +129,48 @@ function resolveArtifactDiskPath(basePath, artifactPath) {
128
129
  }
129
130
  return null;
130
131
  }
132
+ function relativeToGsdRoot(basePath, diskPath) {
133
+ for (const root of [gsdProjectionRoot(basePath), gsdRoot(basePath)]) {
134
+ if (!isPathInside(root, diskPath))
135
+ continue;
136
+ return relative(root, diskPath).split("\\").join("/");
137
+ }
138
+ return null;
139
+ }
140
+ function isFlatPhaseDiskPath(basePath, diskPath) {
141
+ return relativeToGsdRoot(basePath, diskPath)?.startsWith(`${LAYOUT_SEGMENTS.level1}/`) ?? false;
142
+ }
143
+ function hasFlatPhaseLayout(basePath) {
144
+ return existsSync(join(gsdProjectionRoot(basePath), LAYOUT_SEGMENTS.level1));
145
+ }
146
+ function resolveFlatPhaseArtifactDiskPath(basePath, row) {
147
+ if (!hasFlatPhaseLayout(basePath))
148
+ return null;
149
+ if (!row.milestone_id)
150
+ return null;
151
+ const artifactType = normalizedArtifactType(row.artifact_type);
152
+ if (!artifactType)
153
+ return null;
154
+ if (row.slice_id && row.task_id) {
155
+ const phaseDir = resolveMilestonePath(basePath, row.milestone_id);
156
+ if (!phaseDir || !isFlatPhaseDiskPath(basePath, phaseDir))
157
+ return null;
158
+ const candidate = join(phaseDir, buildTaskFileName(row.task_id, artifactType));
159
+ return existsSync(candidate) ? candidate : null;
160
+ }
161
+ const candidate = row.slice_id
162
+ ? resolveSliceFile(basePath, row.milestone_id, row.slice_id, artifactType)
163
+ : resolveMilestoneFile(basePath, row.milestone_id, artifactType);
164
+ return candidate && isFlatPhaseDiskPath(basePath, candidate) ? candidate : null;
165
+ }
166
+ function resolveArtifactDiskPath(basePath, artifactPath, row) {
167
+ if (row && isMilestonesArtifactPath(row.path)) {
168
+ const flatPath = resolveFlatPhaseArtifactDiskPath(basePath, row);
169
+ if (flatPath)
170
+ return flatPath;
171
+ }
172
+ return resolveLiteralArtifactDiskPath(basePath, artifactPath);
173
+ }
131
174
  function taskExistsInPlan(basePath, milestoneId, sliceId, taskId) {
132
175
  const planPath = resolveSliceFile(basePath, milestoneId, sliceId, "PLAN");
133
176
  if (!planPath || !existsSync(planPath))
@@ -156,7 +199,7 @@ function readTaskCompletionEvidenceFromSummary(basePath, row) {
156
199
  if (!row.task_status && Number(row.task_count) > 0 && !taskExistsInPlan(basePath, row.milestone_id, row.slice_id, row.task_id)) {
157
200
  return null;
158
201
  }
159
- const diskPath = resolveArtifactDiskPath(basePath, row.path);
202
+ const diskPath = resolveArtifactDiskPath(basePath, row.path, { ...row, artifact_type: "SUMMARY" });
160
203
  if (!diskPath)
161
204
  return null;
162
205
  try {
@@ -256,6 +299,43 @@ function hasPresentMilestonesReplacement(basePath, row, artifactRows) {
256
299
  sameArtifactIdentity(row, other) &&
257
300
  artifactExistsOnDisk(basePath, other.path));
258
301
  }
302
+ function hasPresentFlatPhaseReplacement(basePath, row, artifactRows) {
303
+ if (resolveFlatPhaseArtifactDiskPath(basePath, row))
304
+ return true;
305
+ return artifactRows.some((other) => other.path !== row.path &&
306
+ !isMilestonesArtifactPath(other.path) &&
307
+ sameArtifactIdentity(row, other) &&
308
+ artifactExistsOnDisk(basePath, other.path, other));
309
+ }
310
+ function hasNoFlatPhaseFileEquivalent(row) {
311
+ const artifactType = normalizedArtifactType(row.artifact_type);
312
+ if (row.slice_id && row.task_id) {
313
+ return artifactType === "PLAN" || artifactType === "SUMMARY";
314
+ }
315
+ return Boolean(row.slice_id && !row.task_id && artifactType === "SUMMARY");
316
+ }
317
+ function staleMilestonesArtifactRowFixable(basePath, row, artifactRows) {
318
+ if (!isMilestonesArtifactPath(row.path))
319
+ return false;
320
+ if (!hasFlatPhaseLayout(basePath))
321
+ return false;
322
+ if (hasPresentFlatPhaseReplacement(basePath, row, artifactRows))
323
+ return true;
324
+ return hasNoFlatPhaseFileEquivalent(row) && !resolveLiteralArtifactDiskPath(basePath, row.path);
325
+ }
326
+ function stalePhasesArtifactRowFixable(basePath, row, artifactRows) {
327
+ const issuePath = artifactPathRelativeToGsd(row.path);
328
+ return issuePath.startsWith(`${LAYOUT_SEGMENTS.level1}/`) && hasPresentMilestonesReplacement(basePath, row, artifactRows);
329
+ }
330
+ function staleArtifactRowFixable(basePath, row, artifactRows) {
331
+ return staleMilestonesArtifactRowFixable(basePath, row, artifactRows) ||
332
+ stalePhasesArtifactRowFixable(basePath, row, artifactRows);
333
+ }
334
+ function staleArtifactPruneMessage(row) {
335
+ return isMilestonesArtifactPath(row.path)
336
+ ? `pruned stale legacy artifact row ${row.path}`
337
+ : `pruned stale flat-phase artifact row ${row.path}`;
338
+ }
259
339
  export async function checkEngineHealth(basePath, issues, fixesApplied, options) {
260
340
  const dbPath = resolveGsdPathContract(basePath).projectDb;
261
341
  if (!isDbAvailable() && existsSync(dbPath)) {
@@ -456,18 +536,20 @@ export async function checkEngineHealth(basePath, issues, fixesApplied, options)
456
536
  ORDER BY path`)
457
537
  .all();
458
538
  for (const row of artifactRows) {
459
- if (artifactExistsOnDisk(basePath, row.path))
460
- continue;
461
539
  const unitId = artifactUnitId(row);
462
540
  const issuePath = artifactPathRelativeToGsd(row.path);
463
- if (options?.repair && issuePath.startsWith("phases/") && hasPresentMilestonesReplacement(basePath, row, artifactRows)) {
541
+ if (artifactExistsOnDisk(basePath, row.path))
542
+ continue;
543
+ if (options?.repair && staleArtifactRowFixable(basePath, row, artifactRows)) {
464
544
  // Route the write through the Single Writer owner (gsd-db.ts) instead
465
545
  // of issuing raw DELETE SQL here — doctor is a read-only consumer and
466
546
  // the single-writer invariant forbids write SQL outside the allowlist.
467
547
  deleteArtifactByPath(row.path);
468
- fixesApplied.push(`pruned stale flat-phase artifact row ${row.path}`);
548
+ fixesApplied.push(staleArtifactPruneMessage(row));
469
549
  continue;
470
550
  }
551
+ if (artifactExistsOnDisk(basePath, row.path, row))
552
+ continue;
471
553
  if (isUserAuthoredArtifactType(row.artifact_type)) {
472
554
  const artifactType = normalizedArtifactType(row.artifact_type);
473
555
  missingUserContentArtifacts.push({ path: issuePath, artifactType });
@@ -489,7 +571,7 @@ export async function checkEngineHealth(basePath, issues, fixesApplied, options)
489
571
  unitId,
490
572
  message: `Artifact ${issuePath} is recorded in the database as ${row.artifact_type || "UNKNOWN"} but no matching file exists on disk`,
491
573
  file: issuePath,
492
- fixable: issuePath.startsWith("phases/") && hasPresentMilestonesReplacement(basePath, row, artifactRows),
574
+ fixable: staleArtifactRowFixable(basePath, row, artifactRows),
493
575
  });
494
576
  }
495
577
  }
@@ -518,7 +600,7 @@ export async function checkEngineHealth(basePath, issues, fixesApplied, options)
518
600
  .all();
519
601
  const seen = new Set();
520
602
  for (const row of rows) {
521
- if (!artifactExistsOnDisk(basePath, row.path))
603
+ if (!artifactExistsOnDisk(basePath, row.path, row))
522
604
  continue;
523
605
  const reopenAt = latestExplicitReopenAt(basePath, row.milestone_id);
524
606
  if (!isAfter(row.imported_at, reopenAt))
@@ -4,7 +4,7 @@
4
4
  import { cpSync, existsSync, mkdirSync, readdirSync, renameSync, rmSync, statSync } from "node:fs";
5
5
  import { join } from "node:path";
6
6
  import { renderAllFromDb, renderRoadmapFromDb } from "./markdown-renderer.js";
7
- import { getAllMilestones, getMilestoneSlices } from "./gsd-db.js";
7
+ import { deleteArtifactsByPathPrefix, getAllMilestones, getMilestoneSlices } from "./gsd-db.js";
8
8
  import { migrateFromMarkdown } from "./md-importer.js";
9
9
  import { countDbHierarchy } from "./migration-auto-check.js";
10
10
  import { logWarning } from "./workflow-logger.js";
@@ -78,15 +78,33 @@ function rollbackPartialMigration(basePath, backupDir, migratingPath) {
78
78
  catch (removeErr) {
79
79
  logWarning("migration", `rollback: could not remove partial ${LAYOUT_SEGMENTS.level1}/: ${removeErr.message}`);
80
80
  }
81
+ // Only the standalone .gsd-backups/migrate-<ts>/ copy is disposable once the
82
+ // restore succeeds. When resuming an interrupted migration, backupDir IS the
83
+ // preserved migrating tree (the sole surviving data source) and must never be
84
+ // removed here. Deleting the backup after a successful rollback keeps the gate
85
+ // from leaking one .gsd-backups/migrate-<ts>/ per session_start.
86
+ const isDisposableBackup = Boolean(backupDir) && backupDir !== migratingPath;
87
+ const cleanupBackup = () => {
88
+ if (!isDisposableBackup || !existsSync(backupDir))
89
+ return;
90
+ try {
91
+ removePathWithRetries(backupDir);
92
+ }
93
+ catch (cleanupErr) {
94
+ logWarning("migration", `rollback: could not clean backup ${backupDir}: ${cleanupErr.message}`);
95
+ }
96
+ };
81
97
  // Restore milestones/ — prefer renaming the preserved migrating copy back.
82
98
  const milestonesPath = join(basePath, ".gsd", "milestones");
83
99
  try {
84
100
  if (migratingPath && existsSync(migratingPath) && !existsSync(milestonesPath)) {
85
101
  movePathWithCopyDeleteFallback(migratingPath, milestonesPath);
102
+ cleanupBackup();
86
103
  return;
87
104
  }
88
105
  if (existsSync(backupDir)) {
89
106
  cpSync(backupDir, milestonesPath, { recursive: true });
107
+ cleanupBackup();
90
108
  }
91
109
  if (migratingPath && existsSync(migratingPath)) {
92
110
  removePathWithRetries(migratingPath);
@@ -163,7 +181,8 @@ export function pruneStaleFlatPhaseBackups(basePath) {
163
181
  * 2. Rename .gsd/milestones/ aside so path resolvers target phases/
164
182
  * 3. Render flat-phase from the DB (which already has the data)
165
183
  * 4. Verify counts match
166
- * 5. Remove the renamed legacy tree
184
+ * 5. Prune legacy milestones/ artifact rows
185
+ * 6. Remove the renamed legacy tree
167
186
  *
168
187
  * Idempotent: if .gsd/milestones/ doesn't exist, returns immediately.
169
188
  */
@@ -262,7 +281,17 @@ export async function migrateToFlatPhase(basePath) {
262
281
  rollbackPartialMigration(basePath, backupDir, migratingPath);
263
282
  throw new Error("flat-phase migration verification failed: no artifacts rendered");
264
283
  }
265
- // 6. Verified — remove the renamed legacy tree (backup already on disk).
284
+ // 6. Verified — prune legacy artifact rows now that renderAllFromDb has
285
+ // re-inserted flat-phase rows for artifacts that still have files.
286
+ try {
287
+ deleteArtifactsByPathPrefix("milestones/");
288
+ }
289
+ catch (err) {
290
+ logWarning("migration", `flat-phase migration could not prune legacy artifact rows: ${err.message}`);
291
+ rollbackPartialMigration(basePath, backupDir, migratingPath);
292
+ throw err;
293
+ }
294
+ // 7. Remove the renamed legacy tree (backup already on disk).
266
295
  try {
267
296
  removePathWithRetries(migratingPath);
268
297
  }
@@ -24,7 +24,7 @@ import { loadPrompt } from "./prompt-loader.js";
24
24
  import { gsdRoot } from "./paths.js";
25
25
  import { isDbAvailable, getHierarchyCompletionCounts } from "./gsd-db.js";
26
26
  import { formatDuration } from "../shared/format-utils.js";
27
- import { getAutoWorktreePath } from "./auto-worktree.js";
27
+ import { getAutoWorktreePath } from "./auto-worktree-path-resolution.js";
28
28
  import { clearGSDPreferencesCache, loadEffectiveGSDPreferences, loadGlobalGSDPreferences, getGlobalGSDPreferencesPath } from "./preferences.js";
29
29
  import { showNextAction } from "../shared/tui.js";
30
30
  import { isInteractiveCommandContext, notifyForensicsNeedsInteractiveMenu, } from "./command-feedback.js";
@@ -2,8 +2,8 @@
2
2
  // File Purpose: Shared auto-resolution of safe merge conflict paths.
3
3
  import { logWarning } from "./workflow-logger.js";
4
4
  import { nativeAddPaths, nativeCheckoutTheirs, nativeRmForce, } from "./native-git-bridge.js";
5
- import { isSafeToAutoResolve } from "./auto-worktree.js";
6
- export { isSafeToAutoResolve } from "./auto-worktree.js";
5
+ import { isSafeToAutoResolve } from "./auto-worktree-conflict-auto-resolve.js";
6
+ export { isSafeToAutoResolve } from "./auto-worktree-conflict-auto-resolve.js";
7
7
  /**
8
8
  * Auto-resolve paths that are safe to accept from the milestone side
9
9
  * (.gsd/ state and build artifacts).
@@ -519,6 +519,51 @@ export function runGit(basePath, args, options = {}) {
519
519
  throw new GSDError(GSD_GIT_ERROR, `git ${args.join(" ")} failed in ${basePath}: ${filterGitSvnNoise(message)}`);
520
520
  }
521
521
  }
522
+ /**
523
+ * Arguments for creating a task/workflow branch.
524
+ *
525
+ * When the current branch is a leftover GSD task branch (workflow template,
526
+ * quick task, or slice), the new branch starts from `baseBranch` — the
527
+ * caller's resolved integration branch, normally `GitServiceImpl.getMainBranch()`
528
+ * so `main_branch` preference, milestone integration branch, and worktree base
529
+ * all take priority over repo-default detection. Branching from HEAD there
530
+ * silently stacks consecutive workflow branches on top of each other, so every
531
+ * new gsd/* branch (and its eventual PR) drags along the previous workflow's
532
+ * commits.
533
+ *
534
+ * On any other branch (a user's feature branch, a worktree base, the main
535
+ * branch itself) the legacy branch-from-HEAD behavior is kept: basing new work
536
+ * on the branch the user chose to stand on is intentional there.
537
+ *
538
+ * Falls back to branch-from-HEAD, with a logged warning, when the base branch
539
+ * has no local ref or the working tree is still dirty (a start point that
540
+ * changes the tree would abort the checkout or strand in-flight changes).
541
+ * The base is the local ref — deliberately no fetch, so branch creation stays
542
+ * offline-safe even if the local base is behind its remote.
543
+ */
544
+ export function taskBranchArgs(basePath, branchName, currentBranch, baseBranch) {
545
+ const args = ["checkout", "-b", branchName];
546
+ const onTaskBranch = WORKFLOW_BRANCH_RE.test(currentBranch) ||
547
+ QUICK_BRANCH_RE.test(currentBranch) ||
548
+ SLICE_BRANCH_RE.test(currentBranch);
549
+ if (!onTaskBranch || !baseBranch || baseBranch === currentBranch)
550
+ return args;
551
+ try {
552
+ if (!nativeBranchExists(basePath, baseBranch)) {
553
+ logWarning("engine", `taskBranchArgs: base branch "${baseBranch}" has no local ref — branching ${branchName} from HEAD (${currentBranch})`, { file: "git-service.ts" });
554
+ return args;
555
+ }
556
+ if (nativeHasChanges(basePath)) {
557
+ logWarning("engine", `taskBranchArgs: working tree still dirty after auto-commit — branching ${branchName} from HEAD (${currentBranch}) to preserve in-flight changes`, { file: "git-service.ts" });
558
+ return args;
559
+ }
560
+ }
561
+ catch {
562
+ return args;
563
+ }
564
+ args.push(baseBranch);
565
+ return args;
566
+ }
522
567
  // ─── Commit Type Inference ─────────────────────────────────────────────────
523
568
  /**
524
569
  * Keyword-to-commit-type mapping. Order matters — first match wins.