@opengsd/gsd-pi 1.2.0-dev.4813ead6 → 1.2.0-dev.5457a158

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (342) hide show
  1. package/dist/cli-style.d.ts +17 -0
  2. package/dist/cli-style.js +28 -0
  3. package/dist/cli.js +1 -1
  4. package/dist/headless-events.d.ts +4 -2
  5. package/dist/headless-events.js +7 -29
  6. package/dist/models-resolver.d.ts +3 -13
  7. package/dist/models-resolver.js +3 -22
  8. package/dist/resource-loader.js +2 -14
  9. package/dist/resources/.managed-resources-content-hash +1 -1
  10. package/dist/resources/extensions/bg-shell/utilities.js +5 -2
  11. package/dist/resources/extensions/claude-code-cli/models.js +9 -0
  12. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +35 -4
  13. package/dist/resources/extensions/gsd/auto/orchestrator.js +33 -4
  14. package/dist/resources/extensions/gsd/auto/phases.js +6 -1
  15. package/dist/resources/extensions/gsd/auto-post-unit.js +19 -8
  16. package/dist/resources/extensions/gsd/auto-prompts.js +3 -0
  17. package/dist/resources/extensions/gsd/auto-start.js +12 -14
  18. package/dist/resources/extensions/gsd/auto-tool-tracking.js +18 -0
  19. package/dist/resources/extensions/gsd/auto-unit-tool-scope.js +7 -16
  20. package/dist/resources/extensions/gsd/auto-worktree-repair.js +10 -2
  21. package/dist/resources/extensions/gsd/auto-worktree.js +35 -352
  22. package/dist/resources/extensions/gsd/auto.js +8 -20
  23. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +3 -2
  24. package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +9 -6
  25. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +86 -6
  26. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +30 -4
  27. package/dist/resources/extensions/gsd/branch-patterns.js +2 -0
  28. package/dist/resources/extensions/gsd/captures.js +5 -15
  29. package/dist/resources/extensions/gsd/closeout-recovery.js +3 -2
  30. package/dist/resources/extensions/gsd/commands/catalog.js +6 -62
  31. package/dist/resources/extensions/gsd/crash-recovery.js +4 -12
  32. package/dist/resources/extensions/gsd/db/engine.js +755 -0
  33. package/dist/resources/extensions/gsd/db/queries.js +372 -0
  34. package/dist/resources/extensions/gsd/db/sql-constants.js +11 -0
  35. package/dist/resources/extensions/gsd/db/writers/cascades.js +194 -0
  36. package/dist/resources/extensions/gsd/db/writers/import-restore.js +182 -0
  37. package/dist/resources/extensions/gsd/db/writers/memory.js +149 -0
  38. package/dist/resources/extensions/gsd/db/writers/reconcile.js +458 -0
  39. package/dist/resources/extensions/gsd/db/writers/status.js +70 -0
  40. package/dist/resources/extensions/gsd/doctor-environment.js +5 -11
  41. package/dist/resources/extensions/gsd/doctor-format.js +9 -6
  42. package/dist/resources/extensions/gsd/doctor-git-checks.js +4 -3
  43. package/dist/resources/extensions/gsd/doctor-runtime-checks.js +21 -16
  44. package/dist/resources/extensions/gsd/error-classifier.js +9 -0
  45. package/dist/resources/extensions/gsd/git-service.js +1 -0
  46. package/dist/resources/extensions/gsd/gitignore.js +3 -0
  47. package/dist/resources/extensions/gsd/gsd-db.js +171 -2048
  48. package/dist/resources/extensions/gsd/guidance.js +98 -0
  49. package/dist/resources/extensions/gsd/guided-flow.js +51 -5
  50. package/dist/resources/extensions/gsd/mcp-tool-name.js +5 -13
  51. package/dist/resources/extensions/gsd/memory-consolidation-scanner.js +1 -1
  52. package/dist/resources/extensions/gsd/migrate/safety.js +20 -9
  53. package/dist/resources/extensions/gsd/migration-auto-check.js +24 -3
  54. package/dist/resources/extensions/gsd/model-cost-table.js +1 -0
  55. package/dist/resources/extensions/gsd/model-router.js +3 -0
  56. package/dist/resources/extensions/gsd/notification-store.js +11 -4
  57. package/dist/resources/extensions/gsd/parallel-merge.js +14 -11
  58. package/dist/resources/extensions/gsd/parallel-monitor-overlay.js +11 -7
  59. package/dist/resources/extensions/gsd/paths.js +37 -24
  60. package/dist/resources/extensions/gsd/pre-execution-checks.js +91 -3
  61. package/dist/resources/extensions/gsd/preferences-models.js +12 -46
  62. package/dist/resources/extensions/gsd/preferences.js +14 -0
  63. package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  64. package/dist/resources/extensions/gsd/prompts/refine-slice.md +1 -1
  65. package/dist/resources/extensions/gsd/prompts/replan-slice.md +1 -1
  66. package/dist/resources/extensions/gsd/provider-error-guidance.js +1 -5
  67. package/dist/resources/extensions/gsd/provider-switch-observer.js +1 -1
  68. package/dist/resources/extensions/gsd/publication.js +87 -0
  69. package/dist/resources/extensions/gsd/recovery-classification.js +41 -87
  70. package/dist/resources/extensions/gsd/safety/evidence-collector.js +37 -4
  71. package/dist/resources/extensions/gsd/safety/evidence-cross-ref.js +7 -2
  72. package/dist/resources/extensions/gsd/safety/file-change-validator.js +10 -0
  73. package/dist/resources/extensions/gsd/state-transition-matrix.js +38 -0
  74. package/dist/resources/extensions/gsd/state.js +1 -20
  75. package/dist/resources/extensions/gsd/status-guards.js +56 -8
  76. package/dist/resources/extensions/gsd/stop-notice.js +57 -0
  77. package/dist/resources/extensions/gsd/tool-surface-readiness.js +56 -0
  78. package/dist/resources/extensions/gsd/tools/complete-slice.js +24 -43
  79. package/dist/resources/extensions/gsd/tools/exec-tool.js +5 -8
  80. package/dist/resources/extensions/gsd/tools/plan-slice.js +12 -6
  81. package/dist/resources/extensions/gsd/tools/reopen-milestone.js +11 -29
  82. package/dist/resources/extensions/gsd/tools/reopen-slice.js +14 -33
  83. package/dist/resources/extensions/gsd/tools/skip-slice.js +18 -36
  84. package/dist/resources/extensions/gsd/undo.js +8 -7
  85. package/dist/resources/extensions/gsd/unit-closeout.js +138 -0
  86. package/dist/resources/extensions/gsd/unit-context-composer.js +9 -1
  87. package/dist/resources/extensions/gsd/unit-context-manifest.js +4 -27
  88. package/dist/resources/extensions/gsd/unit-registry.js +350 -0
  89. package/dist/resources/extensions/gsd/unit-tool-contracts.js +9 -182
  90. package/dist/resources/extensions/gsd/workflow-tool-surface.js +1 -1
  91. package/dist/resources/extensions/gsd/worktree-git-recovery.js +293 -0
  92. package/dist/resources/extensions/gsd/worktree-lifecycle.js +9 -1
  93. package/dist/resources/extensions/gsd/worktree-manager.js +45 -28
  94. package/dist/resources/extensions/gsd/worktree-placement.js +59 -0
  95. package/dist/resources/extensions/gsd/worktree-reentry.js +12 -8
  96. package/dist/resources/extensions/gsd/worktree-root.js +28 -6
  97. package/dist/resources/extensions/gsd/worktree-safety.js +8 -5
  98. package/dist/resources/extensions/gsd/worktree-session-state.js +12 -11
  99. package/dist/resources/skills/gsd-browser/SKILL.md +1 -1
  100. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  101. package/dist/web/standalone/.next/BUILD_ID +1 -1
  102. package/dist/web/standalone/.next/app-path-routes-manifest.json +6 -6
  103. package/dist/web/standalone/.next/build-manifest.json +2 -2
  104. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  105. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  106. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  107. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  108. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  109. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  110. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  111. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  112. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  113. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  114. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  115. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  116. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  117. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  118. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  119. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  120. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  121. package/dist/web/standalone/.next/server/app/api/boot/route.js.nft.json +1 -1
  122. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js.nft.json +1 -1
  123. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js.nft.json +1 -1
  124. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js.nft.json +1 -1
  125. package/dist/web/standalone/.next/server/app/api/captures/route.js.nft.json +1 -1
  126. package/dist/web/standalone/.next/server/app/api/cleanup/route.js.nft.json +1 -1
  127. package/dist/web/standalone/.next/server/app/api/doctor/route.js.nft.json +1 -1
  128. package/dist/web/standalone/.next/server/app/api/export-data/route.js.nft.json +1 -1
  129. package/dist/web/standalone/.next/server/app/api/files/route.js.nft.json +1 -1
  130. package/dist/web/standalone/.next/server/app/api/forensics/route.js.nft.json +1 -1
  131. package/dist/web/standalone/.next/server/app/api/git/route.js.nft.json +1 -1
  132. package/dist/web/standalone/.next/server/app/api/history/route.js.nft.json +1 -1
  133. package/dist/web/standalone/.next/server/app/api/hooks/route.js.nft.json +1 -1
  134. package/dist/web/standalone/.next/server/app/api/inspect/route.js.nft.json +1 -1
  135. package/dist/web/standalone/.next/server/app/api/knowledge/route.js.nft.json +1 -1
  136. package/dist/web/standalone/.next/server/app/api/live-state/route.js.nft.json +1 -1
  137. package/dist/web/standalone/.next/server/app/api/mcp-connections/route.js.nft.json +1 -1
  138. package/dist/web/standalone/.next/server/app/api/notifications/route.js.nft.json +1 -1
  139. package/dist/web/standalone/.next/server/app/api/onboarding/route.js.nft.json +1 -1
  140. package/dist/web/standalone/.next/server/app/api/projects/route.js.nft.json +1 -1
  141. package/dist/web/standalone/.next/server/app/api/recovery/route.js.nft.json +1 -1
  142. package/dist/web/standalone/.next/server/app/api/session/browser/route.js.nft.json +1 -1
  143. package/dist/web/standalone/.next/server/app/api/session/command/route.js.nft.json +1 -1
  144. package/dist/web/standalone/.next/server/app/api/session/events/route.js.nft.json +1 -1
  145. package/dist/web/standalone/.next/server/app/api/session/manage/route.js.nft.json +1 -1
  146. package/dist/web/standalone/.next/server/app/api/settings-data/route.js.nft.json +1 -1
  147. package/dist/web/standalone/.next/server/app/api/shutdown/route.js.nft.json +1 -1
  148. package/dist/web/standalone/.next/server/app/api/skill-health/route.js.nft.json +1 -1
  149. package/dist/web/standalone/.next/server/app/api/steer/route.js.nft.json +1 -1
  150. package/dist/web/standalone/.next/server/app/api/switch-root/route.js.nft.json +1 -1
  151. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js.nft.json +1 -1
  152. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js.nft.json +1 -1
  153. package/dist/web/standalone/.next/server/app/api/undo/route.js.nft.json +1 -1
  154. package/dist/web/standalone/.next/server/app/api/visualizer/route.js.nft.json +1 -1
  155. package/dist/web/standalone/.next/server/app/index.html +1 -1
  156. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  157. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  158. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  159. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  160. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  161. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  162. package/dist/web/standalone/.next/server/app-paths-manifest.json +6 -6
  163. package/dist/web/standalone/.next/server/chunks/5124.js +1 -1
  164. package/dist/web/standalone/.next/server/chunks/{5047.js → 5942.js} +2 -2
  165. package/dist/web/standalone/.next/server/chunks/8357.js +1 -1
  166. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  167. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  168. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  169. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  170. package/dist/worktree-cli.js +3 -6
  171. package/dist/worktree-status-banner.js +7 -11
  172. package/package.json +1 -1
  173. package/packages/cloud-mcp-gateway/package.json +2 -2
  174. package/packages/contracts/dist/workflow.d.ts +4 -0
  175. package/packages/contracts/dist/workflow.d.ts.map +1 -1
  176. package/packages/contracts/dist/workflow.js.map +1 -1
  177. package/packages/contracts/package.json +1 -1
  178. package/packages/daemon/package.json +4 -4
  179. package/packages/gsd-agent-core/package.json +5 -5
  180. package/packages/gsd-agent-modes/package.json +7 -7
  181. package/packages/mcp-server/dist/cli.js +6 -3
  182. package/packages/mcp-server/dist/cli.js.map +1 -1
  183. package/packages/mcp-server/dist/workflow-tools.d.ts +8 -0
  184. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  185. package/packages/mcp-server/dist/workflow-tools.js +46 -21
  186. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  187. package/packages/mcp-server/package.json +3 -3
  188. package/packages/native/package.json +1 -1
  189. package/packages/pi-agent-core/package.json +1 -1
  190. package/packages/pi-ai/dist/image-models.generated.d.ts +0 -30
  191. package/packages/pi-ai/dist/image-models.generated.d.ts.map +1 -1
  192. package/packages/pi-ai/dist/image-models.generated.js +0 -30
  193. package/packages/pi-ai/dist/image-models.generated.js.map +1 -1
  194. package/packages/pi-ai/dist/models.generated.d.ts +361 -255
  195. package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
  196. package/packages/pi-ai/dist/models.generated.js +311 -256
  197. package/packages/pi-ai/dist/models.generated.js.map +1 -1
  198. package/packages/pi-ai/package.json +1 -1
  199. package/packages/pi-coding-agent/dist/core/capability-patches.d.ts.map +1 -1
  200. package/packages/pi-coding-agent/dist/core/capability-patches.js +3 -1
  201. package/packages/pi-coding-agent/dist/core/capability-patches.js.map +1 -1
  202. package/packages/pi-coding-agent/package.json +7 -7
  203. package/packages/pi-tui/package.json +2 -2
  204. package/packages/rpc-client/package.json +2 -2
  205. package/pkg/package.json +1 -1
  206. package/src/resources/extensions/bg-shell/utilities.ts +5 -2
  207. package/src/resources/extensions/claude-code-cli/models.ts +9 -0
  208. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +37 -2
  209. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +28 -0
  210. package/src/resources/extensions/gsd/auto/loop-deps.ts +1 -1
  211. package/src/resources/extensions/gsd/auto/orchestrator.ts +39 -5
  212. package/src/resources/extensions/gsd/auto/phases.ts +10 -1
  213. package/src/resources/extensions/gsd/auto-post-unit.ts +25 -7
  214. package/src/resources/extensions/gsd/auto-prompts.ts +3 -0
  215. package/src/resources/extensions/gsd/auto-start.ts +12 -15
  216. package/src/resources/extensions/gsd/auto-tool-tracking.ts +19 -0
  217. package/src/resources/extensions/gsd/auto-unit-tool-scope.ts +10 -17
  218. package/src/resources/extensions/gsd/auto-worktree-repair.ts +13 -2
  219. package/src/resources/extensions/gsd/auto-worktree.ts +41 -364
  220. package/src/resources/extensions/gsd/auto.ts +20 -24
  221. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +3 -5
  222. package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +10 -6
  223. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +87 -6
  224. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +29 -3
  225. package/src/resources/extensions/gsd/branch-patterns.ts +3 -0
  226. package/src/resources/extensions/gsd/captures.ts +5 -16
  227. package/src/resources/extensions/gsd/closeout-recovery.ts +2 -1
  228. package/src/resources/extensions/gsd/commands/catalog.ts +6 -68
  229. package/src/resources/extensions/gsd/crash-recovery.ts +3 -9
  230. package/src/resources/extensions/gsd/db/engine.ts +809 -0
  231. package/src/resources/extensions/gsd/db/queries.ts +453 -0
  232. package/src/resources/extensions/gsd/db/sql-constants.ts +12 -0
  233. package/src/resources/extensions/gsd/db/writers/cascades.ts +237 -0
  234. package/src/resources/extensions/gsd/db/writers/import-restore.ts +310 -0
  235. package/src/resources/extensions/gsd/db/writers/memory.ts +220 -0
  236. package/src/resources/extensions/gsd/db/writers/reconcile.ts +500 -0
  237. package/src/resources/extensions/gsd/db/writers/status.ts +88 -0
  238. package/src/resources/extensions/gsd/doctor-environment.ts +5 -13
  239. package/src/resources/extensions/gsd/doctor-format.ts +12 -7
  240. package/src/resources/extensions/gsd/doctor-git-checks.ts +3 -3
  241. package/src/resources/extensions/gsd/doctor-runtime-checks.ts +22 -17
  242. package/src/resources/extensions/gsd/error-classifier.ts +11 -0
  243. package/src/resources/extensions/gsd/git-service.ts +1 -0
  244. package/src/resources/extensions/gsd/gitignore.ts +3 -0
  245. package/src/resources/extensions/gsd/gsd-db.ts +173 -2373
  246. package/src/resources/extensions/gsd/guidance.ts +139 -0
  247. package/src/resources/extensions/gsd/guided-flow.ts +50 -5
  248. package/src/resources/extensions/gsd/mcp-tool-name.ts +6 -11
  249. package/src/resources/extensions/gsd/memory-consolidation-scanner.ts +1 -1
  250. package/src/resources/extensions/gsd/migrate/safety.ts +18 -7
  251. package/src/resources/extensions/gsd/migration-auto-check.ts +28 -3
  252. package/src/resources/extensions/gsd/model-cost-table.ts +1 -0
  253. package/src/resources/extensions/gsd/model-router.ts +3 -0
  254. package/src/resources/extensions/gsd/notification-store.ts +26 -3
  255. package/src/resources/extensions/gsd/parallel-merge.ts +12 -9
  256. package/src/resources/extensions/gsd/parallel-monitor-overlay.ts +10 -7
  257. package/src/resources/extensions/gsd/paths.ts +42 -22
  258. package/src/resources/extensions/gsd/pre-execution-checks.ts +109 -3
  259. package/src/resources/extensions/gsd/preferences-models.ts +10 -46
  260. package/src/resources/extensions/gsd/preferences.ts +18 -0
  261. package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  262. package/src/resources/extensions/gsd/prompts/refine-slice.md +1 -1
  263. package/src/resources/extensions/gsd/prompts/replan-slice.md +1 -1
  264. package/src/resources/extensions/gsd/provider-error-guidance.ts +4 -9
  265. package/src/resources/extensions/gsd/provider-switch-observer.ts +1 -1
  266. package/src/resources/extensions/gsd/publication.ts +122 -0
  267. package/src/resources/extensions/gsd/recovery-classification.ts +47 -88
  268. package/src/resources/extensions/gsd/safety/evidence-collector.ts +36 -4
  269. package/src/resources/extensions/gsd/safety/evidence-cross-ref.ts +7 -2
  270. package/src/resources/extensions/gsd/safety/file-change-validator.ts +14 -0
  271. package/src/resources/extensions/gsd/state-transition-matrix.ts +42 -0
  272. package/src/resources/extensions/gsd/state.ts +4 -21
  273. package/src/resources/extensions/gsd/status-guards.ts +59 -8
  274. package/src/resources/extensions/gsd/stop-notice.ts +75 -0
  275. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +123 -0
  276. package/src/resources/extensions/gsd/tests/auto-paused-ui-cleanup.test.ts +3 -1
  277. package/src/resources/extensions/gsd/tests/auto-post-unit-evidence-crossref-4909.test.ts +46 -0
  278. package/src/resources/extensions/gsd/tests/auto-worktree-registry.test.ts +2 -2
  279. package/src/resources/extensions/gsd/tests/auto-worktree-repair.test.ts +4 -2
  280. package/src/resources/extensions/gsd/tests/checkout-branch-stash-guard.test.ts +66 -1
  281. package/src/resources/extensions/gsd/tests/clear-stale-autostart.test.ts +44 -0
  282. package/src/resources/extensions/gsd/tests/commands-verdict.test.ts +8 -7
  283. package/src/resources/extensions/gsd/tests/evidence-xref-gsd-exec.test.ts +157 -0
  284. package/src/resources/extensions/gsd/tests/file-change-validator.test.ts +33 -1
  285. package/src/resources/extensions/gsd/tests/guidance.test.ts +125 -0
  286. package/src/resources/extensions/gsd/tests/integration/auto-worktree-milestone-merge.test.ts +51 -4
  287. package/src/resources/extensions/gsd/tests/integration/auto-worktree.test.ts +54 -1
  288. package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +3 -2
  289. package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +85 -1
  290. package/src/resources/extensions/gsd/tests/notification-store.test.ts +32 -0
  291. package/src/resources/extensions/gsd/tests/pre-execution-checks.test.ts +193 -1
  292. package/src/resources/extensions/gsd/tests/provider-error-guidance.test.ts +3 -3
  293. package/src/resources/extensions/gsd/tests/publication.test.ts +120 -0
  294. package/src/resources/extensions/gsd/tests/recovery-classification-illegal-transition.test.ts +30 -0
  295. package/src/resources/extensions/gsd/tests/register-hooks-depth-verification.test.ts +248 -1
  296. package/src/resources/extensions/gsd/tests/runtime-invariant-modules.test.ts +1 -0
  297. package/src/resources/extensions/gsd/tests/safety-harness-false-positives.test.ts +38 -0
  298. package/src/resources/extensions/gsd/tests/session-switch-clears-pending-autostart.test.ts +108 -0
  299. package/src/resources/extensions/gsd/tests/single-writer-invariant.test.ts +43 -6
  300. package/src/resources/extensions/gsd/tests/state-transition-matrix.test.ts +36 -0
  301. package/src/resources/extensions/gsd/tests/status-guards.test.ts +38 -0
  302. package/src/resources/extensions/gsd/tests/stop-notice.test.ts +70 -0
  303. package/src/resources/extensions/gsd/tests/tool-invocation-error-loop-break.test.ts +8 -0
  304. package/src/resources/extensions/gsd/tests/tool-surface-readiness.test.ts +155 -0
  305. package/src/resources/extensions/gsd/tests/unit-closeout.test.ts +209 -0
  306. package/src/resources/extensions/gsd/tests/unit-context-composer.test.ts +23 -2
  307. package/src/resources/extensions/gsd/tests/unit-registry.test.ts +163 -0
  308. package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +2 -2
  309. package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +2 -2
  310. package/src/resources/extensions/gsd/tests/worktree-lifecycle.test.ts +41 -4
  311. package/src/resources/extensions/gsd/tests/worktree-manager.test.ts +22 -1
  312. package/src/resources/extensions/gsd/tests/worktree-placement.test.ts +113 -0
  313. package/src/resources/extensions/gsd/tests/worktree-reentry.test.ts +1 -1
  314. package/src/resources/extensions/gsd/tests/worktree-safety.test.ts +3 -1
  315. package/src/resources/extensions/gsd/tests/worktree-symlink-removal.test.ts +12 -6
  316. package/src/resources/extensions/gsd/tests/worktree-teardown-safety.test.ts +2 -2
  317. package/src/resources/extensions/gsd/tests/write-gate.test.ts +42 -0
  318. package/src/resources/extensions/gsd/tool-surface-readiness.ts +76 -0
  319. package/src/resources/extensions/gsd/tools/complete-slice.ts +23 -58
  320. package/src/resources/extensions/gsd/tools/exec-tool.ts +5 -8
  321. package/src/resources/extensions/gsd/tools/plan-slice.ts +12 -6
  322. package/src/resources/extensions/gsd/tools/reopen-milestone.ts +11 -38
  323. package/src/resources/extensions/gsd/tools/reopen-slice.ts +14 -42
  324. package/src/resources/extensions/gsd/tools/skip-slice.ts +18 -44
  325. package/src/resources/extensions/gsd/undo.ts +9 -8
  326. package/src/resources/extensions/gsd/unit-closeout.ts +201 -0
  327. package/src/resources/extensions/gsd/unit-context-composer.ts +12 -1
  328. package/src/resources/extensions/gsd/unit-context-manifest.ts +4 -28
  329. package/src/resources/extensions/gsd/unit-registry.ts +425 -0
  330. package/src/resources/extensions/gsd/unit-tool-contracts.ts +27 -192
  331. package/src/resources/extensions/gsd/workflow-tool-surface.ts +4 -1
  332. package/src/resources/extensions/gsd/worktree-git-recovery.ts +314 -0
  333. package/src/resources/extensions/gsd/worktree-lifecycle.ts +10 -1
  334. package/src/resources/extensions/gsd/worktree-manager.ts +47 -28
  335. package/src/resources/extensions/gsd/worktree-placement.ts +63 -0
  336. package/src/resources/extensions/gsd/worktree-reentry.ts +10 -7
  337. package/src/resources/extensions/gsd/worktree-root.ts +29 -6
  338. package/src/resources/extensions/gsd/worktree-safety.ts +8 -5
  339. package/src/resources/extensions/gsd/worktree-session-state.ts +11 -11
  340. package/src/resources/skills/gsd-browser/SKILL.md +1 -1
  341. /package/dist/web/standalone/.next/static/{tkLHUSzPA2kMmWz4DmGwI → 2p9Rv9pQflAxCBbGVI2vb}/_buildManifest.js +0 -0
  342. /package/dist/web/standalone/.next/static/{tkLHUSzPA2kMmWz4DmGwI → 2p9Rv9pQflAxCBbGVI2vb}/_ssgManifest.js +0 -0
