@opengsd/gsd-pi 1.4.0-dev.c18009cd → 1.5.0-dev.553dc518

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 (274) hide show
  1. package/README.md +10 -10
  2. package/dist/resources/.managed-resources-content-hash +1 -1
  3. package/dist/resources/extensions/gsd/auto/closeout.js +97 -5
  4. package/dist/resources/extensions/gsd/auto/orchestrator.js +4 -3
  5. package/dist/resources/extensions/gsd/auto/pre-dispatch.js +6 -3
  6. package/dist/resources/extensions/gsd/auto/unit-phase.js +5 -9
  7. package/dist/resources/extensions/gsd/auto-direct-dispatch.js +4 -5
  8. package/dist/resources/extensions/gsd/auto-start.js +3 -2
  9. package/dist/resources/extensions/gsd/auto-worktree-branch-lifecycle.js +189 -0
  10. package/dist/resources/extensions/gsd/auto-worktree-cleanup.js +133 -0
  11. package/dist/resources/extensions/gsd/auto-worktree-conflict-auto-resolve.js +35 -0
  12. package/dist/resources/extensions/gsd/auto-worktree-creation.js +130 -0
  13. package/dist/resources/extensions/gsd/auto-worktree-entry.js +118 -0
  14. package/dist/resources/extensions/gsd/auto-worktree-merge-already-merged.js +98 -0
  15. package/dist/resources/extensions/gsd/auto-worktree-merge-branch-head.js +52 -0
  16. package/dist/resources/extensions/gsd/auto-worktree-merge-cleanup.js +87 -0
  17. package/dist/resources/extensions/gsd/auto-worktree-merge-code-changes.js +46 -0
  18. package/dist/resources/extensions/gsd/auto-worktree-merge-db-ready.js +68 -0
  19. package/dist/resources/extensions/gsd/auto-worktree-merge-integration-branch.js +51 -0
  20. package/dist/resources/extensions/gsd/auto-worktree-merge-message.js +106 -0
  21. package/dist/resources/extensions/gsd/auto-worktree-merge-pre-teardown.js +64 -0
  22. package/dist/resources/extensions/gsd/auto-worktree-merge-stash.js +168 -0
  23. package/dist/resources/extensions/gsd/auto-worktree-merge.js +302 -0
  24. package/dist/resources/extensions/gsd/auto-worktree-milestone-shelter.js +119 -0
  25. package/dist/resources/extensions/gsd/auto-worktree-path-resolution.js +5 -0
  26. package/dist/resources/extensions/gsd/auto-worktree-path-utils.js +5 -0
  27. package/dist/resources/extensions/gsd/auto-worktree-resource-version.js +41 -0
  28. package/dist/resources/extensions/gsd/auto-worktree-runtime-cleanup.js +99 -0
  29. package/dist/resources/extensions/gsd/auto-worktree-session-registry.js +57 -0
  30. package/dist/resources/extensions/gsd/auto-worktree-sync.js +249 -0
  31. package/dist/resources/extensions/gsd/auto-worktree-teardown.js +120 -0
  32. package/dist/resources/extensions/gsd/auto-worktree.js +20 -2073
  33. package/dist/resources/extensions/gsd/auto.js +9 -3
  34. package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +1 -1
  35. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +19 -1
  36. package/dist/resources/extensions/gsd/bootstrap/system-context.js +1 -1
  37. package/dist/resources/extensions/gsd/bootstrap/tool-call-loop-guard.js +118 -21
  38. package/dist/resources/extensions/gsd/commands-handlers.js +1 -1
  39. package/dist/resources/extensions/gsd/commands-workflow-templates.js +3 -3
  40. package/dist/resources/extensions/gsd/db/queries.js +9 -9
  41. package/dist/resources/extensions/gsd/doctor-engine-checks.js +93 -11
  42. package/dist/resources/extensions/gsd/flat-phase-migration.js +14 -3
  43. package/dist/resources/extensions/gsd/forensics.js +1 -1
  44. package/dist/resources/extensions/gsd/git-conflict-resolve.js +2 -2
  45. package/dist/resources/extensions/gsd/git-service.js +45 -0
  46. package/dist/resources/extensions/gsd/gsd-db.js +11 -0
  47. package/dist/resources/extensions/gsd/guided-flow.js +6 -10
  48. package/dist/resources/extensions/gsd/markdown-renderer.js +15 -3
  49. package/dist/resources/extensions/gsd/milestone-merge-transaction.js +11 -0
  50. package/dist/resources/extensions/gsd/milestone-settlement.js +1 -1
  51. package/dist/resources/extensions/gsd/parallel-merge.js +1 -1
  52. package/dist/resources/extensions/gsd/parallel-orchestrator.js +3 -1
  53. package/dist/resources/extensions/gsd/preferences-types.js +1 -0
  54. package/dist/resources/extensions/gsd/preferences-validation.js +82 -0
  55. package/dist/resources/extensions/gsd/prompts/system.md +6 -6
  56. package/dist/resources/extensions/gsd/quick.js +12 -3
  57. package/dist/resources/extensions/gsd/slice-parallel-orchestrator.js +2 -1
  58. package/dist/resources/extensions/gsd/state/derive/from-db.js +88 -153
  59. package/dist/resources/extensions/gsd/tool-contract.js +14 -0
  60. package/dist/resources/extensions/gsd/unit-registry.js +12 -2
  61. package/dist/resources/extensions/gsd/unmerged-milestone-guard.js +1 -1
  62. package/dist/resources/extensions/gsd/validation-block-guard.js +2 -1
  63. package/dist/resources/extensions/gsd/workspace-git-preflight.js +2 -2
  64. package/dist/resources/extensions/gsd/worktree-command.js +1 -1
  65. package/dist/resources/extensions/gsd/worktree-lifecycle.js +14 -9
  66. package/dist/resources/extensions/gsd/worktree-reentry.js +2 -1
  67. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  68. package/dist/web/standalone/.next/BUILD_ID +1 -1
  69. package/dist/web/standalone/.next/app-path-routes-manifest.json +8 -8
  70. package/dist/web/standalone/.next/build-manifest.json +2 -2
  71. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  72. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  73. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  74. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  75. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  76. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  77. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  78. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  79. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  80. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  81. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  82. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  83. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  84. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  85. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  86. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  87. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  88. package/dist/web/standalone/.next/server/app/index.html +1 -1
  89. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  90. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  91. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  92. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  93. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  94. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  95. package/dist/web/standalone/.next/server/app-paths-manifest.json +8 -8
  96. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  97. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  98. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  99. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  100. package/dist/web/standalone/node_modules/@gsd/native/package.json +1 -1
  101. package/dist/web/standalone/node_modules/node-pty/build/Makefile +1 -1
  102. package/integrations/hermes/open_gsd_hermes/gsd_client.py +24 -35
  103. package/integrations/hermes/tests/test_gsd_client_timeout.py +4 -4
  104. package/package.json +6 -6
  105. package/packages/cloud-mcp-gateway/package.json +2 -2
  106. package/packages/contracts/package.json +1 -1
  107. package/packages/daemon/package.json +4 -4
  108. package/packages/gsd-agent-core/dist/export-html/template.js +53 -4
  109. package/packages/gsd-agent-core/package.json +5 -5
  110. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  111. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js +3 -2
  112. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  113. package/packages/gsd-agent-modes/package.json +7 -7
  114. package/packages/mcp-server/package.json +4 -4
  115. package/packages/native/package.json +1 -1
  116. package/packages/pi-agent-core/dist/agent-loop.d.ts.map +1 -1
  117. package/packages/pi-agent-core/dist/agent-loop.js +14 -9
  118. package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
  119. package/packages/pi-agent-core/package.json +1 -1
  120. package/packages/pi-ai/dist/index.d.ts +1 -0
  121. package/packages/pi-ai/dist/index.d.ts.map +1 -1
  122. package/packages/pi-ai/dist/index.js +1 -0
  123. package/packages/pi-ai/dist/index.js.map +1 -1
  124. package/packages/pi-ai/dist/providers/transform-messages.d.ts.map +1 -1
  125. package/packages/pi-ai/dist/providers/transform-messages.js +7 -3
  126. package/packages/pi-ai/dist/providers/transform-messages.js.map +1 -1
  127. package/packages/pi-ai/dist/tool-result-content.d.ts +3 -0
  128. package/packages/pi-ai/dist/tool-result-content.d.ts.map +1 -0
  129. package/packages/pi-ai/dist/tool-result-content.js +62 -0
  130. package/packages/pi-ai/dist/tool-result-content.js.map +1 -0
  131. package/packages/pi-ai/dist/types.d.ts +8 -20
  132. package/packages/pi-ai/dist/types.d.ts.map +1 -1
  133. package/packages/pi-ai/dist/types.js.map +1 -1
  134. package/packages/pi-ai/package.json +1 -1
  135. package/packages/pi-coding-agent/package.json +7 -7
  136. package/packages/pi-tui/package.json +2 -2
  137. package/packages/rpc-client/package.json +2 -2
  138. package/pkg/dist/core/export-html/template.js +53 -4
  139. package/pkg/package.json +1 -1
  140. package/src/resources/extensions/gsd/auto/closeout.ts +114 -7
  141. package/src/resources/extensions/gsd/auto/orchestrator.ts +4 -3
  142. package/src/resources/extensions/gsd/auto/pre-dispatch.ts +6 -3
  143. package/src/resources/extensions/gsd/auto/unit-phase.ts +5 -9
  144. package/src/resources/extensions/gsd/auto-direct-dispatch.ts +4 -5
  145. package/src/resources/extensions/gsd/auto-start.ts +4 -2
  146. package/src/resources/extensions/gsd/auto-worktree-branch-lifecycle.ts +237 -0
  147. package/src/resources/extensions/gsd/auto-worktree-cleanup.ts +166 -0
  148. package/src/resources/extensions/gsd/auto-worktree-conflict-auto-resolve.ts +37 -0
  149. package/src/resources/extensions/gsd/auto-worktree-creation.ts +171 -0
  150. package/src/resources/extensions/gsd/auto-worktree-entry.ts +154 -0
  151. package/src/resources/extensions/gsd/auto-worktree-merge-already-merged.ts +161 -0
  152. package/src/resources/extensions/gsd/auto-worktree-merge-branch-head.ts +83 -0
  153. package/src/resources/extensions/gsd/auto-worktree-merge-cleanup.ts +132 -0
  154. package/src/resources/extensions/gsd/auto-worktree-merge-code-changes.ts +71 -0
  155. package/src/resources/extensions/gsd/auto-worktree-merge-db-ready.ts +112 -0
  156. package/src/resources/extensions/gsd/auto-worktree-merge-integration-branch.ts +99 -0
  157. package/src/resources/extensions/gsd/auto-worktree-merge-message.ts +166 -0
  158. package/src/resources/extensions/gsd/auto-worktree-merge-pre-teardown.ts +93 -0
  159. package/src/resources/extensions/gsd/auto-worktree-merge-stash.ts +213 -0
  160. package/src/resources/extensions/gsd/auto-worktree-merge.ts +391 -0
  161. package/src/resources/extensions/gsd/auto-worktree-milestone-shelter.ts +159 -0
  162. package/src/resources/extensions/gsd/auto-worktree-path-resolution.ts +6 -0
  163. package/src/resources/extensions/gsd/auto-worktree-path-utils.ts +10 -0
  164. package/src/resources/extensions/gsd/auto-worktree-resource-version.ts +44 -0
  165. package/src/resources/extensions/gsd/auto-worktree-runtime-cleanup.ts +119 -0
  166. package/src/resources/extensions/gsd/auto-worktree-session-registry.ts +71 -0
  167. package/src/resources/extensions/gsd/auto-worktree-sync.ts +370 -0
  168. package/src/resources/extensions/gsd/auto-worktree-teardown.ts +161 -0
  169. package/src/resources/extensions/gsd/auto-worktree.ts +69 -2429
  170. package/src/resources/extensions/gsd/auto.ts +11 -17
  171. package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +1 -1
  172. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +19 -1
  173. package/src/resources/extensions/gsd/bootstrap/system-context.ts +1 -1
  174. package/src/resources/extensions/gsd/bootstrap/tool-call-loop-guard.ts +152 -21
  175. package/src/resources/extensions/gsd/commands-handlers.ts +1 -1
  176. package/src/resources/extensions/gsd/commands-workflow-templates.ts +3 -3
  177. package/src/resources/extensions/gsd/db/queries.ts +9 -9
  178. package/src/resources/extensions/gsd/doctor-engine-checks.ts +109 -10
  179. package/src/resources/extensions/gsd/flat-phase-migration.ts +14 -3
  180. package/src/resources/extensions/gsd/forensics.ts +1 -1
  181. package/src/resources/extensions/gsd/git-conflict-resolve.ts +2 -2
  182. package/src/resources/extensions/gsd/git-service.ts +53 -0
  183. package/src/resources/extensions/gsd/gsd-db.ts +13 -0
  184. package/src/resources/extensions/gsd/guided-flow.ts +7 -11
  185. package/src/resources/extensions/gsd/markdown-renderer.ts +15 -3
  186. package/src/resources/extensions/gsd/milestone-merge-transaction.ts +15 -0
  187. package/src/resources/extensions/gsd/milestone-settlement.ts +1 -1
  188. package/src/resources/extensions/gsd/parallel-merge.ts +1 -1
  189. package/src/resources/extensions/gsd/parallel-orchestrator.ts +3 -1
  190. package/src/resources/extensions/gsd/preferences-types.ts +30 -0
  191. package/src/resources/extensions/gsd/preferences-validation.ts +69 -0
  192. package/src/resources/extensions/gsd/prompts/system.md +6 -6
  193. package/src/resources/extensions/gsd/quick.ts +11 -3
  194. package/src/resources/extensions/gsd/slice-parallel-orchestrator.ts +3 -1
  195. package/src/resources/extensions/gsd/state/derive/from-db.ts +201 -157
  196. package/src/resources/extensions/gsd/tests/auto-loop-no-copy-artifacts.test.ts +2 -1
  197. package/src/resources/extensions/gsd/tests/auto-start-clean-runtime-db-gated.test.ts +1 -1
  198. package/src/resources/extensions/gsd/tests/auto-warning-noise-regression.test.ts +2 -1
  199. package/src/resources/extensions/gsd/tests/auto-worktree-auto-resolve.test.ts +1 -1
  200. package/src/resources/extensions/gsd/tests/auto-worktree-merge-already-merged.test.ts +177 -0
  201. package/src/resources/extensions/gsd/tests/auto-worktree-merge-branch-head.test.ts +118 -0
  202. package/src/resources/extensions/gsd/tests/auto-worktree-merge-cleanup.test.ts +115 -0
  203. package/src/resources/extensions/gsd/tests/auto-worktree-merge-code-changes.test.ts +76 -0
  204. package/src/resources/extensions/gsd/tests/auto-worktree-merge-db-ready.test.ts +140 -0
  205. package/src/resources/extensions/gsd/tests/auto-worktree-merge-integration-branch.test.ts +123 -0
  206. package/src/resources/extensions/gsd/tests/auto-worktree-merge-message.test.ts +114 -0
  207. package/src/resources/extensions/gsd/tests/auto-worktree-merge-pre-teardown.test.ts +84 -0
  208. package/src/resources/extensions/gsd/tests/auto-worktree-merge-stash.test.ts +70 -0
  209. package/src/resources/extensions/gsd/tests/auto-worktree-milestone-shelter.test.ts +52 -0
  210. package/src/resources/extensions/gsd/tests/auto-worktree-registry.test.ts +6 -6
  211. package/src/resources/extensions/gsd/tests/auto-worktree-resource-version.test.ts +59 -0
  212. package/src/resources/extensions/gsd/tests/auto-worktree-untracked-content.test.ts +3 -1
  213. package/src/resources/extensions/gsd/tests/completed-units-metrics-sync.test.ts +1 -1
  214. package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +4 -0
  215. package/src/resources/extensions/gsd/tests/doctor-scope-db-unavailable.test.ts +174 -0
  216. package/src/resources/extensions/gsd/tests/fast-forward-reused-milestone-branch.test.ts +1 -1
  217. package/src/resources/extensions/gsd/tests/flat-phase-migration.test.ts +44 -1
  218. package/src/resources/extensions/gsd/tests/flat-phase-renderer.test.ts +25 -1
  219. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +40 -0
  220. package/src/resources/extensions/gsd/tests/integration/all-milestones-complete-merge.test.ts +4 -6
  221. package/src/resources/extensions/gsd/tests/integration/auto-stash-merge.test.ts +2 -1
  222. package/src/resources/extensions/gsd/tests/integration/auto-worktree-milestone-merge.test.ts +3 -3
  223. package/src/resources/extensions/gsd/tests/integration/auto-worktree.test.ts +7 -8
  224. package/src/resources/extensions/gsd/tests/integration/feature-branch-lifecycle-integration.test.ts +2 -5
  225. package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +76 -0
  226. package/src/resources/extensions/gsd/tests/integration/merge-cwd-restore.test.ts +1 -1
  227. package/src/resources/extensions/gsd/tests/integration/merge-preserve-worktree-on-preteardown-error.test.ts +1 -1
  228. package/src/resources/extensions/gsd/tests/integration/merge-strategy-regular.test.ts +2 -1
  229. package/src/resources/extensions/gsd/tests/integration/milestone-transition-worktree.test.ts +5 -7
  230. package/src/resources/extensions/gsd/tests/integration/workspace-collapse-integration.test.ts +2 -4
  231. package/src/resources/extensions/gsd/tests/merge-closeout-consistency-gate.test.ts +1 -1
  232. package/src/resources/extensions/gsd/tests/merge-db-cycle.test.ts +1 -1
  233. package/src/resources/extensions/gsd/tests/merge-self-branch-guard.test.ts +1 -1
  234. package/src/resources/extensions/gsd/tests/milestone-merge-stash-restore.test.ts +439 -0
  235. package/src/resources/extensions/gsd/tests/milestone-merge-transaction.test.ts +24 -0
  236. package/src/resources/extensions/gsd/tests/none-mode-gates.test.ts +1 -1
  237. package/src/resources/extensions/gsd/tests/originalbase-path-comparison.test.ts +3 -3
  238. package/src/resources/extensions/gsd/tests/parallel-worker-lock-contention.test.ts +1 -1
  239. package/src/resources/extensions/gsd/tests/preferences-worktree-sync.test.ts +1 -4
  240. package/src/resources/extensions/gsd/tests/prefs-wizard-coverage.test.ts +5 -0
  241. package/src/resources/extensions/gsd/tests/runtime-invariant-modules.test.ts +20 -0
  242. package/src/resources/extensions/gsd/tests/stale-worktree-cwd.test.ts +3 -5
  243. package/src/resources/extensions/gsd/tests/stash-pop-gsd-conflict.test.ts +2 -1
  244. package/src/resources/extensions/gsd/tests/stash-queued-context-files.test.ts +2 -1
  245. package/src/resources/extensions/gsd/tests/steer-worktree-path.test.ts +1 -1
  246. package/src/resources/extensions/gsd/tests/sync-worktree-skip-current.test.ts +1 -1
  247. package/src/resources/extensions/gsd/tests/system-flat-phase-layout.test.ts +33 -0
  248. package/src/resources/extensions/gsd/tests/teardown-chdir-failure-clears-registry.test.ts +4 -4
  249. package/src/resources/extensions/gsd/tests/teardown-cleanup-parity.test.ts +2 -1
  250. package/src/resources/extensions/gsd/tests/teardown-failure-clears-registry.test.ts +4 -4
  251. package/src/resources/extensions/gsd/tests/tool-call-loop-guard.test.ts +103 -0
  252. package/src/resources/extensions/gsd/tests/uat-stuck-loop-orphaned-worktree.test.ts +1 -1
  253. package/src/resources/extensions/gsd/tests/unit-registry.test.ts +26 -1
  254. package/src/resources/extensions/gsd/tests/worktree-db-integration.test.ts +1 -1
  255. package/src/resources/extensions/gsd/tests/worktree-db-respawn-truncation.test.ts +1 -1
  256. package/src/resources/extensions/gsd/tests/worktree-db-same-file.test.ts +1 -3
  257. package/src/resources/extensions/gsd/tests/worktree-expected-warnings.test.ts +1 -1
  258. package/src/resources/extensions/gsd/tests/worktree-git-pathspec.test.ts +1 -1
  259. package/src/resources/extensions/gsd/tests/worktree-main-branch.test.ts +1 -1
  260. package/src/resources/extensions/gsd/tests/worktree-preferences-sync.test.ts +1 -1
  261. package/src/resources/extensions/gsd/tests/worktree-reentry.test.ts +2 -1
  262. package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +5 -2
  263. package/src/resources/extensions/gsd/tests/worktree-sync-overwrite-loop.test.ts +1 -1
  264. package/src/resources/extensions/gsd/tests/worktree-sync-tasks.test.ts +1 -1
  265. package/src/resources/extensions/gsd/tool-contract.ts +29 -0
  266. package/src/resources/extensions/gsd/unit-registry.ts +16 -2
  267. package/src/resources/extensions/gsd/unmerged-milestone-guard.ts +1 -1
  268. package/src/resources/extensions/gsd/validation-block-guard.ts +2 -1
  269. package/src/resources/extensions/gsd/workspace-git-preflight.ts +2 -2
  270. package/src/resources/extensions/gsd/worktree-command.ts +1 -1
  271. package/src/resources/extensions/gsd/worktree-lifecycle.ts +19 -15
  272. package/src/resources/extensions/gsd/worktree-reentry.ts +2 -1
  273. /package/dist/web/standalone/.next/static/{L2UMqXELiDH3oz-7yguXp → ZpFDazP-38PXBPiI_LdZf}/_buildManifest.js +0 -0
  274. /package/dist/web/standalone/.next/static/{L2UMqXELiDH3oz-7yguXp → ZpFDazP-38PXBPiI_LdZf}/_ssgManifest.js +0 -0
