@opengsd/gsd-pi 1.4.0-dev.c18009cd → 1.5.0-dev.4fb2d460

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 (334) 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/gsd/auto/closeout.js +97 -5
  5. package/dist/resources/extensions/gsd/auto/orchestrator.js +4 -3
  6. package/dist/resources/extensions/gsd/auto/pre-dispatch.js +6 -3
  7. package/dist/resources/extensions/gsd/auto/unit-phase.js +5 -9
  8. package/dist/resources/extensions/gsd/auto-artifact-paths.js +12 -3
  9. package/dist/resources/extensions/gsd/auto-direct-dispatch.js +11 -7
  10. package/dist/resources/extensions/gsd/auto-dispatch.js +16 -2
  11. package/dist/resources/extensions/gsd/auto-recovery.js +50 -6
  12. package/dist/resources/extensions/gsd/auto-start.js +4 -3
  13. package/dist/resources/extensions/gsd/auto-worktree-branch-lifecycle.js +189 -0
  14. package/dist/resources/extensions/gsd/auto-worktree-cleanup.js +133 -0
  15. package/dist/resources/extensions/gsd/auto-worktree-conflict-auto-resolve.js +35 -0
  16. package/dist/resources/extensions/gsd/auto-worktree-creation.js +130 -0
  17. package/dist/resources/extensions/gsd/auto-worktree-entry.js +118 -0
  18. package/dist/resources/extensions/gsd/auto-worktree-merge-already-merged.js +98 -0
  19. package/dist/resources/extensions/gsd/auto-worktree-merge-branch-head.js +52 -0
  20. package/dist/resources/extensions/gsd/auto-worktree-merge-cleanup.js +87 -0
  21. package/dist/resources/extensions/gsd/auto-worktree-merge-code-changes.js +46 -0
  22. package/dist/resources/extensions/gsd/auto-worktree-merge-db-ready.js +68 -0
  23. package/dist/resources/extensions/gsd/auto-worktree-merge-integration-branch.js +51 -0
  24. package/dist/resources/extensions/gsd/auto-worktree-merge-message.js +106 -0
  25. package/dist/resources/extensions/gsd/auto-worktree-merge-pre-teardown.js +64 -0
  26. package/dist/resources/extensions/gsd/auto-worktree-merge-stash.js +168 -0
  27. package/dist/resources/extensions/gsd/auto-worktree-merge.js +302 -0
  28. package/dist/resources/extensions/gsd/auto-worktree-milestone-shelter.js +119 -0
  29. package/dist/resources/extensions/gsd/auto-worktree-path-resolution.js +5 -0
  30. package/dist/resources/extensions/gsd/auto-worktree-path-utils.js +5 -0
  31. package/dist/resources/extensions/gsd/auto-worktree-resource-version.js +41 -0
  32. package/dist/resources/extensions/gsd/auto-worktree-runtime-cleanup.js +99 -0
  33. package/dist/resources/extensions/gsd/auto-worktree-session-registry.js +57 -0
  34. package/dist/resources/extensions/gsd/auto-worktree-sync.js +249 -0
  35. package/dist/resources/extensions/gsd/auto-worktree-teardown.js +120 -0
  36. package/dist/resources/extensions/gsd/auto-worktree.js +20 -2073
  37. package/dist/resources/extensions/gsd/auto.js +9 -3
  38. package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +1 -1
  39. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +20 -2
  40. package/dist/resources/extensions/gsd/bootstrap/system-context.js +1 -1
  41. package/dist/resources/extensions/gsd/bootstrap/tool-call-loop-guard.js +185 -27
  42. package/dist/resources/extensions/gsd/codebase-generator.js +7 -1
  43. package/dist/resources/extensions/gsd/commands-handlers.js +1 -1
  44. package/dist/resources/extensions/gsd/commands-workflow-templates.js +3 -3
  45. package/dist/resources/extensions/gsd/db/queries.js +9 -9
  46. package/dist/resources/extensions/gsd/db/writers/cascades.js +20 -2
  47. package/dist/resources/extensions/gsd/doctor-engine-checks.js +93 -11
  48. package/dist/resources/extensions/gsd/flat-phase-migration.js +32 -3
  49. package/dist/resources/extensions/gsd/forensics.js +1 -1
  50. package/dist/resources/extensions/gsd/git-conflict-resolve.js +2 -2
  51. package/dist/resources/extensions/gsd/git-service.js +45 -0
  52. package/dist/resources/extensions/gsd/gsd-db.js +23 -11
  53. package/dist/resources/extensions/gsd/guided-flow.js +22 -17
  54. package/dist/resources/extensions/gsd/markdown-renderer.js +15 -3
  55. package/dist/resources/extensions/gsd/md-importer.js +89 -23
  56. package/dist/resources/extensions/gsd/migration-auto-check.js +30 -3
  57. package/dist/resources/extensions/gsd/milestone-merge-transaction.js +11 -0
  58. package/dist/resources/extensions/gsd/milestone-settlement.js +1 -1
  59. package/dist/resources/extensions/gsd/parallel-merge.js +1 -1
  60. package/dist/resources/extensions/gsd/parallel-orchestrator.js +3 -1
  61. package/dist/resources/extensions/gsd/paths.js +17 -3
  62. package/dist/resources/extensions/gsd/preferences-skills.js +10 -16
  63. package/dist/resources/extensions/gsd/preferences-types.js +1 -0
  64. package/dist/resources/extensions/gsd/preferences-validation.js +82 -0
  65. package/dist/resources/extensions/gsd/preferences.js +20 -0
  66. package/dist/resources/extensions/gsd/prompts/system.md +6 -6
  67. package/dist/resources/extensions/gsd/quick.js +12 -3
  68. package/dist/resources/extensions/gsd/skill-discovery.js +36 -23
  69. package/dist/resources/extensions/gsd/slice-parallel-orchestrator.js +2 -1
  70. package/dist/resources/extensions/gsd/state/derive/from-db.js +88 -153
  71. package/dist/resources/extensions/gsd/tool-contract.js +14 -0
  72. package/dist/resources/extensions/gsd/tools/reopen-slice.js +5 -0
  73. package/dist/resources/extensions/gsd/tools/reopen-task.js +13 -5
  74. package/dist/resources/extensions/gsd/unit-registry.js +12 -2
  75. package/dist/resources/extensions/gsd/unmerged-milestone-guard.js +1 -1
  76. package/dist/resources/extensions/gsd/validation-block-guard.js +2 -1
  77. package/dist/resources/extensions/gsd/workspace-git-preflight.js +2 -2
  78. package/dist/resources/extensions/gsd/worktree-command.js +1 -1
  79. package/dist/resources/extensions/gsd/worktree-lifecycle.js +14 -9
  80. package/dist/resources/extensions/gsd/worktree-reentry.js +2 -1
  81. package/dist/resources/extensions/shared/interview-ui.js +62 -9
  82. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  83. package/dist/web/standalone/.next/BUILD_ID +1 -1
  84. package/dist/web/standalone/.next/app-path-routes-manifest.json +8 -8
  85. package/dist/web/standalone/.next/build-manifest.json +2 -2
  86. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  87. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  88. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  89. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  90. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  91. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  92. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  93. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  94. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  95. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  96. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  97. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  98. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  99. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  100. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  101. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  102. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  103. package/dist/web/standalone/.next/server/app/index.html +1 -1
  104. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  105. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  106. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  107. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  108. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  109. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  110. package/dist/web/standalone/.next/server/app-paths-manifest.json +8 -8
  111. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  112. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  113. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  114. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  115. package/dist/web/standalone/node_modules/@gsd/native/package.json +1 -1
  116. package/dist/web/standalone/node_modules/node-pty/build/Makefile +1 -1
  117. package/integrations/hermes/open_gsd_hermes/gsd_client.py +24 -35
  118. package/integrations/hermes/tests/test_gsd_client_timeout.py +4 -4
  119. package/package.json +6 -6
  120. package/packages/cloud-mcp-gateway/package.json +2 -2
  121. package/packages/contracts/package.json +1 -1
  122. package/packages/daemon/package.json +4 -4
  123. package/packages/gsd-agent-core/dist/export-html/template.js +53 -4
  124. package/packages/gsd-agent-core/package.json +5 -5
  125. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  126. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js +3 -2
  127. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  128. package/packages/gsd-agent-modes/package.json +7 -7
  129. package/packages/mcp-server/package.json +4 -4
  130. package/packages/native/package.json +1 -1
  131. package/packages/pi-agent-core/dist/agent-loop.d.ts.map +1 -1
  132. package/packages/pi-agent-core/dist/agent-loop.js +14 -9
  133. package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
  134. package/packages/pi-agent-core/package.json +1 -1
  135. package/packages/pi-ai/dist/index.d.ts +1 -0
  136. package/packages/pi-ai/dist/index.d.ts.map +1 -1
  137. package/packages/pi-ai/dist/index.js +1 -0
  138. package/packages/pi-ai/dist/index.js.map +1 -1
  139. package/packages/pi-ai/dist/providers/transform-messages.d.ts.map +1 -1
  140. package/packages/pi-ai/dist/providers/transform-messages.js +7 -3
  141. package/packages/pi-ai/dist/providers/transform-messages.js.map +1 -1
  142. package/packages/pi-ai/dist/tool-result-content.d.ts +3 -0
  143. package/packages/pi-ai/dist/tool-result-content.d.ts.map +1 -0
  144. package/packages/pi-ai/dist/tool-result-content.js +62 -0
  145. package/packages/pi-ai/dist/tool-result-content.js.map +1 -0
  146. package/packages/pi-ai/dist/types.d.ts +8 -20
  147. package/packages/pi-ai/dist/types.d.ts.map +1 -1
  148. package/packages/pi-ai/dist/types.js.map +1 -1
  149. package/packages/pi-ai/package.json +1 -1
  150. package/packages/pi-coding-agent/dist/core/package-manager.d.ts +9 -0
  151. package/packages/pi-coding-agent/dist/core/package-manager.d.ts.map +1 -1
  152. package/packages/pi-coding-agent/dist/core/package-manager.js +74 -53
  153. package/packages/pi-coding-agent/dist/core/package-manager.js.map +1 -1
  154. package/packages/pi-coding-agent/dist/core/resource-loader.d.ts +6 -0
  155. package/packages/pi-coding-agent/dist/core/resource-loader.d.ts.map +1 -1
  156. package/packages/pi-coding-agent/dist/core/resource-loader.js +30 -2
  157. package/packages/pi-coding-agent/dist/core/resource-loader.js.map +1 -1
  158. package/packages/pi-coding-agent/dist/core/skill-directories.d.ts +66 -0
  159. package/packages/pi-coding-agent/dist/core/skill-directories.d.ts.map +1 -0
  160. package/packages/pi-coding-agent/dist/core/skill-directories.js +138 -0
  161. package/packages/pi-coding-agent/dist/core/skill-directories.js.map +1 -0
  162. package/packages/pi-coding-agent/dist/core/skills.d.ts +4 -1
  163. package/packages/pi-coding-agent/dist/core/skills.d.ts.map +1 -1
  164. package/packages/pi-coding-agent/dist/core/skills.js +4 -1
  165. package/packages/pi-coding-agent/dist/core/skills.js.map +1 -1
  166. package/packages/pi-coding-agent/dist/index.d.ts +1 -0
  167. package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
  168. package/packages/pi-coding-agent/dist/index.js +1 -0
  169. package/packages/pi-coding-agent/dist/index.js.map +1 -1
  170. package/packages/pi-coding-agent/package.json +7 -7
  171. package/packages/pi-tui/package.json +2 -2
  172. package/packages/rpc-client/package.json +2 -2
  173. package/pkg/dist/core/export-html/template.js +53 -4
  174. package/pkg/package.json +1 -1
  175. package/src/resources/extensions/ask-user-questions.ts +1 -1
  176. package/src/resources/extensions/gsd/auto/closeout.ts +114 -7
  177. package/src/resources/extensions/gsd/auto/orchestrator.ts +4 -3
  178. package/src/resources/extensions/gsd/auto/pre-dispatch.ts +6 -3
  179. package/src/resources/extensions/gsd/auto/unit-phase.ts +5 -9
  180. package/src/resources/extensions/gsd/auto-artifact-paths.ts +12 -3
  181. package/src/resources/extensions/gsd/auto-direct-dispatch.ts +13 -7
  182. package/src/resources/extensions/gsd/auto-dispatch.ts +18 -2
  183. package/src/resources/extensions/gsd/auto-recovery.ts +50 -6
  184. package/src/resources/extensions/gsd/auto-start.ts +5 -3
  185. package/src/resources/extensions/gsd/auto-worktree-branch-lifecycle.ts +237 -0
  186. package/src/resources/extensions/gsd/auto-worktree-cleanup.ts +166 -0
  187. package/src/resources/extensions/gsd/auto-worktree-conflict-auto-resolve.ts +37 -0
  188. package/src/resources/extensions/gsd/auto-worktree-creation.ts +171 -0
  189. package/src/resources/extensions/gsd/auto-worktree-entry.ts +154 -0
  190. package/src/resources/extensions/gsd/auto-worktree-merge-already-merged.ts +161 -0
  191. package/src/resources/extensions/gsd/auto-worktree-merge-branch-head.ts +83 -0
  192. package/src/resources/extensions/gsd/auto-worktree-merge-cleanup.ts +132 -0
  193. package/src/resources/extensions/gsd/auto-worktree-merge-code-changes.ts +71 -0
  194. package/src/resources/extensions/gsd/auto-worktree-merge-db-ready.ts +112 -0
  195. package/src/resources/extensions/gsd/auto-worktree-merge-integration-branch.ts +99 -0
  196. package/src/resources/extensions/gsd/auto-worktree-merge-message.ts +166 -0
  197. package/src/resources/extensions/gsd/auto-worktree-merge-pre-teardown.ts +93 -0
  198. package/src/resources/extensions/gsd/auto-worktree-merge-stash.ts +213 -0
  199. package/src/resources/extensions/gsd/auto-worktree-merge.ts +391 -0
  200. package/src/resources/extensions/gsd/auto-worktree-milestone-shelter.ts +159 -0
  201. package/src/resources/extensions/gsd/auto-worktree-path-resolution.ts +6 -0
  202. package/src/resources/extensions/gsd/auto-worktree-path-utils.ts +10 -0
  203. package/src/resources/extensions/gsd/auto-worktree-resource-version.ts +44 -0
  204. package/src/resources/extensions/gsd/auto-worktree-runtime-cleanup.ts +119 -0
  205. package/src/resources/extensions/gsd/auto-worktree-session-registry.ts +71 -0
  206. package/src/resources/extensions/gsd/auto-worktree-sync.ts +370 -0
  207. package/src/resources/extensions/gsd/auto-worktree-teardown.ts +161 -0
  208. package/src/resources/extensions/gsd/auto-worktree.ts +69 -2429
  209. package/src/resources/extensions/gsd/auto.ts +11 -17
  210. package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +1 -1
  211. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +20 -2
  212. package/src/resources/extensions/gsd/bootstrap/system-context.ts +1 -1
  213. package/src/resources/extensions/gsd/bootstrap/tool-call-loop-guard.ts +218 -27
  214. package/src/resources/extensions/gsd/codebase-generator.ts +7 -1
  215. package/src/resources/extensions/gsd/commands-handlers.ts +1 -1
  216. package/src/resources/extensions/gsd/commands-workflow-templates.ts +3 -3
  217. package/src/resources/extensions/gsd/db/queries.ts +9 -9
  218. package/src/resources/extensions/gsd/db/writers/cascades.ts +23 -1
  219. package/src/resources/extensions/gsd/doctor-engine-checks.ts +109 -10
  220. package/src/resources/extensions/gsd/flat-phase-migration.ts +34 -3
  221. package/src/resources/extensions/gsd/forensics.ts +1 -1
  222. package/src/resources/extensions/gsd/git-conflict-resolve.ts +2 -2
  223. package/src/resources/extensions/gsd/git-service.ts +53 -0
  224. package/src/resources/extensions/gsd/gsd-db.ts +33 -11
  225. package/src/resources/extensions/gsd/guided-flow.ts +26 -18
  226. package/src/resources/extensions/gsd/markdown-renderer.ts +15 -3
  227. package/src/resources/extensions/gsd/md-importer.ts +93 -22
  228. package/src/resources/extensions/gsd/migration-auto-check.ts +32 -3
  229. package/src/resources/extensions/gsd/milestone-merge-transaction.ts +15 -0
  230. package/src/resources/extensions/gsd/milestone-settlement.ts +1 -1
  231. package/src/resources/extensions/gsd/parallel-merge.ts +1 -1
  232. package/src/resources/extensions/gsd/parallel-orchestrator.ts +3 -1
  233. package/src/resources/extensions/gsd/paths.ts +21 -3
  234. package/src/resources/extensions/gsd/preferences-skills.ts +10 -16
  235. package/src/resources/extensions/gsd/preferences-types.ts +30 -0
  236. package/src/resources/extensions/gsd/preferences-validation.ts +69 -0
  237. package/src/resources/extensions/gsd/preferences.ts +20 -0
  238. package/src/resources/extensions/gsd/prompts/system.md +6 -6
  239. package/src/resources/extensions/gsd/quick.ts +11 -3
  240. package/src/resources/extensions/gsd/skill-discovery.ts +47 -23
  241. package/src/resources/extensions/gsd/slice-parallel-orchestrator.ts +3 -1
  242. package/src/resources/extensions/gsd/state/derive/from-db.ts +201 -157
  243. package/src/resources/extensions/gsd/tests/auto-artifact-paths.test.ts +169 -1
  244. package/src/resources/extensions/gsd/tests/auto-loop-no-copy-artifacts.test.ts +2 -1
  245. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +57 -0
  246. package/src/resources/extensions/gsd/tests/auto-start-clean-runtime-db-gated.test.ts +1 -1
  247. package/src/resources/extensions/gsd/tests/auto-warning-noise-regression.test.ts +2 -1
  248. package/src/resources/extensions/gsd/tests/auto-worktree-auto-resolve.test.ts +1 -1
  249. package/src/resources/extensions/gsd/tests/auto-worktree-merge-already-merged.test.ts +177 -0
  250. package/src/resources/extensions/gsd/tests/auto-worktree-merge-branch-head.test.ts +118 -0
  251. package/src/resources/extensions/gsd/tests/auto-worktree-merge-cleanup.test.ts +115 -0
  252. package/src/resources/extensions/gsd/tests/auto-worktree-merge-code-changes.test.ts +76 -0
  253. package/src/resources/extensions/gsd/tests/auto-worktree-merge-db-ready.test.ts +140 -0
  254. package/src/resources/extensions/gsd/tests/auto-worktree-merge-integration-branch.test.ts +123 -0
  255. package/src/resources/extensions/gsd/tests/auto-worktree-merge-message.test.ts +114 -0
  256. package/src/resources/extensions/gsd/tests/auto-worktree-merge-pre-teardown.test.ts +84 -0
  257. package/src/resources/extensions/gsd/tests/auto-worktree-merge-stash.test.ts +70 -0
  258. package/src/resources/extensions/gsd/tests/auto-worktree-milestone-shelter.test.ts +52 -0
  259. package/src/resources/extensions/gsd/tests/auto-worktree-registry.test.ts +6 -6
  260. package/src/resources/extensions/gsd/tests/auto-worktree-resource-version.test.ts +59 -0
  261. package/src/resources/extensions/gsd/tests/auto-worktree-untracked-content.test.ts +3 -1
  262. package/src/resources/extensions/gsd/tests/codebase-generator.test.ts +22 -0
  263. package/src/resources/extensions/gsd/tests/completed-units-metrics-sync.test.ts +1 -1
  264. package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +4 -0
  265. package/src/resources/extensions/gsd/tests/doctor-scope-db-unavailable.test.ts +174 -0
  266. package/src/resources/extensions/gsd/tests/fast-forward-reused-milestone-branch.test.ts +1 -1
  267. package/src/resources/extensions/gsd/tests/flat-phase-migration.test.ts +71 -1
  268. package/src/resources/extensions/gsd/tests/flat-phase-renderer.test.ts +25 -1
  269. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +40 -0
  270. package/src/resources/extensions/gsd/tests/import-completion-lost-update.test.ts +196 -0
  271. package/src/resources/extensions/gsd/tests/integration/all-milestones-complete-merge.test.ts +4 -6
  272. package/src/resources/extensions/gsd/tests/integration/auto-stash-merge.test.ts +2 -1
  273. package/src/resources/extensions/gsd/tests/integration/auto-worktree-milestone-merge.test.ts +3 -3
  274. package/src/resources/extensions/gsd/tests/integration/auto-worktree.test.ts +7 -8
  275. package/src/resources/extensions/gsd/tests/integration/feature-branch-lifecycle-integration.test.ts +2 -5
  276. package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +76 -0
  277. package/src/resources/extensions/gsd/tests/integration/merge-cwd-restore.test.ts +1 -1
  278. package/src/resources/extensions/gsd/tests/integration/merge-preserve-worktree-on-preteardown-error.test.ts +1 -1
  279. package/src/resources/extensions/gsd/tests/integration/merge-strategy-regular.test.ts +2 -1
  280. package/src/resources/extensions/gsd/tests/integration/milestone-transition-worktree.test.ts +5 -7
  281. package/src/resources/extensions/gsd/tests/integration/workspace-collapse-integration.test.ts +2 -4
  282. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +16 -15
  283. package/src/resources/extensions/gsd/tests/merge-closeout-consistency-gate.test.ts +1 -1
  284. package/src/resources/extensions/gsd/tests/merge-db-cycle.test.ts +1 -1
  285. package/src/resources/extensions/gsd/tests/merge-self-branch-guard.test.ts +1 -1
  286. package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +91 -0
  287. package/src/resources/extensions/gsd/tests/milestone-merge-stash-restore.test.ts +439 -0
  288. package/src/resources/extensions/gsd/tests/milestone-merge-transaction.test.ts +24 -0
  289. package/src/resources/extensions/gsd/tests/none-mode-gates.test.ts +1 -1
  290. package/src/resources/extensions/gsd/tests/originalbase-path-comparison.test.ts +3 -3
  291. package/src/resources/extensions/gsd/tests/parallel-worker-lock-contention.test.ts +1 -1
  292. package/src/resources/extensions/gsd/tests/preferences-worktree-sync.test.ts +1 -4
  293. package/src/resources/extensions/gsd/tests/preferences.test.ts +19 -0
  294. package/src/resources/extensions/gsd/tests/prefs-wizard-coverage.test.ts +5 -0
  295. package/src/resources/extensions/gsd/tests/reopen-slice.test.ts +52 -0
  296. package/src/resources/extensions/gsd/tests/runtime-invariant-modules.test.ts +20 -0
  297. package/src/resources/extensions/gsd/tests/skill-discovery.test.ts +46 -0
  298. package/src/resources/extensions/gsd/tests/stale-worktree-cwd.test.ts +3 -5
  299. package/src/resources/extensions/gsd/tests/stash-pop-gsd-conflict.test.ts +2 -1
  300. package/src/resources/extensions/gsd/tests/stash-queued-context-files.test.ts +2 -1
  301. package/src/resources/extensions/gsd/tests/steer-worktree-path.test.ts +1 -1
  302. package/src/resources/extensions/gsd/tests/sync-worktree-skip-current.test.ts +1 -1
  303. package/src/resources/extensions/gsd/tests/system-flat-phase-layout.test.ts +33 -0
  304. package/src/resources/extensions/gsd/tests/teardown-chdir-failure-clears-registry.test.ts +4 -4
  305. package/src/resources/extensions/gsd/tests/teardown-cleanup-parity.test.ts +2 -1
  306. package/src/resources/extensions/gsd/tests/teardown-failure-clears-registry.test.ts +4 -4
  307. package/src/resources/extensions/gsd/tests/tool-call-loop-guard.test.ts +196 -0
  308. package/src/resources/extensions/gsd/tests/uat-stuck-loop-orphaned-worktree.test.ts +1 -1
  309. package/src/resources/extensions/gsd/tests/unit-registry.test.ts +26 -1
  310. package/src/resources/extensions/gsd/tests/worktree-db-integration.test.ts +1 -1
  311. package/src/resources/extensions/gsd/tests/worktree-db-respawn-truncation.test.ts +1 -1
  312. package/src/resources/extensions/gsd/tests/worktree-db-same-file.test.ts +1 -3
  313. package/src/resources/extensions/gsd/tests/worktree-expected-warnings.test.ts +1 -1
  314. package/src/resources/extensions/gsd/tests/worktree-git-pathspec.test.ts +1 -1
  315. package/src/resources/extensions/gsd/tests/worktree-main-branch.test.ts +1 -1
  316. package/src/resources/extensions/gsd/tests/worktree-preferences-sync.test.ts +1 -1
  317. package/src/resources/extensions/gsd/tests/worktree-reentry.test.ts +2 -1
  318. package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +5 -2
  319. package/src/resources/extensions/gsd/tests/worktree-sync-overwrite-loop.test.ts +1 -1
  320. package/src/resources/extensions/gsd/tests/worktree-sync-tasks.test.ts +1 -1
  321. package/src/resources/extensions/gsd/tool-contract.ts +29 -0
  322. package/src/resources/extensions/gsd/tools/reopen-slice.ts +5 -0
  323. package/src/resources/extensions/gsd/tools/reopen-task.ts +13 -5
  324. package/src/resources/extensions/gsd/unit-registry.ts +16 -2
  325. package/src/resources/extensions/gsd/unmerged-milestone-guard.ts +1 -1
  326. package/src/resources/extensions/gsd/validation-block-guard.ts +2 -1
  327. package/src/resources/extensions/gsd/workspace-git-preflight.ts +2 -2
  328. package/src/resources/extensions/gsd/worktree-command.ts +1 -1
  329. package/src/resources/extensions/gsd/worktree-lifecycle.ts +19 -15
  330. package/src/resources/extensions/gsd/worktree-reentry.ts +2 -1
  331. package/src/resources/extensions/shared/interview-ui.ts +58 -11
  332. package/src/resources/extensions/shared/tests/interview-ui-border.test.ts +32 -0
  333. /package/dist/web/standalone/.next/static/{L2UMqXELiDH3oz-7yguXp → gXoWec4UNqcR0tkuHwYCr}/_buildManifest.js +0 -0
  334. /package/dist/web/standalone/.next/static/{L2UMqXELiDH3oz-7yguXp → gXoWec4UNqcR0tkuHwYCr}/_ssgManifest.js +0 -0