@@ -8,25 +8,27 @@
8
8
  * manages create, enter, detect, and teardown for auto-mode worktrees.
9
9
  */
10
10
  import { existsSync, cpSync, readFileSync, readdirSync, mkdirSync, realpathSync, rmSync, unlinkSync, lstatSync as lstatSyncFn, } from "node:fs";
11
- import { dirname, isAbsolute, join, relative, resolve, sep as pathSep } from "node:path";
11
+ import { dirname, isAbsolute, join, relative } from "node:path";
12
12
  import { GSDError, GSD_IO_ERROR, GSD_GIT_ERROR } from "./errors.js";
13
13
  import { reconcileWorktreeDb, isDbAvailable, getMilestone, getMilestoneSlices, getSliceTasks, } from "./gsd-db.js";
14
14
  import { closeWorkflowDatabase, getWorkflowDatabasePath, openWorkflowDatabasePath, } from "./db-workspace.js";
15
15
  import { execFileSync } from "node:child_process";
16
16
  import { gsdRoot, resolveGsdPathContract } from "./paths.js";
17
- import { createWorktree, removeWorktree, resolveGitDir, worktreePath, isInsideWorktreesDir, } from "./worktree-manager.js";
17
+ import { createWorktree, removeWorktree, worktreePath, isInsideWorktreesDir, } from "./worktree-manager.js";
18
+ import { worktreePathFor } from "./worktree-placement.js";
18
19
  import { detectWorktreeName, nudgeGitBranchCache, } from "./worktree.js";