@@ -3,6 +3,10 @@
3
3
 
4
4
  import test from "node:test";
5
5
  import assert from "node:assert/strict";
6
+ import { execFileSync } from "node:child_process";
7
+ import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
8
+ import { tmpdir } from "node:os";
9
+ import { join } from "node:path";
6
10
 
7
11
  import {
8
12
  _runMilestoneMergeOnceWithStashRestore,
@@ -10,6 +14,9 @@ import {
10
14
  } from "../auto/closeout.js";
11
15
  import type { IterationContext } from "../auto/types.js";
12
16
  import { MergeConflictError } from "../git-service.js";
17
+ import { closeDatabase, insertDecision, insertMilestone, insertSlice, insertTask, openDatabase } from "../gsd-db.js";
18
+ import { renderAllFromDb } from "../markdown-renderer.js";
19
+ import { resolveMilestoneFile } from "../paths.js";
13
20
  import type {
14
21
  PostflightResult,
15
22
  PreflightResult,
@@ -234,6 +241,70 @@ const PREFLIGHT_UNMERGED_EVAL_FAILED: PreflightResult = {
234
241
  summary: "Unable to fully evaluate unresolved Git conflicts before milestone M002 merge.",
235
242
  };
236
243
 
244
+ function git(basePath: string, args: string[], stdio: "ignore" | "pipe" = "pipe"): void {
245
+ execFileSync("git", args, { cwd: basePath, stdio });
246
+ }
247
+
248
+ async function withProjectionBackedProject<T>(
249
+ fn: (basePath: string, roadmapPath: string) => Promise<T>,
250
+ options: { gitInit?: boolean } = {},
251
+ ): Promise<T> {
252
+ const basePath = mkdtempSync(join(tmpdir(), "gsd-postflight-projection-"));
253
+ try {
254
+ mkdirSync(join(basePath, ".gsd"), { recursive: true });
255
+ openDatabase(join(basePath, ".gsd", "gsd.db"));
256
+ insertMilestone({
257
+ id: "M002",
258
+ title: "Postflight Guard",
259
+ status: "active",
260
+ });
261
+ insertSlice({
262
+ id: "S01",
263
+ milestoneId: "M002",
264
+ title: "Slice",
265
+ status: "pending",
266
+ risk: "medium",
267
+ depends: [],
268
+ demo: "demo",
269
+ sequence: 1,
270
+ });
271
+ insertTask({
272
+ id: "T01",
273
+ sliceId: "S01",
274
+ milestoneId: "M002",
275
+ title: "Task",
276
+ status: "pending",
277
+ });
278
+ await renderAllFromDb(basePath);
279
+ if (options.gitInit !== false) {
280
+ git(basePath, ["init"], "ignore");
281
+ git(basePath, ["config", "user.email", "test@example.invalid"]);
282
+ git(basePath, ["config", "user.name", "Test"]);
283
+ git(basePath, ["add", ".gsd"]);
284
+ git(basePath, ["commit", "-m", "initial projections"], "ignore");
285
+ }
286
+ const roadmapPath = resolveMilestoneFile(basePath, "M002", "ROADMAP");
287
+ assert.ok(roadmapPath, "expected rendered M002 roadmap path");
288
+ return await fn(basePath, roadmapPath);
289
+ } finally {
290
+ closeDatabase();
291
+ rmSync(basePath, { recursive: true, force: true });
292
+ }
293
+ }
294
+
295
+ function insertFreshDbSlice(): void {
296
+ insertSlice({
297
+ id: "S02",
298
+ milestoneId: "M002",
299
+ title: "Fresh DB Slice",
300
+ status: "pending",
301
+ risk: "medium",
302
+ depends: [],
303
+ demo: "demo",
304
+ sequence: 2,
305
+ });
306
+ }
307
+
237
308
  const POP_OK: PostflightResult = {
238
309
  restored: true,
239
310
  needsManualRecovery: false,
@@ -469,6 +540,374 @@ test("already-merged milestone skips guarded merge to terminate duplicate closeo
469
540
  );
470
541
  });
471
542
 
543
+
544
+ test("postflight-restored projection edits survive successful merge rebuild", async () => {
545
+ await withProjectionBackedProject(async (basePath, roadmapPath) => {
546
+ const { ic } = buildIc({
547
+ preflightResult: STASH_PUSHED,
548
+ mergeBehavior: "succeed",
549
+ postflightResult: POP_OK,
550
+ });
551
+ (ic.s as { basePath: string; originalBasePath: string }).basePath = basePath;
552
+ (ic.s as { basePath: string; originalBasePath: string }).originalBasePath = basePath;
553
+ (ic.deps as unknown as { postflightPopStash: () => PostflightResult }).postflightPopStash = () => {
554
+ writeFileSync(roadmapPath, "# local projection edit restored from stash\n", "utf8");
555
+ return POP_OK;
556
+ };
557
+
558
+ const result = await _runMilestoneMergeWithStashRestore(ic, "M002");
559
+
560
+ assert.equal(result, null);
561
+ assert.equal(
562
+ readFileSync(roadmapPath, "utf8"),
563
+ "# local projection edit restored from stash\n",
564
+ "projection rebuild must not overwrite user edits restored by postflight stash pop",
565
+ );
566
+ });
567
+ });
568
+
569
+ test("untracked markdown under a new generated .gsd projection directory suppresses successful merge rebuild", async () => {
570
+ await withProjectionBackedProject(async (basePath, roadmapPath) => {
571
+ insertFreshDbSlice();
572
+
573
+ const { ic } = buildIc({
574
+ preflightResult: STASH_PUSHED,
575
+ mergeBehavior: "succeed",
576
+ postflightResult: POP_OK,
577
+ });
578
+ (ic.s as { basePath: string; originalBasePath: string }).basePath = basePath;
579
+ (ic.s as { basePath: string; originalBasePath: string }).originalBasePath = basePath;
580
+ (ic.deps as unknown as { postflightPopStash: () => PostflightResult }).postflightPopStash = () => {
581
+ const restoredDir = join(basePath, ".gsd", "milestones", "M002", "slices", "S99");
582
+ mkdirSync(restoredDir, { recursive: true });
583
+ writeFileSync(join(restoredDir, "S99-SUMMARY.md"), "# restored local markdown\n", "utf8");
584
+ return POP_OK;
585
+ };
586
+
587
+ const result = await _runMilestoneMergeWithStashRestore(ic, "M002");
588
+
589
+ assert.equal(result, null);
590
+ assert.doesNotMatch(
591
+ readFileSync(roadmapPath, "utf8"),
592
+ /Fresh DB Slice/,
593
+ "untracked markdown projections inside generated .gsd directories must suppress rebuild",
594
+ );
595
+ });
596
+ });
597
+
598
+ test("ignored untracked markdown under .gsd suppresses successful merge rebuild", async () => {
599
+ await withProjectionBackedProject(async (basePath, roadmapPath) => {
600
+ writeFileSync(join(basePath, ".gitignore"), ".gsd\n", "utf8");
601
+ git(basePath, ["add", ".gitignore"]);
602
+ git(basePath, ["commit", "-m", "ignore gsd projections"], "ignore");
603
+ insertFreshDbSlice();
604
+
605
+ const { ic } = buildIc({
606
+ preflightResult: STASH_PUSHED,
607
+ mergeBehavior: "succeed",
608
+ postflightResult: POP_OK,
609
+ });
610
+ (ic.s as { basePath: string; originalBasePath: string }).basePath = basePath;
611
+ (ic.s as { basePath: string; originalBasePath: string }).originalBasePath = basePath;
612
+ (ic.deps as unknown as { postflightPopStash: () => PostflightResult }).postflightPopStash = () => {
613
+ const restoredDir = join(basePath, ".gsd", "milestones", "M002", "ignored-restored-projections");
614
+ mkdirSync(restoredDir, { recursive: true });
615
+ writeFileSync(join(restoredDir, "S99-SUMMARY.md"), "# ignored restored local markdown\n", "utf8");
616
+ return POP_OK;
617
+ };
618
+
619
+ const result = await _runMilestoneMergeWithStashRestore(ic, "M002");
620
+
621
+ assert.equal(result, null);
622
+ assert.doesNotMatch(
623
+ readFileSync(roadmapPath, "utf8"),
624
+ /Fresh DB Slice/,
625
+ "ignored .gsd markdown projections restored by postflight must suppress rebuild",
626
+ );
627
+ });
628
+ });
629
+
630
+ test("preexisting ignored markdown under .gsd does not suppress successful merge rebuild", async () => {
631
+ await withProjectionBackedProject(async (basePath, roadmapPath) => {
632
+ writeFileSync(join(basePath, ".gitignore"), ".gsd\n", "utf8");
633
+ git(basePath, ["add", ".gitignore"]);
634
+ git(basePath, ["commit", "-m", "ignore gsd projections"], "ignore");
635
+ mkdirSync(join(basePath, ".gsd", "milestones", "M002", "ignored-existing-projections"), { recursive: true });
636
+ writeFileSync(
637
+ join(basePath, ".gsd", "milestones", "M002", "ignored-existing-projections", "S99-SUMMARY.md"),
638
+ "# already ignored local markdown\n",
639
+ "utf8",
640
+ );
641
+ insertFreshDbSlice();
642
+
643
+ const { ic } = buildIc({
644
+ preflightResult: STASH_PUSHED,
645
+ mergeBehavior: "succeed",
646
+ postflightResult: POP_OK,
647
+ });
648
+ (ic.s as { basePath: string; originalBasePath: string }).basePath = basePath;
649
+ (ic.s as { basePath: string; originalBasePath: string }).originalBasePath = basePath;
650
+
651
+ const result = await _runMilestoneMergeWithStashRestore(ic, "M002");
652
+
653
+ assert.equal(result, null);
654
+ assert.match(
655
+ readFileSync(roadmapPath, "utf8"),
656
+ /Fresh DB Slice/,
657
+ "stable preexisting ignored .gsd markdown must not block the required projection rebuild",
658
+ );
659
+ });
660
+ });
661
+
662
+ test("failed pre-merge ignored snapshot still suppresses rebuild over restored ignored markdown", async () => {
663
+ await withProjectionBackedProject(async (basePath, roadmapPath) => {
664
+ insertFreshDbSlice();
665
+
666
+ const { ic } = buildIc({
667
+ preflightResult: STASH_PUSHED,
668
+ mergeBehavior: "succeed",
669
+ postflightResult: POP_OK,
670
+ });
671
+ (ic.s as { basePath: string; originalBasePath: string }).basePath = basePath;
672
+ (ic.s as { basePath: string; originalBasePath: string }).originalBasePath = basePath;
673
+ (ic.deps as unknown as { postflightPopStash: () => PostflightResult }).postflightPopStash = () => {
674
+ // Git only becomes available AFTER the pre-merge ignored-markdown
675
+ // snapshot was attempted, so `before` is null. Postflight then restores
676
+ // ignored `.gsd` markdown that plain `git status` cannot see. With no
677
+ // baseline to diff against, the guard must fail closed and refuse to
678
+ // rebuild over the restored edit.
679
+ git(basePath, ["init"], "ignore");
680
+ git(basePath, ["config", "user.email", "test@example.invalid"]);
681
+ git(basePath, ["config", "user.name", "Test"]);
682
+ writeFileSync(join(basePath, ".gitignore"), ".gsd\n", "utf8");
683
+ git(basePath, ["add", ".gitignore"]);
684
+ git(basePath, ["commit", "-m", "ignore gsd projections"], "ignore");
685
+ writeFileSync(roadmapPath, "# ignored projection edit restored from stash\n", "utf8");
686
+ return POP_OK;
687
+ };
688
+
689
+ const result = await _runMilestoneMergeWithStashRestore(ic, "M002");
690
+
691
+ assert.equal(result, null);
692
+ assert.equal(
693
+ readFileSync(roadmapPath, "utf8"),
694
+ "# ignored projection edit restored from stash\n",
695
+ "a null pre-merge ignored snapshot must fail closed and preserve restored ignored .gsd markdown",
696
+ );
697
+ }, { gitInit: false });
698
+ });
699
+
700
+ test("non-projection .gsd markdown does not suppress successful merge rebuild", async () => {
701
+ await withProjectionBackedProject(async (basePath, roadmapPath) => {
702
+ insertFreshDbSlice();
703
+
704
+ const { ic } = buildIc({
705
+ preflightResult: STASH_PUSHED,
706
+ mergeBehavior: "succeed",
707
+ postflightResult: POP_OK,
708
+ });
709
+ (ic.s as { basePath: string; originalBasePath: string }).basePath = basePath;
710
+ (ic.s as { basePath: string; originalBasePath: string }).originalBasePath = basePath;
711
+ (ic.deps as unknown as { postflightPopStash: () => PostflightResult }).postflightPopStash = () => {
712
+ writeFileSync(join(basePath, ".gsd", "PREFERENCES.md"), "# local preferences note\n", "utf8");
713
+ mkdirSync(join(basePath, ".gsd", "milestones", "M002"), { recursive: true });
714
+ writeFileSync(join(basePath, ".gsd", "milestones", "M002", "NOTES.md"), "# local milestone note\n", "utf8");
715
+ return POP_OK;
716
+ };
717
+
718
+ const result = await _runMilestoneMergeWithStashRestore(ic, "M002");
719
+
720
+ assert.equal(result, null);
721
+ assert.match(
722
+ readFileSync(roadmapPath, "utf8"),
723
+ /Fresh DB Slice/,
724
+ "non-generated .gsd markdown must not block the required projection rebuild",
725
+ );
726
+ });
727
+ });
728
+
729
+ test("three-digit flat-phase projection restored by postflight suppresses successful merge rebuild", async () => {
730
+ await withProjectionBackedProject(async (basePath, roadmapPath) => {
731
+ insertFreshDbSlice();
732
+
733
+ const { ic } = buildIc({
734
+ preflightResult: STASH_PUSHED,
735
+ mergeBehavior: "succeed",
736
+ postflightResult: POP_OK,
737
+ });
738
+ (ic.s as { basePath: string; originalBasePath: string }).basePath = basePath;
739
+ (ic.s as { basePath: string; originalBasePath: string }).originalBasePath = basePath;
740
+ (ic.deps as unknown as { postflightPopStash: () => PostflightResult }).postflightPopStash = () => {
741
+ const restoredDir = join(basePath, ".gsd", "phases", "999-db-backed-planning");
742
+ mkdirSync(restoredDir, { recursive: true });
743
+ writeFileSync(join(restoredDir, "999-ROADMAP.md"), "# restored 3-digit projection\n", "utf8");
744
+ return POP_OK;
745
+ };
746
+
747
+ const result = await _runMilestoneMergeWithStashRestore(ic, "M002");
748
+
749
+ assert.equal(result, null);
750
+ assert.doesNotMatch(
751
+ readFileSync(roadmapPath, "utf8"),
752
+ /Fresh DB Slice/,
753
+ "three-digit flat-phase projections restored by postflight must suppress rebuild",
754
+ );
755
+ });
756
+ });
757
+
758
+ test("root DECISIONS projection restored by postflight suppresses successful merge rebuild", async () => {
759
+ await withProjectionBackedProject(async (basePath, roadmapPath) => {
760
+ insertFreshDbSlice();
761
+ insertDecision({
762
+ id: "D001",
763
+ when_context: "closeout",
764
+ scope: "global",
765
+ decision: "Regenerate decisions from DB",
766
+ choice: "Use database projection",
767
+ rationale: "Exercise root decision projection rebuild",
768
+ revisable: "Yes",
769
+ made_by: "agent",
770
+ superseded_by: null,
771
+ });
772
+
773
+ const { ic } = buildIc({
774
+ preflightResult: STASH_PUSHED,
775
+ mergeBehavior: "succeed",
776
+ postflightResult: POP_OK,
777
+ });
778
+ (ic.s as { basePath: string; originalBasePath: string }).basePath = basePath;
779
+ (ic.s as { basePath: string; originalBasePath: string }).originalBasePath = basePath;
780
+ (ic.deps as unknown as { postflightPopStash: () => PostflightResult }).postflightPopStash = () => {
781
+ writeFileSync(
782
+ join(basePath, ".gsd", "DECISIONS.md"),
783
+ "# local decisions edit restored from stash\n",
784
+ "utf8",
785
+ );
786
+ return POP_OK;
787
+ };
788
+
789
+ const result = await _runMilestoneMergeWithStashRestore(ic, "M002");
790
+
791
+ assert.equal(result, null);
792
+ assert.doesNotMatch(
793
+ readFileSync(roadmapPath, "utf8"),
794
+ /Fresh DB Slice/,
795
+ "root generated DECISIONS.md restored by postflight must suppress rebuild",
796
+ );
797
+ assert.equal(
798
+ readFileSync(join(basePath, ".gsd", "DECISIONS.md"), "utf8"),
799
+ "# local decisions edit restored from stash\n",
800
+ "rebuild must not overwrite restored root DECISIONS.md edits",
801
+ );
802
+ });
803
+ });
804
+
805
+ test("non-projection .gsd dirt does not suppress successful merge rebuild", async () => {
806
+ await withProjectionBackedProject(async (basePath, roadmapPath) => {
807
+ insertFreshDbSlice();
808
+ mkdirSync(join(basePath, ".gsd", "runtime"), { recursive: true });
809
+
810
+ const { ic } = buildIc({
811
+ preflightResult: STASH_PUSHED,
812
+ mergeBehavior: "succeed",
813
+ postflightResult: POP_OK,
814
+ });
815
+ (ic.s as { basePath: string; originalBasePath: string }).basePath = basePath;
816
+ (ic.s as { basePath: string; originalBasePath: string }).originalBasePath = basePath;
817
+ (ic.deps as unknown as { postflightPopStash: () => PostflightResult }).postflightPopStash = () => {
818
+ writeFileSync(join(basePath, ".gsd", "runtime", "restored.json"), "{}\n", "utf8");
819
+ return POP_OK;
820
+ };
821
+
822
+ const result = await _runMilestoneMergeWithStashRestore(ic, "M002");
823
+
824
+ assert.equal(result, null);
825
+ assert.match(
826
+ readFileSync(roadmapPath, "utf8"),
827
+ /Fresh DB Slice/,
828
+ "non-projection .gsd dirt must not block the required projection rebuild",
829
+ );
830
+ });
831
+ });
832
+
833
+ test("guarded merge stash restore and rebuild target the same canonical project root", async () => {
834
+ await withProjectionBackedProject(async (realRoot, roadmapPath) => {
835
+ insertFreshDbSlice();
836
+ let postflightProjectRoot: string | undefined;
837
+
838
+ const { ic } = buildIc({
839
+ preflightResult: STASH_PUSHED,
840
+ mergeBehavior: "succeed",
841
+ postflightResult: POP_OK,
842
+ });
843
+ // originalBasePath empty + basePath diverging from canonicalProjectRoot is
844
+ // the exact condition where the guard (snapshot / dirty check / rebuild)
845
+ // could run against a different tree than the postflight stash restore.
846
+ // The postflight restore must land on the same canonical root the guard
847
+ // inspects and the rebuild writes, otherwise restored edits are missed.
848
+ (ic.s as unknown as {
849
+ basePath: string;
850
+ originalBasePath: string;
851
+ canonicalProjectRoot: string;
852
+ }).basePath = "/tmp/gsd-decoy-basepath-not-the-project-root";
853
+ (ic.s as unknown as { originalBasePath: string }).originalBasePath = "";
854
+ (ic.s as unknown as { canonicalProjectRoot: string }).canonicalProjectRoot = realRoot;
855
+ (ic.deps as unknown as { postflightPopStash: (projectRoot: string) => PostflightResult }).postflightPopStash =
856
+ (projectRoot: string) => {
857
+ postflightProjectRoot = projectRoot;
858
+ writeFileSync(roadmapPath, "# projection edit restored from stash\n", "utf8");
859
+ return POP_OK;
860
+ };
861
+
862
+ const result = await _runMilestoneMergeWithStashRestore(ic, "M002");
863
+
864
+ assert.equal(result, null);
865
+ assert.equal(
866
+ postflightProjectRoot,
867
+ realRoot,
868
+ "postflight stash restore must run at the canonical project root the guard and rebuild use",
869
+ );
870
+ assert.equal(
871
+ readFileSync(roadmapPath, "utf8"),
872
+ "# projection edit restored from stash\n",
873
+ "restored projection edits on the canonical root must suppress the rebuild",
874
+ );
875
+ });
876
+ });
877
+
878
+ test("git-quoted special-character .gsd markdown paths still suppress rebuild", async () => {
879
+ await withProjectionBackedProject(async (basePath, roadmapPath) => {
880
+ insertFreshDbSlice();
881
+
882
+ const { ic } = buildIc({
883
+ preflightResult: STASH_PUSHED,
884
+ mergeBehavior: "succeed",
885
+ postflightResult: POP_OK,
886
+ });
887
+ (ic.s as { basePath: string; originalBasePath: string }).basePath = basePath;
888
+ (ic.s as { basePath: string; originalBasePath: string }).originalBasePath = basePath;
889
+ (ic.deps as unknown as { postflightPopStash: () => PostflightResult }).postflightPopStash = () => {
890
+ const restoredDir = join(basePath, ".gsd", "milestones", "M002", "restoréd");
891
+ mkdirSync(restoredDir, { recursive: true });
892
+ // A non-ASCII generated projection path that `git status --porcelain`
893
+ // C-quotes without -z; a naive line.endsWith(".md") check would miss the
894
+ // quoted path. NUL-delimited (-z) parsing sees the raw path and still
895
+ // detects it as generated projection dirt.
896
+ writeFileSync(join(restoredDir, "S99-SUMMARY.md"), "# restored local markdown\n", "utf8");
897
+ return POP_OK;
898
+ };
899
+
900
+ const result = await _runMilestoneMergeWithStashRestore(ic, "M002");
901
+
902
+ assert.equal(result, null);
903
+ assert.doesNotMatch(
904
+ readFileSync(roadmapPath, "utf8"),
905
+ /Fresh DB Slice/,
906
+ "special-character generated .gsd markdown restored by postflight must suppress the rebuild",
907
+ );
908
+ });
909
+ });
910
+
472
911
  test("merge succeeds but stash pop needs manual recovery -> postflight-stash-restore-failed break", async () => {
473
912
  const { ic, log } = buildIc({
474
913
  preflightResult: STASH_PUSHED,
@@ -5,6 +5,7 @@ import test from "node:test";
5
5
  import assert from "node:assert/strict";
6
6
 
7
7
  import {
8
+ createDefaultMilestoneMergeTransaction,
8
9
  createMilestoneMergeTransaction,
9
10
  runMilestoneMergeTransaction,
10
11
  } from "../milestone-merge-transaction.js";
@@ -44,3 +45,26 @@ test("createMilestoneMergeTransaction returns a lifecycle-compatible runner", ()
44
45
  codeFilesChanged: false,
45
46
  });
46
47
  });
48
+
49
+ test("createDefaultMilestoneMergeTransaction hides the legacy merge adapter behind the transaction seam", () => {
50
+ const calls: unknown[][] = [];
51
+ const runner = createDefaultMilestoneMergeTransaction(
52
+ (basePath, milestoneId, roadmapContent) => {
53
+ calls.push([basePath, milestoneId, roadmapContent]);
54
+ return {
55
+ pushed: false,
56
+ codeFilesChanged: true,
57
+ commitMessage: "merge M003",
58
+ prCreated: false,
59
+ };
60
+ },
61
+ );
62
+
63
+ assert.deepEqual(runner("/repo", "M003", "# M003"), {
64
+ pushed: false,
65
+ codeFilesChanged: true,
66
+ commitMessage: "merge M003",
67
+ prCreated: false,
68
+ });
69
+ assert.deepEqual(calls, [["/repo", "M003", "# M003"]]);
70
+ });
@@ -18,7 +18,7 @@ import { execFileSync } from "node:child_process";
18
18
 
19
19
  import { shouldUseWorktreeIsolation } from "../auto.ts";
20
20
  import { getIsolationMode } from "../preferences.ts";
21
- import { getActiveAutoWorktreeContext } from "../auto-worktree.ts";
21
+ import { getActiveAutoWorktreeContext } from "../auto-worktree-session-registry.ts";
22
22
  import { invalidateAllCaches } from "../cache.ts";
23
23
  import { describe, test, beforeEach, afterEach } from 'node:test';
24
24
  import assert from 'node:assert/strict';
@@ -26,12 +26,12 @@ import { join } from "node:path";
26
26
  import { tmpdir } from "node:os";
27
27
  import { execFileSync } from "node:child_process";
28
28
 
29
+ import { createAutoWorktree } from "../auto-worktree-creation.ts";
29
30
  import {
30
31
  getAutoWorktreeOriginalBase,
31
32
  _resetAutoWorktreeOriginalBaseForTests,
32
- createAutoWorktree,
33
- teardownAutoWorktree,
34
- } from "../auto-worktree.ts";
33
+ } from "../auto-worktree-session-registry.ts";
34
+ import { teardownAutoWorktree } from "../auto-worktree-teardown.ts";
35
35
  import { normalizeWorktreePathForCompare } from "../worktree-root.ts";
36
36
 
37
37
  // ─── Helpers ──────────────────────────────────────────────────────────────────
@@ -32,7 +32,7 @@ import { gsdRoot } from "../paths.ts";
32
32
  import {
33
33
  syncProjectRootToWorktree,
34
34
  syncStateToProjectRoot,
35
- } from "../auto-worktree.ts";
35
+ } from "../auto-worktree-sync.ts";
36
36
  import { writeLock, readCrashLock, clearLock } from "../crash-recovery.ts";