@@ -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.
@@ -352,17 +352,17 @@ export function insertTask(t) {
352
352
  ON CONFLICT(milestone_id, slice_id, id) DO UPDATE SET
353
353
  title = CASE WHEN NULLIF(:title, '') IS NOT NULL THEN :title ELSE tasks.title END,
354
354
  status = :status,
355
- one_liner = :one_liner,
356
- narrative = :narrative,
357
- verification_result = :verification_result,
358
- duration = :duration,
359
- completed_at = :completed_at,
360
- blocker_discovered = :blocker_discovered,
361
- deviations = :deviations,
362
- known_issues = :known_issues,
363
- key_files = :key_files,
364
- key_decisions = :key_decisions,
365
- full_summary_md = :full_summary_md,
355
+ one_liner = CASE WHEN :preserve_completion = 1 AND NULLIF(:one_liner, '') IS NULL THEN tasks.one_liner ELSE :one_liner END,
356
+ narrative = CASE WHEN :preserve_completion = 1 AND NULLIF(:narrative, '') IS NULL THEN tasks.narrative ELSE :narrative END,
357
+ verification_result = CASE WHEN :preserve_completion = 1 AND NULLIF(:verification_result, '') IS NULL THEN tasks.verification_result ELSE :verification_result END,
358
+ duration = CASE WHEN :preserve_completion = 1 AND NULLIF(:duration, '') IS NULL THEN tasks.duration ELSE :duration END,
359
+ completed_at = CASE WHEN :preserve_completion = 1 AND tasks.completed_at IS NOT NULL THEN tasks.completed_at ELSE :completed_at END,
360
+ blocker_discovered = CASE WHEN :preserve_completion = 1 AND :blocker_discovered = 0 THEN tasks.blocker_discovered ELSE :blocker_discovered END,
361
+ deviations = CASE WHEN :preserve_completion = 1 AND NULLIF(:deviations, '') IS NULL THEN tasks.deviations ELSE :deviations END,
362
+ known_issues = CASE WHEN :preserve_completion = 1 AND NULLIF(:known_issues, '') IS NULL THEN tasks.known_issues ELSE :known_issues END,
363
+ key_files = CASE WHEN :preserve_completion = 1 AND NULLIF(:key_files, '[]') IS NULL THEN tasks.key_files ELSE :key_files END,
364
+ key_decisions = CASE WHEN :preserve_completion = 1 AND NULLIF(:key_decisions, '[]') IS NULL THEN tasks.key_decisions ELSE :key_decisions END,
365
+ full_summary_md = CASE WHEN :preserve_completion = 1 AND NULLIF(:full_summary_md, '') IS NULL THEN tasks.full_summary_md ELSE :full_summary_md END,
366
366
  description = CASE WHEN NULLIF(:description, '') IS NOT NULL THEN :description ELSE tasks.description END,
367
367
  estimate = CASE WHEN NULLIF(:estimate, '') IS NOT NULL THEN :estimate ELSE tasks.estimate END,
368
368
  files = CASE WHEN NULLIF(:files, '[]') IS NOT NULL THEN :files ELSE tasks.files END,
@@ -401,6 +401,7 @@ export function insertTask(t) {
401
401
  ":observability_impact": t.planning?.observabilityImpact ?? "",
402
402
  ":full_plan_md": t.planning?.fullPlanMd ?? "",
403
403
  ":sequence": t.sequence ?? 0,
404
+ ":preserve_completion": t.preserveCompletionMetadata && (t.status === "complete" || t.status === "done") ? 1 : 0,
404
405
  ":target_repositories": JSON.stringify(t.planning?.targetRepositories ?? []),
405
406
  ":raw_target_repositories": t.planning && "targetRepositories" in t.planning
406
407
  ? JSON.stringify(t.planning.targetRepositories ?? [])
@@ -1000,6 +1001,17 @@ export function deleteArtifactByPath(path) {
1000
1001
  throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
1001
1002
  getDbOrNull().prepare("DELETE FROM artifacts WHERE path = :path").run({ ":path": path });
1002
1003
  }
1004
+ /** Delete artifact rows whose paths share a DB-relative prefix. */
1005
+ export function deleteArtifactsByPathPrefix(prefix) {
1006
+ if (!getDbOrNull())
1007
+ throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
1008
+ return transaction(() => {
1009
+ const likePrefix = `${prefix}%`;
1010
+ const countRow = getDbOrNull().prepare("SELECT COUNT(*) AS count FROM artifacts WHERE path LIKE :prefix").get({ ":prefix": likePrefix });
1011
+ getDbOrNull().prepare("DELETE FROM artifacts WHERE path LIKE :prefix").run({ ":prefix": likePrefix });
1012
+ return Number(countRow?.["count"] ?? 0);
1013
+ });
1014
+ }
1003
1015
  /**
1004
1016
  * Drop hierarchy rows in dependency order inside a transaction. Used by
1005
1017
  * `gsd recover --confirm` to rebuild engine state from markdown.
@@ -27,8 +27,8 @@ import { readSessionLockData, isSessionLockProcessAlive } from "./session-lock.j
27
27
  import { nativeAddAll, nativeCommit, nativeHasCommittedHead, nativeIsRepo, nativeInit } from "./native-git-bridge.js";
28
28
  import { isInheritedRepo } from "./repo-identity.js";
29
29
  import { ensureGitignore, ensurePreferences, untrackRuntimeFiles } from "./gitignore.js";
30
- import { getIsolationMode, loadEffectiveGSDPreferences } from "./preferences.js";
31
- import { getAutoWorktreePath } from "./auto-worktree.js";
30
+ import { getIsolationMode, loadEffectiveGSDPreferences, renderLanguageDirectiveForPrompt } from "./preferences.js";
31
+ import { getAutoWorktreePath } from "./auto-worktree-path-resolution.js";
32
32
  import { resolveUokFlags } from "./uok/flags.js";
33
33
  import { ensurePlanV2Graph, isMissingFinalizedContextResult } from "./uok/plan-v2.js";
34
34
  import { detectProjectState, hasGsdBootstrapArtifacts } from "./detection.js";
@@ -50,7 +50,7 @@ import { getCachedWorkflowMcpProbe, probeAndCacheWorkflowMcp, warmWorkflowMcpPro
50
50
  import { probeCoversRequiredWorkflowTools } from "./tool-surface-readiness.js";
51
51
  import { getRequiredWorkflowToolsForUnit } from "./unit-tool-contracts.js";
52
52
  import { isWorkflowToolSurfaceName } from "./workflow-tool-surface.js";
53
- import { getUnitWorkflowDispatchReadinessError } from "./tool-contract.js";
53
+ import { getUnitWorkflowDispatchReadinessErrorForModel } from "./tool-contract.js";
54
54
  import { runPreparation, formatCodebaseBrief, formatPriorContextBrief, } from "./preparation.js";
55
55
  import { verifyExpectedArtifact } from "./auto-recovery.js";
56
56
  import { countPlanMilestoneRoadmapSlices } from "./artifact-verification.js";
@@ -491,7 +491,7 @@ async function dispatchWorkflow(pi, note, customType = "gsd-run", ctx, unitType,
491
491
  const loadPreferences = resolvedOptions.deps?.loadPreferences ?? loadEffectiveGSDPreferences;
492
492
  const selectModel = resolvedOptions.deps?.selectModel ?? selectAndApplyModel;
493
493
  const getDispatchReadinessError = resolvedOptions.deps?.getDispatchReadinessError
494
- ?? getUnitWorkflowDispatchReadinessError;
494
+ ?? getUnitWorkflowDispatchReadinessErrorForModel;
495
495
  // Route through the dynamic routing pipeline (complexity classification,
496
496
  // tier downgrade, fallback chains) — same path as auto-mode dispatches (#2958).
497
497
  if (ctx && unitType) {
@@ -506,16 +506,12 @@ async function dispatchWorkflow(pi, note, customType = "gsd-run", ctx, unitType,
506
506
  });
507
507
  }
508
508
  const compatibilityError = getDispatchReadinessError({
509
- provider: result.appliedModel?.provider ?? ctx.model?.provider,
509
+ model: result.appliedModel,
510
+ fallbackModel: ctx.model,
511
+ getProviderAuthMode: (provider) => ctx.modelRegistry.getProviderAuthMode(provider),
510
512
  projectRoot,
511
513
  surface: "guided flow",
512
514
  unitType,
513
- authMode: result.appliedModel?.provider
514
- ? ctx.modelRegistry.getProviderAuthMode(result.appliedModel.provider)
515
- : ctx.model?.provider
516
- ? ctx.modelRegistry.getProviderAuthMode(ctx.model.provider)
517
- : undefined,
518
- baseUrl: result.appliedModel?.baseUrl ?? ctx.model?.baseUrl,
519
515
  // Guided flow starts the MCP workflow server as part of dispatch, so the
520
516
  // parent session's activeTools doesn't include MCP tools yet. The MCP
521
517
  // launch config check (detectWorkflowMcpLaunchConfig) is the right gate
@@ -646,7 +642,7 @@ function resolveAvailableModel(modelId, availableModels, currentProvider) {
646
642
  * Build the discuss-and-plan prompt for a new milestone.
647
643
  * Used by all three "new milestone" paths (first ever, no active, all complete).
648
644
  */
649
- function buildDiscussPrompt(nextId, preamble, _basePath, pi, ctx, preparationContext) {
645
+ function buildDiscussPrompt(nextId, preamble, basePath, pi, ctx, preparationContext) {
650
646
  const milestoneRel = `.gsd/milestones/${nextId}`;
651
647
  const structuredQuestionsAvailable = getStructuredQuestionsAvailability(pi, ctx);
652
648
  const inlinedTemplates = [
@@ -656,7 +652,7 @@ function buildDiscussPrompt(nextId, preamble, _basePath, pi, ctx, preparationCon
656
652
  inlineTemplate("roadmap", "Roadmap"),
657
653
  inlineTemplate("decisions", "Decisions"),
658
654
  ].join("\n\n---\n\n");
659
- return loadPrompt("discuss", {
655
+ return prependLanguageDirective(basePath, loadPrompt("discuss", {
660
656
  milestoneId: nextId,
661
657
  preamble,
662
658
  preparationContext: preparationContext ?? "",
@@ -666,13 +662,22 @@ function buildDiscussPrompt(nextId, preamble, _basePath, pi, ctx, preparationCon
666
662
  inlinedTemplates,
667
663
  commitInstruction: buildDocsCommitInstruction(`docs(${nextId}): context, requirements, and roadmap`),
668
664
  multiMilestoneCommitInstruction: buildDocsCommitInstruction("docs: project plan — N milestones"),
669
- });
665
+ }));
666
+ }
667
+ /**
668
+ * Prepend the configured response-language directive to a dispatched discuss
669
+ * prompt so the discuss/QA conversation is held in the language set in
670
+ * PREFERENCES.md (#1210). No-op when no language is configured.
671
+ */
672
+ function prependLanguageDirective(basePath, prompt) {
673
+ const directive = renderLanguageDirectiveForPrompt(loadEffectiveGSDPreferences(basePath)?.preferences);
674
+ return directive ? `${directive}\n\n${prompt}` : prompt;
670
675
  }
671
676
  /**
672
677
  * Build the discuss prompt for headless milestone creation.
673
678
  * Uses the discuss-headless prompt template with seed context injected.
674
679
  */
675
- function buildHeadlessDiscussPrompt(nextId, seedContext, _basePath) {
680
+ function buildHeadlessDiscussPrompt(nextId, seedContext, basePath) {
676
681
  const milestoneRel = `.gsd/milestones/${nextId}`;
677
682
  const inlinedTemplates = [
678
683
  inlineTemplate("project", "Project"),
@@ -681,7 +686,7 @@ function buildHeadlessDiscussPrompt(nextId, seedContext, _basePath) {
681
686
  inlineTemplate("roadmap", "Roadmap"),
682
687
  inlineTemplate("decisions", "Decisions"),
683
688
  ].join("\n\n---\n\n");
684
- return loadPrompt("discuss-headless", {
689
+ return prependLanguageDirective(basePath, loadPrompt("discuss-headless", {
685
690
  milestoneId: nextId,
686
691
  seedContext,
687
692
  contextPath: `${milestoneRel}/${nextId}-CONTEXT.md`,
@@ -689,7 +694,7 @@ function buildHeadlessDiscussPrompt(nextId, seedContext, _basePath) {
689
694
  inlinedTemplates,
690
695
  commitInstruction: buildDocsCommitInstruction(`docs(${nextId}): context, requirements, and roadmap`),
691
696
  multiMilestoneCommitInstruction: buildDocsCommitInstruction("docs: project plan — N milestones"),
692
- });
697
+ }));
693
698
  }
694
699
  /**
695
700
  * Run preparation phase if enabled, then build the discuss prompt.
@@ -1045,9 +1045,21 @@ export async function renderReplanFromDb(basePath, milestoneId, sliceId, replanD
1045
1045
  return { replanPath: absPath, content };
1046
1046
  }
1047
1047
  export async function renderAssessmentFromDb(basePath, milestoneId, sliceId, assessmentData) {
1048
- // Flat-phase: assessment file lives in the phase dir
1049
- const slicePath = resolveSlicePath(basePath, milestoneId, sliceId)
1050
- ?? join(milestonesDir(basePath), canonicalPhaseDirName(milestoneId, getMilestone(milestoneId)?.title));
1048
+ // Flat-phase: the assessment file lives in the phase dir, NOT in a slices/SID/
1049
+ // subdir. resolveSlicePath() detects a slices/SID/ dir unconditionally, so a
1050
+ // stray slices/SID/ created by an earlier planning step would send this write
1051
+ // to a hybrid path (wrong dir, flat filename) that verification cannot find.
1052
+ // Guard with the same legacy-base check relSlicePath() uses so flat-phase
1053
+ // milestones always target the phase dir.
1054
+ const mDir = resolveMilestonePath(basePath, milestoneId);
1055
+ const legacyBase = legacyMilestonesDir(basePath);
1056
+ const isLegacyLayout = mDir
1057
+ ? mDir.startsWith(legacyBase + "/") || mDir.startsWith(legacyBase + "\\")
1058
+ : false;
1059
+ const fallbackDir = join(milestonesDir(basePath), canonicalPhaseDirName(milestoneId, getMilestone(milestoneId)?.title));
1060
+ const slicePath = isLegacyLayout
1061
+ ? (resolveSlicePath(basePath, milestoneId, sliceId) ?? fallbackDir)
1062
+ : (mDir ?? fallbackDir);
1051
1063
  const phaseNum = milestoneIdToPhaseNum(milestoneId);
1052
1064
  const planNum = sliceIdToPlanNum(sliceId);
1053
1065
  const absPath = join(slicePath, planFileName(phaseNum, planNum, "ASSESSMENT"));