19
- import { isGsdWorktreePath, normalizeWorktreePathForCompare, resolveWorktreeProjectRoot, } from "./worktree-root.js";
20
+ import { isGsdWorktreePath, projectRootFromWorktreePath, normalizeWorktreePathForCompare, resolveWorktreeProjectRoot, } from "./worktree-root.js";
20
21
  import { autoResolveSafeConflictPaths } from "./git-conflict-resolve.js";
21
22
  import { MergeConflictError, readIntegrationBranch, resolveMilestoneIntegrationBranch, RUNTIME_EXCLUSION_PATHS } from "./git-service.js";
22
- import { buildPullRequestEvidence, createDraftPullRequestFromEvidence, } from "./pull-request-process.js";
23
+ import { publishMilestone } from "./publication.js";
23
24
  import { debugLog } from "./debug-logger.js";
24
25
  import { logWarning, logError } from "./workflow-logger.js";
26
+ import { checkoutBranchWithStashGuard, cleanupConflictState, gsdJsonlFilesWithConflictMarkers, hasConflictMarkers, popStashByRef, removeMergeStateFiles, stashAlreadyExistsFilesFromError, stashRefFromError, } from "./worktree-git-recovery.js";
25
27
  import { loadEffectiveGSDPreferences } from "./preferences.js";