37
37
  import { describe, test, beforeEach, afterEach } from "node:test";
38
38
  import assert from "node:assert/strict";
@@ -12,7 +12,7 @@ import assert from "node:assert/strict";
12
12
  import { readFileSync, mkdtempSync, mkdirSync, writeFileSync, existsSync, readdirSync, rmSync } from "node:fs";
13
13
  import { join } from "node:path";
14
14
  import { tmpdir } from "node:os";
15
- import { syncWorktreeStateBack } from "../auto-worktree.ts";
15
+ import { syncGsdStateToWorktree, syncWorktreeStateBack } from "../auto-worktree-sync.ts";
16
16
 
17
17
  test("#2684: syncWorktreeStateBack does not overwrite project PREFERENCES.md", () => {
18
18
  const mainBase = mkdtempSync(join(tmpdir(), "gsd-wt-prefs-main-"));
@@ -58,8 +58,6 @@ test("syncGsdStateToWorktree copies canonical PREFERENCES.md", async () => {
58
58
  "---\nversion: 1\n---\n\npost_unit_hooks:\n - name: notify\n command: echo done\n",
59
59
  );
60
60
 
61
- // Import and call syncGsdStateToWorktree
62
- const { syncGsdStateToWorktree } = await import("../auto-worktree.ts");
63
61
  syncGsdStateToWorktree(srcBase, dstBase);
64
62
 
65
63
  // Verify PREFERENCES.md was copied
@@ -93,7 +91,6 @@ test("syncGsdStateToWorktree falls back to legacy lowercase preferences.md", asy
93
91
  "---\nversion: 1\n---\n\ngit:\n auto_push: true\n",
94
92
  );
95
93
 
96
- const { syncGsdStateToWorktree } = await import("../auto-worktree.ts");
97
94
  const result = syncGsdStateToWorktree(srcBase, dstBase);
98
95
 
99
96
  const copiedEntries = readdirSync(dstGsd)
@@ -79,6 +79,11 @@ const PREF_SAMPLE_VALUES: Record<string, unknown> = {
79
79
  min_request_interval_ms: 250,
80
80
  stale_commit_threshold_minutes: 15,
81
81
  context_management: { enabled: true },
82
+ tool_call_loop_guard: {
83
+ enabled: true,
84
+ identical_args: { enabled: true, max_consecutive_calls: 4 },
85
+ repeated_tool: { enabled: true, default_cap: 6, repeatable_cap: 15, exempt_tools: ["ctx_execute"] },
86
+ },
82
87
  experimental: { rtk: true },
83
88
  codebase: { indexing: "auto" },
84
89
  slice_parallel: { enabled: true, max_workers: 2 },
@@ -10,6 +10,7 @@ import {
10
10
  compileUnitContextContract,
11
11
  compileUnitToolContract,
12
12
  getUnitWorkflowDispatchReadinessError,
13
+ getUnitWorkflowDispatchReadinessErrorForModel,
13
14
  } from "../tool-contract.js";
14
15
  import { shouldBlockAutoUnitToolCall } from "../auto-unit-tool-scope.js";
15
16
  import type { GSDState } from "../types.js";
@@ -111,6 +112,25 @@ test("Tool Contract derives dispatch readiness from Unit workflow tools", () =>
111
112
  assert.equal(error, null);
112
113
  });
113
114
 
115
+ test("Tool Contract derives dispatch readiness from selected model context", () => {
116
+ const authLookups: string[] = [];
117
+ const error = getUnitWorkflowDispatchReadinessErrorForModel({
118
+ model: { provider: "claude-code" },
119
+ fallbackModel: { provider: "openai-codex", baseUrl: "local://claude-code" },
120
+ getProviderAuthMode(provider) {
121
+ authLookups.push(provider);
122
+ return provider === "claude-code" ? "externalCli" : "oauth";
123
+ },
124
+ unitType: "plan-slice",
125
+ projectRoot: "/tmp/project",
126
+ env: { GSD_WORKFLOW_MCP_COMMAND: "node" },
127
+ surface: "auto-mode",
128
+ });
129
+
130
+ assert.equal(error, null);
131
+ assert.deepEqual(authLookups, ["claude-code"]);
132
+ });
133
+
114
134
  test("Unit Context Contract exposes prompt context without workflow tool surface", () => {
115
135
  const result = compileUnitContextContract("execute-task");
116
136
 
@@ -12,11 +12,9 @@ import { join, sep } from "node:path";
12
12
  import { tmpdir } from "node:os";
13
13
  import { execSync } from "node:child_process";
14
14
 
15
- import {
16
- createAutoWorktree,
17
- teardownAutoWorktree,
18
- mergeMilestoneToMain,
19
- } from "../auto-worktree.ts";
15
+ import { createAutoWorktree } from "../auto-worktree-creation.ts";
16
+ import { mergeMilestoneToMain } from "../auto-worktree-merge.ts";
17
+ import { teardownAutoWorktree } from "../auto-worktree-teardown.ts";
20
18
  import { _resetServiceCache } from "../worktree.ts";
21
19
  import { _clearGsdRootCache } from "../paths.ts";
22
20
 
@@ -14,7 +14,8 @@ import { join } from "node:path";
14
14
  import { tmpdir } from "node:os";
15
15
  import { execSync } from "node:child_process";
16
16
 
17
- import { createAutoWorktree, mergeMilestoneToMain } from "../auto-worktree.ts";
17
+ import { createAutoWorktree } from "../auto-worktree-creation.ts";
18
+ import { mergeMilestoneToMain } from "../auto-worktree-merge.ts";
18
19
  import { _resetServiceCache } from "../worktree.ts";
19
20
  import { _clearGsdRootCache } from "../paths.ts";
20
21
 
@@ -28,7 +28,8 @@ import { join } from "node:path";
28
28
  import { tmpdir } from "node:os";
29
29
  import { execSync } from "node:child_process";
30
30
 
31
- import { createAutoWorktree, mergeMilestoneToMain } from "../auto-worktree.ts";
31
+ import { createAutoWorktree } from "../auto-worktree-creation.ts";
32
+ import { mergeMilestoneToMain } from "../auto-worktree-merge.ts";
32
33
  import { _resetServiceCache } from "../worktree.ts";
33
34
  import { _clearGsdRootCache } from "../paths.ts";
34
35
 
@@ -8,7 +8,7 @@ import { mkdtempSync, mkdirSync, existsSync, readFileSync, rmSync, writeFileSync
8
8
  import { join } from "node:path";
9
9
  import { tmpdir } from "node:os";
10
10
  import { appendOverride, loadActiveOverrides } from "../files.ts";
11
- import { getAutoWorktreePath } from "../auto-worktree.ts";
11
+ import { getAutoWorktreePath } from "../auto-worktree-path-resolution.ts";
12
12
 
13
13
  describe("steer worktree path resolution (#3476)", () => {
14
14
  let projectRoot: string;
@@ -8,7 +8,7 @@ import { existsSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:
8
8
  import { join } from "node:path";
9
9
  import { tmpdir } from "node:os";
10
10
 
11
- import { syncWorktreeStateBack } from "../auto-worktree.ts";
11
+ import { syncWorktreeStateBack } from "../auto-worktree-sync.ts";
12
12
 
13
13
  describe("syncWorktreeStateBack does not copy worktree milestone projections", () => {
14
14
  it("copies root diagnostics but leaves milestone markdown directories behind", () => {