26
28
  import { MILESTONE_ID_RE } from "./milestone-ids.js";
27
29
  import { runWorktreePostCreateHook } from "./worktree-post-create-hook.js";
28
30
  import { classifyProject } from "./detection.js";
29
- import { nativeGetCurrentBranch, nativeDetectMainBranch, nativeWorkingTreeStatus, nativeAddAllWithExclusions, nativeCommit, nativeCheckoutBranch, nativeMergeSquash, nativeMergeRegular, nativeConflictFiles, nativeAddPaths, nativeRmForce, nativeBranchDelete, nativeBranchForceReset, nativeBranchExists, nativeDiffNumstat, nativeUpdateRef, nativeIsAncestor, nativeMergeAbort, nativeWorktreeList, nativeLsFiles, } from "./native-git-bridge.js";
31
+ import { nativeGetCurrentBranch, nativeDetectMainBranch, nativeWorkingTreeStatus, nativeAddAllWithExclusions, nativeCommit, nativeCheckoutBranch, nativeMergeSquash, nativeMergeRegular, nativeConflictFiles, nativeAddPaths, nativeRmForce, nativeBranchDelete, nativeBranchForceReset, nativeBranchExists, nativeDiffNumstat, nativeUpdateRef, nativeIsAncestor, nativeWorktreeList, nativeLsFiles, } from "./native-git-bridge.js";
30
32
  import { CLOSEOUT_CONSISTENCY_BLOCKED_REASON, } from "./closeout-consistency-gate.js";
31
33
  import { formatCloseoutProofBlock, proveMilestoneCloseout, } from "./milestone-closeout-proof.js";
32
34
  import { gsdHome } from "./gsd-home.js";
@@ -61,111 +63,6 @@ const ROOT_STATE_FILES = [
61
63
  // Back-sync (worktree → main) must NEVER overwrite the project root's copy
62
64
  // because the project root is authoritative for preferences (#2684).
63
65
  ];
64
- /**
65
- * Pop a stash entry by tracking the unique marker embedded in its message so
66
- * concurrent stash operations against the same project root cannot cause us to
67
- * pop the wrong entry.
68
- *
69
- * If `stashMarker` is null or no longer present in the stash list (e.g. a
70
- * concurrent process popped/dropped it), leaves the stash list untouched and
71
- * returns null.
72
- *
73
- * Throws on pop failure so callers can handle conflict cases the same way
74
- * they would with the prior `git stash pop` form. When throwing after a
75
- * targeted pop attempt, the error is annotated with the targeted stash ref.
76
- *
77
- * (Issue #4980 HIGH-6)
78
- */
79
- function popStashByRef(basePath, stashMarker) {
80
- let popArg = null;
81
- if (stashMarker) {
82
- try {
83
- const list = execFileSync("git", ["stash", "list", "--format=%gd%x00%s"], {
84
- cwd: basePath,
85
- stdio: ["ignore", "pipe", "pipe"],
86
- encoding: "utf-8",
87
- }).trim().split("\n").filter(Boolean);
88
- for (const entry of list) {
89
- const [ref, subject] = entry.split("\0");
90
- if (ref && subject?.includes(stashMarker)) {
91
- popArg = ref;
92
- break;
93
- }
94
- }
95
- }
96
- catch (err) {
97
- logWarning("worktree", `stash list lookup failed; leaving stash untouched: ${err instanceof Error ? err.message : String(err)}`);
98
- }
99
- }
100
- if (!popArg) {
101
- logWarning("worktree", "recorded stash entry could not be resolved; skipping automatic pop");
102
- return null;
103
- }
104
- try {
105
- execFileSync("git", ["stash", "pop", popArg], {
106
- cwd: basePath,
107
- stdio: ["ignore", "pipe", "pipe"],
108
- encoding: "utf-8",
109
- });
110
- }
111
- catch (err) {
112
- if (err && typeof err === "object") {
113
- err.stashRef = popArg;
114
- }
115
- throw err;
116
- }
117
- return popArg;
118
- }
119
- /**
120
- * Extract a stash ref annotation injected by popStashByRef() when git stash
121
- * pop fails and we need to conditionally drop the exact stash entry later.
122
- */
123
- function stashRefFromError(err) {
124
- if (!err || typeof err !== "object")
125
- return null;
126
- const stashRef = err.stashRef;
127
- return typeof stashRef === "string" && stashRef.length > 0 ? stashRef : null;
128
- }
129
- function stashAlreadyExistsFilesFromError(err) {
130
- if (!err || typeof err !== "object")
131
- return [];
132
- const stderr = err.stderr;
133
- const stderrText = typeof stderr === "string"
134
- ? stderr
135
- : stderr instanceof Uint8Array
136
- ? Buffer.from(stderr).toString("utf-8")
137
- : "";
138
- const message = err instanceof Error ? err.message : String(err);
139
- const text = `${stderrText}\n${message}`;
140
- const files = new Set();
141
- for (const line of text.split("\n")) {
142
- const m = line.match(/^(.*?)\s+already exists, no checkout\s*$/i);
143
- if (!m)
144
- continue;
145
- const filePath = m[1]?.trim();
146
- if (filePath)
147
- files.add(filePath);
148
- }
149
- return [...files];
150
- }
151
- /**
152
- * Detect whether an on-disk file still contains unresolved merge conflict
153
- * markers from a failed stash-pop or merge attempt.
154
- *
155
- * Returns false when the file cannot be read.
156
- */
157
- function hasConflictMarkers(filePath) {
158
- try {
159
- const content = readFileSync(filePath, "utf-8");
160
- return content.includes("<<<<<<<") && content.includes("=======") && content.includes(">>>>>>>");
161
- }
162
- catch {
163
- return false;
164
- }
165
- }
166
- function gsdJsonlFilesWithConflictMarkers(basePath) {
167
- return nativeLsFiles(basePath, ".gsd/*.jsonl").filter((f) => hasConflictMarkers(join(basePath, f)));
168
- }
169
66
  /**
170
67
  * Check if two filesystem paths resolve to the same real location.
171
68
  * Returns false if either path cannot be resolved (e.g. doesn't exist).
@@ -286,19 +183,6 @@ function gitPathspecForWorktreePath(basePath, targetPath) {
286
183
  export function _gitPathspecForWorktreePath(basePath, targetPath) {
287
184
  return gitPathspecForWorktreePath(basePath, targetPath);
288
185
  }
289
- function gitRemoteExists(basePath, remote) {
290
- try {
291
- execFileSync("git", ["remote", "get-url", remote], {
292
- cwd: basePath,
293
- stdio: ["ignore", "pipe", "pipe"],
294
- encoding: "utf-8",
295
- });
296
- return true;
297
- }
298
- catch {
299
- return false;
300
- }
301
- }
302
186
  function findRegularMergeChangedPaths(basePath, milestoneBranch, mainBranch) {
303
187
  const changedPaths = new Set();
304
188
  let mergeLog = "";
@@ -421,51 +305,6 @@ export const SAFE_AUTO_RESOLVE_PATTERNS = [
421
305
  * Covers `.gsd/` state files and common build artifacts. */
422
306
  export const isSafeToAutoResolve = (filePath) => filePath.startsWith(".gsd/") ||
423
307
  SAFE_AUTO_RESOLVE_PATTERNS.some((re) => re.test(filePath));
424
- function removeMergeStateFiles(basePath, contextLabel) {
425
- try {
426
- for (const f of ["SQUASH_MSG", "MERGE_MSG", "MERGE_MODE", "MERGE_HEAD", "AUTO_MERGE"]) {
427
- const rawPath = execFileSync("git", ["rev-parse", "--git-path", f], {
428
- cwd: basePath,
429
- stdio: ["ignore", "pipe", "pipe"],
430
- encoding: "utf-8",
431
- }).trim();
432
- const p = rawPath.length > 0
433
- ? (isAbsolute(rawPath) ? rawPath : resolve(basePath, rawPath))
434
- : join(resolveGitDir(basePath), f);
435
- if (existsSync(p))
436
- unlinkSync(p);
437
- }
438
- }
439
- catch (err) {
440
- logError("worktree", `${contextLabel} merge state cleanup failed: ${err instanceof Error ? err.message : String(err)}`);
441
- }
442
- }
443
- function cleanupConflictState(basePath) {
444
- // Merge conflicts can leave unmerged index entries; merge-abort alone is not
445
- // enough for squash merges (MERGE_HEAD is never written). Reset the merge
446
- // index, then remove merge message files that native/libgit2 paths may have
447
- // created.
448
- try {
449
- nativeMergeAbort(basePath);
450
- }
451
- catch (err) {
452
- // MERGE_HEAD absent (squash merge path) — abort is a no-op, which is fine.
453
- debugLog("conflict-cleanup:merge-abort-skipped", {
454
- error: err instanceof Error ? err.message : String(err),
455
- });
456
- }
457
- try {
458
- execFileSync("git", ["reset", "--merge"], {
459
- cwd: basePath,
460
- stdio: ["ignore", "pipe", "pipe"],
461
- encoding: "utf-8",
462
- });
463
- }
464
- catch (err) {
465
- logError("worktree", `git reset --merge failed after merge conflict: ${err instanceof Error ? err.message : String(err)}`);
466
- }
467
- removeMergeStateFiles(basePath, "conflict");
468
- }
469
308
  // ─── Dispatch-Level Sync (project root ↔ worktree) ──────────────────────────
470
309
  /**
471
310
  * Sync milestone artifacts from project root INTO worktree before deriveState.
@@ -539,19 +378,9 @@ export function checkResourcesStale(versionOnStart) {
539
378
  * Returns the corrected base path.
540
379
  */
541
380
  export function escapeStaleWorktree(base) {
542
- // Direct layout: /.gsd/worktrees/
543
- const directMarker = `${pathSep}.gsd${pathSep}worktrees${pathSep}`;
544
- let idx = base.indexOf(directMarker);
545
- if (idx === -1) {
546
- // Symlink-resolved layout: /.gsd/projects/<hash>/worktrees/
547
- const symlinkRe = new RegExp(`\\${pathSep}\\.gsd\\${pathSep}projects\\${pathSep}[a-f0-9]+\\${pathSep}worktrees\\${pathSep}`);
548
- const match = base.match(symlinkRe);
549
- if (!match || match.index === undefined)
550
- return base;
551
- idx = match.index;
552
- }
553
- // base is inside .gsd/worktrees/<something> — extract the project root
554
- const projectRoot = base.slice(0, idx);
381
+ const projectRoot = projectRootFromWorktreePath(base);
382
+ if (projectRoot === null)
383
+ return base;
555
384
  // Guard: If the candidate project root's .gsd IS the user-level ~/.gsd,
556
385
  // the string-slice heuristic matched the wrong /.gsd/ boundary. This happens
557
386
  // when .gsd is a symlink into ~/.gsd/projects/<hash> and process.cwd()
@@ -908,109 +737,6 @@ export function enterBranchModeForMilestone(basePath, milestoneId) {
908
737
  }
909
738
  checkoutBranchWithStashGuard(basePath, branch, `enter-branch-mode:${milestoneId}`);
910
739
  }
911
- export function checkoutBranchWithStashGuard(basePath, branch, reason) {
912
- let stashMarker = null;
913
- let stashed = false;
914
- const status = nativeWorkingTreeStatus(basePath).trim();
915
- if (status.length > 0) {
916
- stashMarker = `gsd-checkout-stash:${reason}:${process.pid}:${Date.now()}:${process.hrtime.bigint().toString(36)}`;
917
- const stashListBefore = execFileSync("git", ["stash", "list"], {
918
- cwd: basePath,
919
- stdio: ["ignore", "pipe", "pipe"],
920
- encoding: "utf-8",
921
- });
922
- execFileSync("git", ["stash", "push", "--include-untracked", "-m", `gsd: checkout stash [${stashMarker}]`], {
923
- cwd: basePath,
924
- stdio: ["ignore", "pipe", "pipe"],
925
- encoding: "utf-8",
926
- });
927
- const stashListAfter = execFileSync("git", ["stash", "list"], {
928
- cwd: basePath,
929
- stdio: ["ignore", "pipe", "pipe"],
930
- encoding: "utf-8",
931
- });
932
- stashed = stashListAfter !== stashListBefore;
933
- }
934
- // Checkout and stash-restore are split so we can distinguish two failure
935
- // modes: (a) checkout failed → HEAD did not move, restore stash and rethrow;
936
- // (b) checkout succeeded but stash pop failed → HEAD moved to `branch` but
937
- // the working-tree changes remain in the stash list. We surface a distinct
938
- // error in case (b) so callers don't assume the branch switch was rolled back.
939
- try {
940
- nativeCheckoutBranch(basePath, branch);
941
- }
942
- catch (checkoutErr) {
943
- if (stashed) {
944
- try {
945
- popStashByRef(basePath, stashMarker);
946
- }
947
- catch (restoreErr) {
948
- logWarning("worktree", `git stash pop failed during checkout restore: ${restoreErr instanceof Error ? restoreErr.message : String(restoreErr)}`);
949
- }
950
- }
951
- throw checkoutErr;
952
- }
953
- if (stashed) {
954
- try {
955
- popStashByRef(basePath, stashMarker);
956
- }
957
- catch (popErr) {
958
- const msg = popErr instanceof Error ? popErr.message : String(popErr);
959
- const stderr = popErr && typeof popErr === "object"
960
- ? popErr.stderr
961
- : undefined;
962
- const stderrText = typeof stderr === "string"
963
- ? stderr
964
- : stderr instanceof Uint8Array
965
- ? Buffer.from(stderr).toString("utf-8")
966
- : "";
967
- const stashPopMessage = `${stderrText}\n${msg}`.trim();
968
- const alreadyExists = stashAlreadyExistsFilesFromError(popErr);
969
- const gsdAlreadyExists = alreadyExists.filter((f) => f.startsWith(".gsd/"));
970
- const nonGsdAlreadyExists = alreadyExists.filter((f) => !f.startsWith(".gsd/"));
971
- const isUntrackedRestoreFailure = stashPopMessage.includes("could not restore untracked files from stash");
972
- const stashRefForDrop = stashRefFromError(popErr);
973
- const nonGsdUnmerged = nativeConflictFiles(basePath).filter((f) => !f.startsWith(".gsd/"));
974
- const gsdContentConflicts = isUntrackedRestoreFailure
975
- ? gsdJsonlFilesWithConflictMarkers(basePath)
976
- : [];
977
- const gsdConflictFiles = [...new Set([...gsdAlreadyExists, ...gsdContentConflicts])];
978
- if (isUntrackedRestoreFailure &&
979
- gsdConflictFiles.length > 0 &&
980
- nonGsdAlreadyExists.length === 0 &&
981
- nonGsdUnmerged.length === 0) {
982
- for (const f of gsdConflictFiles) {
983
- execFileSync("git", ["checkout", "HEAD", "--", f], {
984
- cwd: basePath,
985
- stdio: ["ignore", "pipe", "pipe"],
986
- encoding: "utf-8",
987
- });
988
- nativeAddPaths(basePath, [f]);
989
- }
990
- if (stashRefForDrop) {
991
- try {
992
- execFileSync("git", ["stash", "drop", stashRefForDrop], {
993
- cwd: basePath,
994
- stdio: ["ignore", "pipe", "pipe"],
995
- encoding: "utf-8",
996
- });
997
- }
998
- catch (err) { /* stash may already be consumed */
999
- logWarning("worktree", `git stash drop failed: ${err instanceof Error ? err.message : String(err)}`);
1000
- }
1001
- }
1002
- else {
1003
- logWarning("worktree", "recorded stash entry could not be resolved; skipping automatic drop");
1004
- }
1005
- return;
1006
- }
1007
- const wrapped = new Error(`checkout to '${branch}' succeeded but stash restore failed; working tree changes remain in the stash list. Original error: ${msg}`);
1008
- if (stashRefForDrop)
1009
- wrapped.stashRef = stashRefForDrop;
1010
- throw wrapped;
1011
- }
1012
- }
1013
- }
1014
740
  // ─── Public API ────────────────────────────────────────────────────────────
1015
741
  /**
1016
742
  * Create a new auto-worktree for a milestone, chdir into it, and store
@@ -1335,7 +1061,9 @@ export function isInAutoWorktree(basePath) {
1335
1061
  */
1336
1062
  export function getAutoWorktreePath(basePath, milestoneId) {
1337
1063
  basePath = resolveWorktreeProjectRoot(basePath);
1338
- const p = worktreePath(basePath, milestoneId);
1064
+ // basePath is already the resolved project root — go straight to placement
1065
+ // instead of worktreePath(), which would re-resolve the root.
1066
+ const p = worktreePathFor(basePath, milestoneId);
1339
1067
  if (!existsSync(p))
1340
1068
  return null;
1341
1069
  // Validate this is a real git worktree, not a stray directory.
@@ -1995,8 +1723,6 @@ export function mergeMilestoneToMain(originalBasePath_, milestoneId, roadmapCont
1995
1723
  const isUntrackedRestoreFailure = stashPopMessage.includes("could not restore untracked files from stash");
1996
1724
  const gsdContentConflicts = [];
1997
1725
  const alreadyExists = stashAlreadyExistsFilesFromError(e);
1998
- const gsdAlreadyExists = alreadyExists.filter((f) => f.startsWith(".gsd/"));
1999
- const nonGsdAlreadyExists = alreadyExists.filter((f) => !f.startsWith(".gsd/"));
2000
1726
  // Untracked-file restore failures can leave marker conflicts in tracked
2001
1727
  // .gsd JSONL files without producing `U` status entries.
2002
1728
  if (isUntrackedRestoreFailure) {
@@ -2057,10 +1783,11 @@ export function mergeMilestoneToMain(originalBasePath_, milestoneId, roadmapCont
2057
1783
  }
2058
1784
  else if (gsdUU.length === 0 &&
2059
1785
  nonGsdUU.length === 0 &&
2060
- gsdAlreadyExists.length > 0 &&
2061
- nonGsdAlreadyExists.length === 0) {
2062
- // Untracked-file restore failure from stash pop where all collided
2063
- // paths are .gsd/ artifacts that already exist after merge.
1786
+ alreadyExists.length > 0) {
1787
+ // Untracked-file restore failure from stash pop where all collided paths
1788
+ // already exist after merge (committed on target). Safe to drop the stash
1789
+ // for the full alreadyExists set they were untracked on source by
1790
+ // definition of the "already exists, no checkout" failure.
2064
1791
  if (stashRefForDrop) {
2065
1792
  try {
2066
1793
  execFileSync("git", ["stash", "drop", stashRefForDrop], {
@@ -2083,11 +1810,6 @@ export function mergeMilestoneToMain(originalBasePath_, milestoneId, roadmapCont
2083
1810
  files: nonGsdUU.join(", "),
2084
1811
  });
2085
1812
  }
2086
- else if (nonGsdAlreadyExists.length > 0) {
2087
- logWarning("reconcile", "Stash pop restore collision on non-.gsd files after merge", {
2088
- files: nonGsdAlreadyExists.join(", "),
2089
- });
2090
- }
2091
1813
  else {
2092
1814
  logWarning("worktree", "git stash pop failed without resolvable conflict files; leaving stash for manual recovery");
2093
1815
  }
@@ -2182,63 +1904,24 @@ export function mergeMilestoneToMain(originalBasePath_, milestoneId, roadmapCont
2182
1904
  }
2183
1905
  };
2184
1906
  let shouldCleanup = false;
2185
- let pushed = false;
2186
- let prCreated = false;
2187
1907
  try {
2188
- // 10. Auto-push if enabled
2189
- if (prefs.auto_push === true && prefs.auto_pr !== true && !nothingToCommit) {
2190
- const remote = prefs.remote ?? "origin";
2191
- if (gitRemoteExists(originalBasePath_, remote)) {
2192
- try {
2193
- execFileSync("git", ["push", remote, mainBranch], {
2194
- cwd: originalBasePath_,
2195
- stdio: ["ignore", "pipe", "pipe"],
2196
- encoding: "utf-8",
2197
- });
2198
- pushed = true;
2199
- }
2200
- catch (err) {
2201
- // Push failure is non-fatal
2202
- logWarning("worktree", `git push failed: ${err instanceof Error ? err.message : String(err)}`);
2203
- }
2204
- }
2205
- }
2206
- // 9b. Auto-create PR if enabled (#2302: no longer gated on pushed/auto_push)
2207
- if (prefs.auto_pr === true && !nothingToCommit) {
2208
- const remote = prefs.remote ?? "origin";
2209
- const prTarget = prefs.pr_target_branch ?? mainBranch;
2210
- if (gitRemoteExists(originalBasePath_, remote)) {
2211
- try {
2212
- // Push the milestone branch to remote first
2213
- execFileSync("git", ["push", remote, milestoneBranch], {
2214
- cwd: originalBasePath_,
2215
- stdio: ["ignore", "pipe", "pipe"],
2216
- encoding: "utf-8",
2217
- });
2218
- const prEvidence = buildPullRequestEvidence({
2219
- milestoneId,
2220
- milestoneTitle,
2221
- changeType: "feat",
2222
- summaries: completedSlices.map((slice) => `### ${slice.id}\n${slice.title}`),
2223
- testsRun: ["Auto-created after milestone merge. Run `npm run verify:merge` before marking this draft ready."],
2224
- rollbackNotes: ["Close the draft PR or revert the merge commit if review finds a behavior regression."],
2225
- how: "Generated by git.auto_pr after the milestone branch was pushed and merged locally.",
2226
- });
2227
- const prUrl = createDraftPullRequestFromEvidence(originalBasePath_, milestoneId, prEvidence, {
2228
- head: milestoneBranch,
2229
- base: prTarget,
2230
- });
2231
- if (!prUrl) {
2232
- throw new Error("gh pr create returned no URL");
2233
- }
2234
- prCreated = true;
2235
- }
2236
- catch (err) {
2237
- // PR creation failure is non-fatal — gh may not be installed or authenticated
2238
- logWarning("worktree", `PR creation failed: ${err instanceof Error ? err.message : String(err)}`);
2239
- }
2240
- }
2241
- }
1908
+ // 10/9b. Publication (auto-push / draft PR) — Publication module seam (ADR-034).
1909
+ const publication = publishMilestone({
1910
+ basePath: originalBasePath_,
1911
+ milestoneId,
1912
+ milestoneTitle,
1913
+ integrationBranch: mainBranch,
1914
+ milestoneBranch,
1915
+ sliceSummaries: completedSlices.map((slice) => `### ${slice.id}\n${slice.title}`),
1916
+ nothingToCommit,
1917
+ prefs: {
1918
+ autoPush: prefs.auto_push === true,
1919
+ autoPr: prefs.auto_pr === true,
1920
+ remote: prefs.remote,
1921
+ prTargetBranch: prefs.pr_target_branch,
1922
+ },
1923
+ });
1924
+ const { pushed, prCreated } = publication;
2242
1925
  // 11. Guard removed — step 9b (#1792) now handles this with a smarter check:
2243
1926
  // throws only when the milestone has unanchored code changes, passes
2244
1927
  // through when the code is genuinely already on the integration branch.
@@ -25,7 +25,7 @@ import { clearActivityLogState } from "./activity-log.js";
25
25
  import { synthesizeCrashRecovery, getDeepDiagnostic, readActiveMilestoneId, } from "./session-forensics.js";
26
26
  import { writeLock, clearLock, clearStaleWorkerLock, readCrashLock, isLockProcessAlive, formatCrashInfo, emitCrashRecoveredUnitEnd, emitOpenUnitEndForUnit, } from "./crash-recovery.js";
27
27
  import { acquireSessionLock, getSessionLockStatus, releaseSessionLock, updateSessionLock, } from "./session-lock.js";
28
- import { resolveAutoSupervisorConfig, loadEffectiveGSDPreferences, getIsolationMode, } from "./preferences.js";
28
+ import { resolveAutoSupervisorConfig, loadEffectiveGSDPreferences, getIsolationMode, resolveEffectiveUnitIsolationMode, } from "./preferences.js";
29
29
  import { playNotificationBell, sendDesktopNotification } from "./notifications.js";
30
30
  import { getBudgetAlertLevel, getNewBudgetAlertLevel, getBudgetEnforcementAction, resolveCompactionThresholdPercent, shouldRerootStepSessionForContext, } from "./auto-budget.js";
31
31
  import { markToolStart as _markToolStart, markToolEnd as _markToolEnd, getOldestInFlightToolAgeMs as _getOldestInFlightToolAgeMs, clearInFlightTools, isToolInvocationError, isQueuedUserMessageSkip, isDeterministicPolicyError, } from "./auto-tool-tracking.js";
@@ -117,6 +117,7 @@ import { createWorkspace, scopeMilestone } from "./workspace.js";
117
117
  import { registerAutoWorker, markWorkerStopping, } from "./db/auto-workers.js";
118
118
  import { releaseMilestoneLease } from "./db/milestone-leases.js";
119
119
  import { normalizeRealPath } from "./paths.js";
120
+ import { formatStopNoticePrefix, isBlockedStopReason, stopNoticeDisplayReason, } from "./stop-notice.js";
120
121
  // ── ENCAPSULATION INVARIANT ─────────────────────────────────────────────────
121
122
  // ALL mutable auto-mode state lives in the AutoSession class (auto/session.ts).
122
123
  // This file must NOT declare module-level `let` or `var` variables for state.
@@ -137,17 +138,7 @@ export function formatAutoStopNotification(prefix, totals, unitCount) {
137
138
  `Session: ${formatCost(totals.cost)} · ${formatTokenCount(totals.tokens.total)} tokens · ${unitCount} units`,
138
139
  ].join("\n");
139
140
  }
140
- function isBlockedStopReason(reason) {
141
- return /^Blocked:\s*/i.test(reason ?? "");
142
- }
143
- function formatAutoStopDisplayReason(reason) {
144
- return (reason ?? "").replace(/^Blocked:\s*/i, "").trim();
145
- }
146
- export function formatAutoStopNotificationPrefix(reason) {
147
- const displayReason = formatAutoStopDisplayReason(reason);
148
- const prefix = isBlockedStopReason(reason) ? "Auto-mode blocked" : "Auto-mode stopped";
149
- return displayReason ? `${prefix} — ${displayReason}` : prefix;
150
- }
141
+ export { formatStopNoticePrefix as formatAutoStopNotificationPrefix } from "./stop-notice.js";
151
142
  function clearSessionModelOverrideForCommandSession(ctx) {
152
143
  const sessionId = s.cmdCtx?.sessionManager?.getSessionId?.() ??
153
144
  ctx?.sessionManager?.getSessionId?.();
@@ -346,14 +337,11 @@ export function startAutoDetached(ctx, pi, base, verboseMode, options) {
346
337
  export function shouldUseWorktreeIsolation(basePath) {
347
338
  return getIsolationMode(basePath) === "worktree";
348
339
  }
349
- function resolveEffectiveUnitIsolationMode(configuredMode, isolationDegraded) {
350
- return configuredMode === "worktree" && isolationDegraded ? "branch" : configuredMode;
351
- }
352
- export function _resolveEffectiveUnitIsolationModeForTest(configuredMode, isolationDegraded) {
353
- return resolveEffectiveUnitIsolationMode(configuredMode, isolationDegraded);
340
+ export function _resolveEffectiveUnitIsolationModeForTest(configuredMode, isolationDegraded, strandedRecoveryIsolationMode = null) {
341
+ return resolveEffectiveUnitIsolationMode(configuredMode, isolationDegraded, strandedRecoveryIsolationMode);
354
342
  }
355
343
  function getEffectiveUnitIsolationMode(basePath) {
356
- return resolveEffectiveUnitIsolationMode(getIsolationMode(basePath), s.isolationDegraded);
344
+ return resolveEffectiveUnitIsolationMode(getIsolationMode(basePath), s.isolationDegraded, s.strandedRecoveryIsolationMode);
357
345
  }
358
346
  /** Crash recovery prompt — set by startAuto, consumed by the main loop */
359
347
  /** Pending verification retry — set when gate fails with retries remaining, consumed by autoLoop */
@@ -993,8 +981,8 @@ export async function stopAuto(ctx, pi, reason, options = {}) {
993
981
  if (!s.active && !s.paused)
994
982
  return;
995
983
  const loadedPreferences = loadEffectiveGSDPreferences(s.basePath || undefined)?.preferences;
996
- const stopNotificationPrefix = formatAutoStopNotificationPrefix(reason);
997
- const displayReason = formatAutoStopDisplayReason(reason);
984
+ const stopNotificationPrefix = formatStopNoticePrefix(reason);
985
+ const displayReason = stopNoticeDisplayReason(reason);
998
986
  const isHeadlessStop = process.env.GSD_HEADLESS === "1";
999
987
  const completionStopRequested = Boolean(options.completionWidget);
1000
988
  const preserveCloseoutTranscript = !isHeadlessStop && (options.preserveCloseoutTranscript ?? completionStopRequested);
@@ -15,7 +15,8 @@ import { resumeAutoAfterProviderDelay } from "./provider-error-resume.js";
15
15
  import { classifyError, createRetryState, resetRetryState, isTransient, } from "../error-classifier.js";
16
16
  import { blockModel, isModelBlocked } from "../blocked-models.js";
17
17
  import { getProjectGSDPreferencesPath } from "../preferences.js";
18
- import { formatProviderErrorGuidance, resolveProviderErrorGuidance, } from "../provider-error-guidance.js";
18
+ import { resolveProviderErrorGuidance } from "../provider-error-guidance.js";
19
+ import { formatGuidance } from "../guidance.js";
19
20
  const retryState = createRetryState();
20
21
  const MAX_NETWORK_RETRIES = 2;
21
22
  function isObjectRecord(value) {
@@ -486,7 +487,7 @@ export async function handleAgentEnd(pi, event, ctx) {
486
487
  preferencesPath: dash.basePath ? getProjectGSDPreferencesPath(dash.basePath) : undefined,
487
488
  hasConfiguredFallbacks: (modelConfig?.fallbacks.length ?? 0) > 0,
488
489
  });
489
- const guidanceText = formatProviderErrorGuidance(guidance);
490
+ const guidanceText = formatGuidance(guidance);
490
491
  await pauseForProviderModelRejection(ctx, pi, {
491
492
  errorDetail,
492
493
  rawErrorMsg,
@@ -9,6 +9,7 @@ import { logWarning } from "../workflow-logger.js";
9
9
  import { openWorkflowDatabase } from "../db-workspace.js";
10
10
  import { getAutoWorktreePath } from "../auto-worktree.js";
11
11
  import { resolveWorktreeProjectRoot } from "../worktree-root.js";
12
+ import { worktreesDirs } from "../worktree-placement.js";
12
13
  export function safeWorkspaceCwd() {
13
14
  try {
14
15
  return process.cwd();
@@ -42,19 +43,21 @@ export function resolveWorkflowToolBasePath(ctx, scope) {
42
43
  return worktree;
43
44
  }
44
45
  else {
45
- const worktreesDir = join(projectRoot, ".gsd", "worktrees");
46
- if (existsSync(worktreesDir)) {
46
+ const live = [];
47
+ for (const worktreesDir of worktreesDirs(projectRoot)) {
48
+ if (!existsSync(worktreesDir))
49
+ continue;
47
50
  try {
48
- const live = readdirSync(worktreesDir)
51
+ live.push(...readdirSync(worktreesDir)
49
52
  .map((name) => join(worktreesDir, name))
50
- .filter((p) => existsSync(join(p, ".git")));
51
- if (live.length === 1)
52
- return live[0];
53
+ .filter((p) => existsSync(join(p, ".git"))));
53
54
  }
54
55
  catch (err) {
55
56
  logWarning("bootstrap", `resolveWorkflowToolBasePath: failed to scan worktrees: ${err instanceof Error ? err.message : String(err)}`);
56
57
  }
57
58
  }
59
+ if (live.length === 1)
60
+ return live[0];
58
61
  }
59
62
  return cwd;
60
63
  }