@opengsd/gsd-pi 1.2.0-dev.4c756166 → 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 (338) 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 +9 -9
  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 +9 -9
  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/models.generated.d.ts +294 -239
  191. package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
  192. package/packages/pi-ai/dist/models.generated.js +260 -256
  193. package/packages/pi-ai/dist/models.generated.js.map +1 -1
  194. package/packages/pi-ai/package.json +1 -1
  195. package/packages/pi-coding-agent/dist/core/capability-patches.d.ts.map +1 -1
  196. package/packages/pi-coding-agent/dist/core/capability-patches.js +3 -1
  197. package/packages/pi-coding-agent/dist/core/capability-patches.js.map +1 -1
  198. package/packages/pi-coding-agent/package.json +7 -7
  199. package/packages/pi-tui/package.json +2 -2
  200. package/packages/rpc-client/package.json +2 -2
  201. package/pkg/package.json +1 -1
  202. package/src/resources/extensions/bg-shell/utilities.ts +5 -2
  203. package/src/resources/extensions/claude-code-cli/models.ts +9 -0
  204. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +37 -2
  205. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +28 -0
  206. package/src/resources/extensions/gsd/auto/loop-deps.ts +1 -1
  207. package/src/resources/extensions/gsd/auto/orchestrator.ts +39 -5
  208. package/src/resources/extensions/gsd/auto/phases.ts +10 -1
  209. package/src/resources/extensions/gsd/auto-post-unit.ts +25 -7
  210. package/src/resources/extensions/gsd/auto-prompts.ts +3 -0
  211. package/src/resources/extensions/gsd/auto-start.ts +12 -15
  212. package/src/resources/extensions/gsd/auto-tool-tracking.ts +19 -0
  213. package/src/resources/extensions/gsd/auto-unit-tool-scope.ts +10 -17
  214. package/src/resources/extensions/gsd/auto-worktree-repair.ts +13 -2
  215. package/src/resources/extensions/gsd/auto-worktree.ts +41 -364
  216. package/src/resources/extensions/gsd/auto.ts +20 -24
  217. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +3 -5
  218. package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +10 -6
  219. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +87 -6
  220. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +29 -3
  221. package/src/resources/extensions/gsd/branch-patterns.ts +3 -0
  222. package/src/resources/extensions/gsd/captures.ts +5 -16
  223. package/src/resources/extensions/gsd/closeout-recovery.ts +2 -1
  224. package/src/resources/extensions/gsd/commands/catalog.ts +6 -68
  225. package/src/resources/extensions/gsd/crash-recovery.ts +3 -9
  226. package/src/resources/extensions/gsd/db/engine.ts +809 -0
  227. package/src/resources/extensions/gsd/db/queries.ts +453 -0
  228. package/src/resources/extensions/gsd/db/sql-constants.ts +12 -0
  229. package/src/resources/extensions/gsd/db/writers/cascades.ts +237 -0
  230. package/src/resources/extensions/gsd/db/writers/import-restore.ts +310 -0
  231. package/src/resources/extensions/gsd/db/writers/memory.ts +220 -0
  232. package/src/resources/extensions/gsd/db/writers/reconcile.ts +500 -0
  233. package/src/resources/extensions/gsd/db/writers/status.ts +88 -0
  234. package/src/resources/extensions/gsd/doctor-environment.ts +5 -13
  235. package/src/resources/extensions/gsd/doctor-format.ts +12 -7
  236. package/src/resources/extensions/gsd/doctor-git-checks.ts +3 -3
  237. package/src/resources/extensions/gsd/doctor-runtime-checks.ts +22 -17
  238. package/src/resources/extensions/gsd/error-classifier.ts +11 -0
  239. package/src/resources/extensions/gsd/git-service.ts +1 -0
  240. package/src/resources/extensions/gsd/gitignore.ts +3 -0
  241. package/src/resources/extensions/gsd/gsd-db.ts +173 -2373
  242. package/src/resources/extensions/gsd/guidance.ts +139 -0
  243. package/src/resources/extensions/gsd/guided-flow.ts +50 -5
  244. package/src/resources/extensions/gsd/mcp-tool-name.ts +6 -11
  245. package/src/resources/extensions/gsd/memory-consolidation-scanner.ts +1 -1
  246. package/src/resources/extensions/gsd/migrate/safety.ts +18 -7
  247. package/src/resources/extensions/gsd/migration-auto-check.ts +28 -3
  248. package/src/resources/extensions/gsd/model-cost-table.ts +1 -0
  249. package/src/resources/extensions/gsd/model-router.ts +3 -0
  250. package/src/resources/extensions/gsd/notification-store.ts +26 -3
  251. package/src/resources/extensions/gsd/parallel-merge.ts +12 -9
  252. package/src/resources/extensions/gsd/parallel-monitor-overlay.ts +10 -7
  253. package/src/resources/extensions/gsd/paths.ts +42 -22
  254. package/src/resources/extensions/gsd/pre-execution-checks.ts +109 -3
  255. package/src/resources/extensions/gsd/preferences-models.ts +10 -46
  256. package/src/resources/extensions/gsd/preferences.ts +18 -0
  257. package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  258. package/src/resources/extensions/gsd/prompts/refine-slice.md +1 -1
  259. package/src/resources/extensions/gsd/prompts/replan-slice.md +1 -1
  260. package/src/resources/extensions/gsd/provider-error-guidance.ts +4 -9
  261. package/src/resources/extensions/gsd/provider-switch-observer.ts +1 -1
  262. package/src/resources/extensions/gsd/publication.ts +122 -0
  263. package/src/resources/extensions/gsd/recovery-classification.ts +47 -88
  264. package/src/resources/extensions/gsd/safety/evidence-collector.ts +36 -4
  265. package/src/resources/extensions/gsd/safety/evidence-cross-ref.ts +7 -2
  266. package/src/resources/extensions/gsd/safety/file-change-validator.ts +14 -0
  267. package/src/resources/extensions/gsd/state-transition-matrix.ts +42 -0
  268. package/src/resources/extensions/gsd/state.ts +4 -21
  269. package/src/resources/extensions/gsd/status-guards.ts +59 -8
  270. package/src/resources/extensions/gsd/stop-notice.ts +75 -0
  271. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +123 -0
  272. package/src/resources/extensions/gsd/tests/auto-paused-ui-cleanup.test.ts +3 -1
  273. package/src/resources/extensions/gsd/tests/auto-post-unit-evidence-crossref-4909.test.ts +46 -0
  274. package/src/resources/extensions/gsd/tests/auto-worktree-registry.test.ts +2 -2
  275. package/src/resources/extensions/gsd/tests/auto-worktree-repair.test.ts +4 -2
  276. package/src/resources/extensions/gsd/tests/checkout-branch-stash-guard.test.ts +66 -1
  277. package/src/resources/extensions/gsd/tests/clear-stale-autostart.test.ts +44 -0
  278. package/src/resources/extensions/gsd/tests/commands-verdict.test.ts +8 -7
  279. package/src/resources/extensions/gsd/tests/evidence-xref-gsd-exec.test.ts +157 -0
  280. package/src/resources/extensions/gsd/tests/file-change-validator.test.ts +33 -1
  281. package/src/resources/extensions/gsd/tests/guidance.test.ts +125 -0
  282. package/src/resources/extensions/gsd/tests/integration/auto-worktree-milestone-merge.test.ts +51 -4
  283. package/src/resources/extensions/gsd/tests/integration/auto-worktree.test.ts +54 -1
  284. package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +3 -2
  285. package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +85 -1
  286. package/src/resources/extensions/gsd/tests/notification-store.test.ts +32 -0
  287. package/src/resources/extensions/gsd/tests/pre-execution-checks.test.ts +193 -1
  288. package/src/resources/extensions/gsd/tests/provider-error-guidance.test.ts +3 -3
  289. package/src/resources/extensions/gsd/tests/publication.test.ts +120 -0
  290. package/src/resources/extensions/gsd/tests/recovery-classification-illegal-transition.test.ts +30 -0
  291. package/src/resources/extensions/gsd/tests/register-hooks-depth-verification.test.ts +248 -1
  292. package/src/resources/extensions/gsd/tests/runtime-invariant-modules.test.ts +1 -0
  293. package/src/resources/extensions/gsd/tests/safety-harness-false-positives.test.ts +38 -0
  294. package/src/resources/extensions/gsd/tests/session-switch-clears-pending-autostart.test.ts +108 -0
  295. package/src/resources/extensions/gsd/tests/single-writer-invariant.test.ts +43 -6
  296. package/src/resources/extensions/gsd/tests/state-transition-matrix.test.ts +36 -0
  297. package/src/resources/extensions/gsd/tests/status-guards.test.ts +38 -0
  298. package/src/resources/extensions/gsd/tests/stop-notice.test.ts +70 -0
  299. package/src/resources/extensions/gsd/tests/tool-invocation-error-loop-break.test.ts +8 -0
  300. package/src/resources/extensions/gsd/tests/tool-surface-readiness.test.ts +155 -0
  301. package/src/resources/extensions/gsd/tests/unit-closeout.test.ts +209 -0
  302. package/src/resources/extensions/gsd/tests/unit-context-composer.test.ts +23 -2
  303. package/src/resources/extensions/gsd/tests/unit-registry.test.ts +163 -0
  304. package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +2 -2
  305. package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +2 -2
  306. package/src/resources/extensions/gsd/tests/worktree-lifecycle.test.ts +41 -4
  307. package/src/resources/extensions/gsd/tests/worktree-manager.test.ts +22 -1
  308. package/src/resources/extensions/gsd/tests/worktree-placement.test.ts +113 -0
  309. package/src/resources/extensions/gsd/tests/worktree-reentry.test.ts +1 -1
  310. package/src/resources/extensions/gsd/tests/worktree-safety.test.ts +3 -1
  311. package/src/resources/extensions/gsd/tests/worktree-symlink-removal.test.ts +12 -6
  312. package/src/resources/extensions/gsd/tests/worktree-teardown-safety.test.ts +2 -2
  313. package/src/resources/extensions/gsd/tests/write-gate.test.ts +42 -0
  314. package/src/resources/extensions/gsd/tool-surface-readiness.ts +76 -0
  315. package/src/resources/extensions/gsd/tools/complete-slice.ts +23 -58
  316. package/src/resources/extensions/gsd/tools/exec-tool.ts +5 -8
  317. package/src/resources/extensions/gsd/tools/plan-slice.ts +12 -6
  318. package/src/resources/extensions/gsd/tools/reopen-milestone.ts +11 -38
  319. package/src/resources/extensions/gsd/tools/reopen-slice.ts +14 -42
  320. package/src/resources/extensions/gsd/tools/skip-slice.ts +18 -44
  321. package/src/resources/extensions/gsd/undo.ts +9 -8
  322. package/src/resources/extensions/gsd/unit-closeout.ts +201 -0
  323. package/src/resources/extensions/gsd/unit-context-composer.ts +12 -1
  324. package/src/resources/extensions/gsd/unit-context-manifest.ts +4 -28
  325. package/src/resources/extensions/gsd/unit-registry.ts +425 -0
  326. package/src/resources/extensions/gsd/unit-tool-contracts.ts +27 -192
  327. package/src/resources/extensions/gsd/workflow-tool-surface.ts +4 -1
  328. package/src/resources/extensions/gsd/worktree-git-recovery.ts +314 -0
  329. package/src/resources/extensions/gsd/worktree-lifecycle.ts +10 -1
  330. package/src/resources/extensions/gsd/worktree-manager.ts +47 -28
  331. package/src/resources/extensions/gsd/worktree-placement.ts +63 -0
  332. package/src/resources/extensions/gsd/worktree-reentry.ts +10 -7
  333. package/src/resources/extensions/gsd/worktree-root.ts +29 -6
  334. package/src/resources/extensions/gsd/worktree-safety.ts +8 -5
  335. package/src/resources/extensions/gsd/worktree-session-state.ts +11 -11
  336. package/src/resources/skills/gsd-browser/SKILL.md +1 -1
  337. /package/dist/web/standalone/.next/static/{DUFWcMFRH3iXh7d2fbrOF → 2p9Rv9pQflAxCBbGVI2vb}/_buildManifest.js +0 -0
  338. /package/dist/web/standalone/.next/static/{DUFWcMFRH3iXh7d2fbrOF → 2p9Rv9pQflAxCBbGVI2vb}/_ssgManifest.js +0 -0
@@ -15,22 +15,15 @@ export {
15
15
  RUN_UAT_BROWSER_TOOL_NAMES,
16
16
  } from "./unit-tool-contracts.js";
17
17
 
18
- const EXECUTE_TASK_UNIT_TYPES = new Set([
19
- "execute-task",
20
- "execute-task-simple",
21
- "reactive-execute",
22
- ]);
23
-
24
- // These units own quality gates, but their completion handlers persist verdicts
25
- // from artifact sections. gsd_save_gate_result belongs to gate-evaluate, so keep
26
- // blocking it here with a calm redirect to the section-write path.
27
- const SECTION_CLOSE_GATE_UNIT_TYPES = new Set([
28
- "execute-task",
29
- "execute-task-simple",
30
- "reactive-execute",
31
- "complete-slice",
32
- "validate-milestone",
33
- ]);
18
+ // Scope-class membership is declared per unit in the Unit Registry (ADR-033).
19
+ // EXECUTE_TASK_UNIT_TYPES = scopeClass "execute-task"; the section-close gate
20
+ // Set additionally includes scopeClass "section-close" — units whose completion
21
+ // handlers persist gate verdicts from artifact sections (gsd_save_gate_result
22
+ // belongs to gate-evaluate, so it is soft-blocked with a redirect below).
23
+ import {
24
+ EXECUTE_TASK_UNIT_TYPES,
25
+ SECTION_CLOSE_GATE_UNIT_TYPES,
26
+ } from "./unit-registry.js";
34
27
 
35
28
  const EXTRA_SCOPED_GSD_LIFECYCLE_TOOLS = [
36
29
  "gsd_skip_slice",
@@ -110,7 +103,7 @@ function isNativeWorkflowTool(toolName: string): boolean {
110
103
  return stripMcpToolPrefix(toolName) === "Workflow";
111
104
  }
112
105
 
113
- function readStringField(input: unknown, camel: string, snake: string): string | undefined {
106
+ export function readStringField(input: unknown, camel: string, snake: string): string | undefined {
114
107
  if (!input || typeof input !== "object") return undefined;
115
108
  const record = input as Record<string, unknown>;
116
109
  const value = record[camel] ?? record[snake];
@@ -5,6 +5,7 @@ import { existsSync, lstatSync, readdirSync, type Stats } from "node:fs";
5
5
  import { join } from "node:path";
6
6
 
7
7
  import { worktreePath } from "./worktree-manager.js";
8
+ import { worktreesDirs } from "./worktree-placement.js";
8
9
  import { normalizeWorktreePathForCompare } from "./worktree-root.js";
9
10
  import type { WorktreeSafetyResult } from "./worktree-safety.js";
10
11
 
@@ -39,6 +40,16 @@ export function expectedAutoWorktreePath(
39
40
  return worktreePath(projectRoot, id);
40
41
  }
41
42
 
43
+ /** Every path the milestone worktree may legitimately live at (canonical + legacy). */
44
+ function candidateAutoWorktreePaths(
45
+ projectRoot: string,
46
+ milestoneId: string | null | undefined,
47
+ ): string[] {
48
+ const id = milestoneId?.trim();
49
+ if (!id || !isValidMilestoneId(id)) return [];
50
+ return worktreesDirs(projectRoot).map((dir) => join(dir, id));
51
+ }
52
+
42
53
  export function resolvePausedAutoWorktreePath(input: {
43
54
  basePath: string;
44
55
  originalBasePath?: string | null;
@@ -82,8 +93,8 @@ export function assessAutoWorktreeRepairTarget(input: {
82
93
  if (!expectedPath) {
83
94
  return { ok: false, reason: "missing expected worktree path" };
84
95
  }
85
- const computedExpectedPath = expectedAutoWorktreePath(input.projectRoot, input.milestoneId);
86
- if (!computedExpectedPath || !samePath(expectedPath, computedExpectedPath)) {
96
+ const candidatePaths = candidateAutoWorktreePaths(input.projectRoot, input.milestoneId);
97
+ if (candidatePaths.length === 0 || !candidatePaths.some((candidate) => samePath(expectedPath, candidate))) {
87
98
  return { ok: false, reason: "expected worktree path does not match milestone" };
88
99
  }
89
100
  if (!samePath(input.activeRoot, input.projectRoot) && !samePath(input.activeRoot, expectedPath)) {
@@ -20,7 +20,7 @@ import {
20
20
  unlinkSync,
21
21
  lstatSync as lstatSyncFn,
22
22
  } from "node:fs";
23
- import { dirname, isAbsolute, join, relative, resolve, sep as pathSep } from "node:path";
23
+ import { dirname, isAbsolute, join, relative, resolve } from "node:path";
24
24
  import { GSDError, GSD_IO_ERROR, GSD_GIT_ERROR } from "./errors.js";
25
25
  import {
26
26
  reconcileWorktreeDb,
@@ -44,6 +44,7 @@ import {
44
44
  worktreePath,
45
45
  isInsideWorktreesDir,
46
46
  } from "./worktree-manager.js";
47
+ import { worktreePathFor } from "./worktree-placement.js";
47
48
  import {
48
49
  detectWorktreeName,
49
50
  resolveGitHeadPath,
@@ -51,17 +52,25 @@ import {
51
52
  } from "./worktree.js";
52
53
  import {
53
54
  isGsdWorktreePath,
55
+ projectRootFromWorktreePath,
54
56
  normalizeWorktreePathForCompare,
55
57
  resolveWorktreeProjectRoot,
56
58
  } from "./worktree-root.js";
57
59
  import { autoResolveSafeConflictPaths } from "./git-conflict-resolve.js";
58
60
  import { MergeConflictError, readIntegrationBranch, resolveMilestoneIntegrationBranch, RUNTIME_EXCLUSION_PATHS } from "./git-service.js";
59
- import {
60
- buildPullRequestEvidence,
61
- createDraftPullRequestFromEvidence,
62
- } from "./pull-request-process.js";
61
+ import { publishMilestone } from "./publication.js";
63
62
  import { debugLog } from "./debug-logger.js";
64
63
  import { logWarning, logError } from "./workflow-logger.js";
64
+ import {
65
+ checkoutBranchWithStashGuard,
66
+ cleanupConflictState,
67
+ gsdJsonlFilesWithConflictMarkers,
68
+ hasConflictMarkers,
69
+ popStashByRef,
70
+ removeMergeStateFiles,
71
+ stashAlreadyExistsFilesFromError,
72
+ stashRefFromError,
73
+ } from "./worktree-git-recovery.js";
65
74
  import { loadEffectiveGSDPreferences } from "./preferences.js";
66
75
  import { MILESTONE_ID_RE } from "./milestone-ids.js";
67
76
  import { runWorktreePostCreateHook } from "./worktree-post-create-hook.js";
@@ -85,7 +94,6 @@ import {
85
94
  nativeDiffNumstat,
86
95
  nativeUpdateRef,
87
96
  nativeIsAncestor,
88
- nativeMergeAbort,
89
97
  nativeWorktreeList,
90
98
  nativeLsFiles,
91
99
  } from "./native-git-bridge.js";
@@ -132,111 +140,6 @@ const ROOT_STATE_FILES = [
132
140
  // because the project root is authoritative for preferences (#2684).
133
141
  ] as const;
134
142
 
135
- /**
136
- * Pop a stash entry by tracking the unique marker embedded in its message so
137
- * concurrent stash operations against the same project root cannot cause us to
138
- * pop the wrong entry.
139
- *
140
- * If `stashMarker` is null or no longer present in the stash list (e.g. a
141
- * concurrent process popped/dropped it), leaves the stash list untouched and
142
- * returns null.
143
- *
144
- * Throws on pop failure so callers can handle conflict cases the same way
145
- * they would with the prior `git stash pop` form. When throwing after a
146
- * targeted pop attempt, the error is annotated with the targeted stash ref.
147
- *
148
- * (Issue #4980 HIGH-6)
149
- */
150
- function popStashByRef(basePath: string, stashMarker: string | null): string | null {
151
- let popArg: string | null = null;
152
- if (stashMarker) {
153
- try {
154
- const list = execFileSync("git", ["stash", "list", "--format=%gd%x00%s"], {
155
- cwd: basePath,
156
- stdio: ["ignore", "pipe", "pipe"],
157
- encoding: "utf-8",
158
- }).trim().split("\n").filter(Boolean);
159
- for (const entry of list) {
160
- const [ref, subject] = entry.split("\0");
161
- if (ref && subject?.includes(stashMarker)) {
162
- popArg = ref;
163
- break;
164
- }
165
- }
166
- } catch (err) {
167
- logWarning("worktree", `stash list lookup failed; leaving stash untouched: ${err instanceof Error ? err.message : String(err)}`);
168
- }
169
- }
170
- if (!popArg) {
171
- logWarning("worktree", "recorded stash entry could not be resolved; skipping automatic pop");
172
- return null;
173
- }
174
- try {
175
- execFileSync("git", ["stash", "pop", popArg], {
176
- cwd: basePath,
177
- stdio: ["ignore", "pipe", "pipe"],
178
- encoding: "utf-8",
179
- });
180
- } catch (err) {
181
- if (err && typeof err === "object") {
182
- (err as { stashRef?: string }).stashRef = popArg;
183
- }
184
- throw err;
185
- }
186
- return popArg;
187
- }
188
-
189
- /**
190
- * Extract a stash ref annotation injected by popStashByRef() when git stash
191
- * pop fails and we need to conditionally drop the exact stash entry later.
192
- */
193
- function stashRefFromError(err: unknown): string | null {
194
- if (!err || typeof err !== "object") return null;
195
- const stashRef = (err as { stashRef?: unknown }).stashRef;
196
- return typeof stashRef === "string" && stashRef.length > 0 ? stashRef : null;
197
- }
198
-
199
- function stashAlreadyExistsFilesFromError(err: unknown): string[] {
200
- if (!err || typeof err !== "object") return [];
201
- const stderr = (err as { stderr?: unknown }).stderr;
202
- const stderrText = typeof stderr === "string"
203
- ? stderr
204
- : stderr instanceof Uint8Array
205
- ? Buffer.from(stderr).toString("utf-8")
206
- : "";
207
- const message = err instanceof Error ? err.message : String(err);
208
- const text = `${stderrText}\n${message}`;
209
- const files = new Set<string>();
210
- for (const line of text.split("\n")) {
211
- const m = line.match(/^(.*?)\s+already exists, no checkout\s*$/i);
212
- if (!m) continue;
213
- const filePath = m[1]?.trim();
214
- if (filePath) files.add(filePath);
215
- }
216
- return [...files];
217
- }
218
-
219
- /**
220
- * Detect whether an on-disk file still contains unresolved merge conflict
221
- * markers from a failed stash-pop or merge attempt.
222
- *
223
- * Returns false when the file cannot be read.
224
- */
225
- function hasConflictMarkers(filePath: string): boolean {
226
- try {
227
- const content = readFileSync(filePath, "utf-8");
228
- return content.includes("<<<<<<<") && content.includes("=======") && content.includes(">>>>>>>");
229
- } catch {
230
- return false;
231
- }
232
- }
233
-
234
- function gsdJsonlFilesWithConflictMarkers(basePath: string): string[] {
235
- return nativeLsFiles(basePath, ".gsd/*.jsonl").filter((f) =>
236
- hasConflictMarkers(join(basePath, f)),
237
- );
238
- }
239
-
240
143
  /**
241
144
  * Check if two filesystem paths resolve to the same real location.
242
145
  * Returns false if either path cannot be resolved (e.g. doesn't exist).
@@ -382,19 +285,6 @@ export function _gitPathspecForWorktreePath(basePath: string, targetPath: string
382
285
  return gitPathspecForWorktreePath(basePath, targetPath);
383
286
  }
384
287
 
385
- function gitRemoteExists(basePath: string, remote: string): boolean {
386
- try {
387
- execFileSync("git", ["remote", "get-url", remote], {
388
- cwd: basePath,
389
- stdio: ["ignore", "pipe", "pipe"],
390
- encoding: "utf-8",
391
- });
392
- return true;
393
- } catch {
394
- return false;
395
- }
396
- }
397
-
398
288
  function findRegularMergeChangedPaths(basePath: string, milestoneBranch: string, mainBranch: string): Set<string> {
399
289
  const changedPaths = new Set<string>();
400
290
  let mergeLog = "";
@@ -524,49 +414,6 @@ export const isSafeToAutoResolve = (filePath: string): boolean =>
524
414
  filePath.startsWith(".gsd/") ||
525
415
  SAFE_AUTO_RESOLVE_PATTERNS.some((re) => re.test(filePath));
526
416
 
527
- function removeMergeStateFiles(basePath: string, contextLabel: string): void {
528
- try {
529
- for (const f of ["SQUASH_MSG", "MERGE_MSG", "MERGE_MODE", "MERGE_HEAD", "AUTO_MERGE"]) {
530
- const rawPath = execFileSync("git", ["rev-parse", "--git-path", f], {
531
- cwd: basePath,
532
- stdio: ["ignore", "pipe", "pipe"],
533
- encoding: "utf-8",
534
- }).trim();
535
- const p = rawPath.length > 0
536
- ? (isAbsolute(rawPath) ? rawPath : resolve(basePath, rawPath))
537
- : join(resolveGitDir(basePath), f);
538
- if (existsSync(p)) unlinkSync(p);
539
- }
540
- } catch (err) {
541
- logError("worktree", `${contextLabel} merge state cleanup failed: ${err instanceof Error ? err.message : String(err)}`);
542
- }
543
- }
544
-
545
- function cleanupConflictState(basePath: string): void {
546
- // Merge conflicts can leave unmerged index entries; merge-abort alone is not
547
- // enough for squash merges (MERGE_HEAD is never written). Reset the merge
548
- // index, then remove merge message files that native/libgit2 paths may have
549
- // created.
550
- try {
551
- nativeMergeAbort(basePath);
552
- } catch (err) {
553
- // MERGE_HEAD absent (squash merge path) — abort is a no-op, which is fine.
554
- debugLog("conflict-cleanup:merge-abort-skipped", {
555
- error: err instanceof Error ? err.message : String(err),
556
- });
557
- }
558
- try {
559
- execFileSync("git", ["reset", "--merge"], {
560
- cwd: basePath,
561
- stdio: ["ignore", "pipe", "pipe"],
562
- encoding: "utf-8",
563
- });
564
- } catch (err) {
565
- logError("worktree", `git reset --merge failed after merge conflict: ${err instanceof Error ? err.message : String(err)}`);
566
- }
567
- removeMergeStateFiles(basePath, "conflict");
568
- }
569
-
570
417
  // ─── Dispatch-Level Sync (project root ↔ worktree) ──────────────────────────
571
418
 
572
419
  /**
@@ -655,21 +502,8 @@ export function checkResourcesStale(
655
502
  * Returns the corrected base path.
656
503
  */
657
504
  export function escapeStaleWorktree(base: string): string {
658
- // Direct layout: /.gsd/worktrees/
659
- const directMarker = `${pathSep}.gsd${pathSep}worktrees${pathSep}`;
660
- let idx = base.indexOf(directMarker);
661
- if (idx === -1) {
662
- // Symlink-resolved layout: /.gsd/projects/<hash>/worktrees/
663
- const symlinkRe = new RegExp(
664
- `\\${pathSep}\\.gsd\\${pathSep}projects\\${pathSep}[a-f0-9]+\\${pathSep}worktrees\\${pathSep}`,
665
- );
666
- const match = base.match(symlinkRe);
667
- if (!match || match.index === undefined) return base;
668
- idx = match.index;
669
- }
670
-
671
- // base is inside .gsd/worktrees/<something> — extract the project root
672
- const projectRoot = base.slice(0, idx);
505
+ const projectRoot = projectRootFromWorktreePath(base);
506
+ if (projectRoot === null) return base;
673
507
 
674
508
  // Guard: If the candidate project root's .gsd IS the user-level ~/.gsd,
675
509
  // the string-slice heuristic matched the wrong /.gsd/ boundary. This happens
@@ -1066,122 +900,6 @@ export function enterBranchModeForMilestone(
1066
900
  checkoutBranchWithStashGuard(basePath, branch, `enter-branch-mode:${milestoneId}`);
1067
901
  }
1068
902
 
1069
- export function checkoutBranchWithStashGuard(
1070
- basePath: string,
1071
- branch: string,
1072
- reason: string,
1073
- ): void {
1074
- let stashMarker: string | null = null;
1075
- let stashed = false;
1076
-
1077
- const status = nativeWorkingTreeStatus(basePath).trim();
1078
- if (status.length > 0) {
1079
- stashMarker = `gsd-checkout-stash:${reason}:${process.pid}:${Date.now()}:${process.hrtime.bigint().toString(36)}`;
1080
- const stashListBefore = execFileSync("git", ["stash", "list"], {
1081
- cwd: basePath,
1082
- stdio: ["ignore", "pipe", "pipe"],
1083
- encoding: "utf-8",
1084
- });
1085
- execFileSync(
1086
- "git",
1087
- ["stash", "push", "--include-untracked", "-m", `gsd: checkout stash [${stashMarker}]`],
1088
- {
1089
- cwd: basePath,
1090
- stdio: ["ignore", "pipe", "pipe"],
1091
- encoding: "utf-8",
1092
- },
1093
- );
1094
- const stashListAfter = execFileSync("git", ["stash", "list"], {
1095
- cwd: basePath,
1096
- stdio: ["ignore", "pipe", "pipe"],
1097
- encoding: "utf-8",
1098
- });
1099
- stashed = stashListAfter !== stashListBefore;
1100
- }
1101
-
1102
- // Checkout and stash-restore are split so we can distinguish two failure
1103
- // modes: (a) checkout failed → HEAD did not move, restore stash and rethrow;
1104
- // (b) checkout succeeded but stash pop failed → HEAD moved to `branch` but
1105
- // the working-tree changes remain in the stash list. We surface a distinct
1106
- // error in case (b) so callers don't assume the branch switch was rolled back.
1107
- try {
1108
- nativeCheckoutBranch(basePath, branch);
1109
- } catch (checkoutErr) {
1110
- if (stashed) {
1111
- try {
1112
- popStashByRef(basePath, stashMarker);
1113
- } catch (restoreErr) {
1114
- logWarning("worktree", `git stash pop failed during checkout restore: ${restoreErr instanceof Error ? restoreErr.message : String(restoreErr)}`);
1115
- }
1116
- }
1117
- throw checkoutErr;
1118
- }
1119
-
1120
- if (stashed) {
1121
- try {
1122
- popStashByRef(basePath, stashMarker);
1123
- } catch (popErr) {
1124
- const msg = popErr instanceof Error ? popErr.message : String(popErr);
1125
- const stderr = popErr && typeof popErr === "object"
1126
- ? (popErr as { stderr?: unknown }).stderr
1127
- : undefined;
1128
- const stderrText = typeof stderr === "string"
1129
- ? stderr
1130
- : stderr instanceof Uint8Array
1131
- ? Buffer.from(stderr).toString("utf-8")
1132
- : "";
1133
- const stashPopMessage = `${stderrText}\n${msg}`.trim();
1134
- const alreadyExists = stashAlreadyExistsFilesFromError(popErr);
1135
- const gsdAlreadyExists = alreadyExists.filter((f) => f.startsWith(".gsd/"));
1136
- const nonGsdAlreadyExists = alreadyExists.filter((f) => !f.startsWith(".gsd/"));
1137
- const isUntrackedRestoreFailure = stashPopMessage.includes("could not restore untracked files from stash");
1138
- const stashRefForDrop = stashRefFromError(popErr);
1139
- const nonGsdUnmerged = nativeConflictFiles(basePath).filter((f) => !f.startsWith(".gsd/"));
1140
- const gsdContentConflicts = isUntrackedRestoreFailure
1141
- ? gsdJsonlFilesWithConflictMarkers(basePath)
1142
- : [];
1143
- const gsdConflictFiles = [...new Set([...gsdAlreadyExists, ...gsdContentConflicts])];
1144
-
1145
- if (
1146
- isUntrackedRestoreFailure &&
1147
- gsdConflictFiles.length > 0 &&
1148
- nonGsdAlreadyExists.length === 0 &&
1149
- nonGsdUnmerged.length === 0
1150
- ) {
1151
- for (const f of gsdConflictFiles) {
1152
- execFileSync("git", ["checkout", "HEAD", "--", f], {
1153
- cwd: basePath,
1154
- stdio: ["ignore", "pipe", "pipe"],
1155
- encoding: "utf-8",
1156
- });
1157
- nativeAddPaths(basePath, [f]);
1158
- }
1159
-
1160
- if (stashRefForDrop) {
1161
- try {
1162
- execFileSync("git", ["stash", "drop", stashRefForDrop], {
1163
- cwd: basePath,
1164
- stdio: ["ignore", "pipe", "pipe"],
1165
- encoding: "utf-8",
1166
- });
1167
- } catch (err) { /* stash may already be consumed */
1168
- logWarning("worktree", `git stash drop failed: ${err instanceof Error ? err.message : String(err)}`);
1169
- }
1170
- } else {
1171
- logWarning("worktree", "recorded stash entry could not be resolved; skipping automatic drop");
1172
- }
1173
- return;
1174
- }
1175
-
1176
- const wrapped = new Error(
1177
- `checkout to '${branch}' succeeded but stash restore failed; working tree changes remain in the stash list. Original error: ${msg}`,
1178
- );
1179
- if (stashRefForDrop) (wrapped as { stashRef?: string }).stashRef = stashRefForDrop;
1180
- throw wrapped;
1181
- }
1182
- }
1183
- }
1184
-
1185
903
  // ─── Public API ────────────────────────────────────────────────────────────
1186
904
 
1187
905
  /**
@@ -1567,7 +1285,9 @@ export function getAutoWorktreePath(
1567
1285
  ): string | null {
1568
1286
  basePath = resolveWorktreeProjectRoot(basePath);
1569
1287
 
1570
- const p = worktreePath(basePath, milestoneId);
1288
+ // basePath is already the resolved project root — go straight to placement
1289
+ // instead of worktreePath(), which would re-resolve the root.
1290
+ const p = worktreePathFor(basePath, milestoneId);
1571
1291
  if (!existsSync(p)) return null;
1572
1292
 
1573
1293
  // Validate this is a real git worktree, not a stray directory.
@@ -2309,8 +2029,6 @@ export function mergeMilestoneToMain(
2309
2029
  const isUntrackedRestoreFailure = stashPopMessage.includes("could not restore untracked files from stash");
2310
2030
  const gsdContentConflicts: string[] = [];
2311
2031
  const alreadyExists = stashAlreadyExistsFilesFromError(e);
2312
- const gsdAlreadyExists = alreadyExists.filter((f) => f.startsWith(".gsd/"));
2313
- const nonGsdAlreadyExists = alreadyExists.filter((f) => !f.startsWith(".gsd/"));
2314
2032
 
2315
2033
  // Untracked-file restore failures can leave marker conflicts in tracked
2316
2034
  // .gsd JSONL files without producing `U` status entries.
@@ -2374,11 +2092,12 @@ export function mergeMilestoneToMain(
2374
2092
  } else if (
2375
2093
  gsdUU.length === 0 &&
2376
2094
  nonGsdUU.length === 0 &&
2377
- gsdAlreadyExists.length > 0 &&
2378
- nonGsdAlreadyExists.length === 0
2095
+ alreadyExists.length > 0
2379
2096
  ) {
2380
- // Untracked-file restore failure from stash pop where all collided
2381
- // paths are .gsd/ artifacts that already exist after merge.
2097
+ // Untracked-file restore failure from stash pop where all collided paths
2098
+ // already exist after merge (committed on target). Safe to drop the stash
2099
+ // for the full alreadyExists set — they were untracked on source by
2100
+ // definition of the "already exists, no checkout" failure.
2382
2101
  if (stashRefForDrop) {
2383
2102
  try {
2384
2103
  execFileSync("git", ["stash", "drop", stashRefForDrop], {
@@ -2397,10 +2116,6 @@ export function mergeMilestoneToMain(
2397
2116
  logWarning("reconcile", "Stash pop conflict on non-.gsd files after merge", {
2398
2117
  files: nonGsdUU.join(", "),
2399
2118
  });
2400
- } else if (nonGsdAlreadyExists.length > 0) {
2401
- logWarning("reconcile", "Stash pop restore collision on non-.gsd files after merge", {
2402
- files: nonGsdAlreadyExists.join(", "),
2403
- });
2404
2119
  } else {
2405
2120
  logWarning(
2406
2121
  "worktree",
@@ -2518,62 +2233,24 @@ export function mergeMilestoneToMain(
2518
2233
  };
2519
2234
 
2520
2235
  let shouldCleanup = false;
2521
- let pushed = false;
2522
- let prCreated = false;
2523
2236
  try {
2524
- // 10. Auto-push if enabled
2525
- if (prefs.auto_push === true && prefs.auto_pr !== true && !nothingToCommit) {
2526
- const remote = prefs.remote ?? "origin";
2527
- if (gitRemoteExists(originalBasePath_, remote)) {
2528
- try {
2529
- execFileSync("git", ["push", remote, mainBranch], {
2530
- cwd: originalBasePath_,
2531
- stdio: ["ignore", "pipe", "pipe"],
2532
- encoding: "utf-8",
2533
- });
2534
- pushed = true;
2535
- } catch (err) {
2536
- // Push failure is non-fatal
2537
- logWarning("worktree", `git push failed: ${err instanceof Error ? err.message : String(err)}`);
2538
- }
2539
- }
2540
- }
2541
-
2542
- // 9b. Auto-create PR if enabled (#2302: no longer gated on pushed/auto_push)
2543
- if (prefs.auto_pr === true && !nothingToCommit) {
2544
- const remote = prefs.remote ?? "origin";
2545
- const prTarget = prefs.pr_target_branch ?? mainBranch;
2546
- if (gitRemoteExists(originalBasePath_, remote)) {
2547
- try {
2548
- // Push the milestone branch to remote first
2549
- execFileSync("git", ["push", remote, milestoneBranch], {
2550
- cwd: originalBasePath_,
2551
- stdio: ["ignore", "pipe", "pipe"],
2552
- encoding: "utf-8",
2553
- });
2554
- const prEvidence = buildPullRequestEvidence({
2555
- milestoneId,
2556
- milestoneTitle,
2557
- changeType: "feat",
2558
- summaries: completedSlices.map((slice) => `### ${slice.id}\n${slice.title}`),
2559
- testsRun: ["Auto-created after milestone merge. Run `npm run verify:merge` before marking this draft ready."],
2560
- rollbackNotes: ["Close the draft PR or revert the merge commit if review finds a behavior regression."],
2561
- how: "Generated by git.auto_pr after the milestone branch was pushed and merged locally.",
2562
- });
2563
- const prUrl = createDraftPullRequestFromEvidence(originalBasePath_, milestoneId, prEvidence, {
2564
- head: milestoneBranch,
2565
- base: prTarget,
2566
- });
2567
- if (!prUrl) {
2568
- throw new Error("gh pr create returned no URL");
2569
- }
2570
- prCreated = true;
2571
- } catch (err) {
2572
- // PR creation failure is non-fatal — gh may not be installed or authenticated
2573
- logWarning("worktree", `PR creation failed: ${err instanceof Error ? err.message : String(err)}`);
2574
- }
2575
- }
2576
- }
2237
+ // 10/9b. Publication (auto-push / draft PR) — Publication module seam (ADR-034).
2238
+ const publication = publishMilestone({
2239
+ basePath: originalBasePath_,
2240
+ milestoneId,
2241
+ milestoneTitle,
2242
+ integrationBranch: mainBranch,
2243
+ milestoneBranch,
2244
+ sliceSummaries: completedSlices.map((slice) => `### ${slice.id}\n${slice.title}`),
2245
+ nothingToCommit,
2246
+ prefs: {
2247
+ autoPush: prefs.auto_push === true,
2248
+ autoPr: prefs.auto_pr === true,
2249
+ remote: prefs.remote,
2250
+ prTargetBranch: prefs.pr_target_branch,
2251
+ },
2252
+ });
2253
+ const { pushed, prCreated } = publication;
2577
2254
 
2578
2255
  // 11. Guard removed — step 9b (#1792) now handles this with a smarter check:
2579
2256
  // throws only when the milestone has unanchored code changes, passes
@@ -81,6 +81,7 @@ import {
81
81
  resolveAutoSupervisorConfig,
82
82
  loadEffectiveGSDPreferences,
83
83
  getIsolationMode,
84
+ resolveEffectiveUnitIsolationMode,
84
85
  } from "./preferences.js";
85
86
  import { playNotificationBell, sendDesktopNotification } from "./notifications.js";
86
87
  import type { GSDPreferences } from "./preferences.js";
@@ -330,6 +331,11 @@ import {
330
331
  } from "./db/auto-workers.js";
331
332
  import { releaseMilestoneLease } from "./db/milestone-leases.js";
332
333
  import { normalizeRealPath } from "./paths.js";
334
+ import {
335
+ formatStopNoticePrefix,
336
+ isBlockedStopReason,
337
+ stopNoticeDisplayReason,
338
+ } from "./stop-notice.js";
333
339
 
334
340
  // ── ENCAPSULATION INVARIANT ─────────────────────────────────────────────────
335
341
  // ALL mutable auto-mode state lives in the AutoSession class (auto/session.ts).
@@ -354,19 +360,7 @@ export function formatAutoStopNotification(prefix: string, totals: { cost: numbe
354
360
  ].join("\n");
355
361
  }
356
362
 
357
- function isBlockedStopReason(reason?: string | null): boolean {
358
- return /^Blocked:\s*/i.test(reason ?? "");
359
- }
360
-
361
- function formatAutoStopDisplayReason(reason?: string | null): string {
362
- return (reason ?? "").replace(/^Blocked:\s*/i, "").trim();
363
- }
364
-
365
- export function formatAutoStopNotificationPrefix(reason?: string | null): string {
366
- const displayReason = formatAutoStopDisplayReason(reason);
367
- const prefix = isBlockedStopReason(reason) ? "Auto-mode blocked" : "Auto-mode stopped";
368
- return displayReason ? `${prefix} — ${displayReason}` : prefix;
369
- }
363
+ export { formatStopNoticePrefix as formatAutoStopNotificationPrefix } from "./stop-notice.js";
370
364
 
371
365
  function clearSessionModelOverrideForCommandSession(ctx?: ExtensionContext | null): void {
372
366
  const sessionId =
@@ -634,22 +628,24 @@ export function shouldUseWorktreeIsolation(basePath?: string): boolean {
634
628
 
635
629
  type AutoIsolationMode = ReturnType<typeof getIsolationMode>;
636
630
 
637
- function resolveEffectiveUnitIsolationMode(
638
- configuredMode: AutoIsolationMode,
639
- isolationDegraded: boolean,
640
- ): AutoIsolationMode {
641
- return configuredMode === "worktree" && isolationDegraded ? "branch" : configuredMode;
642
- }
643
-
644
631
  export function _resolveEffectiveUnitIsolationModeForTest(
645
632
  configuredMode: AutoIsolationMode,
646
633
  isolationDegraded: boolean,
634
+ strandedRecoveryIsolationMode: "worktree" | "branch" | null = null,
647
635
  ): AutoIsolationMode {
648
- return resolveEffectiveUnitIsolationMode(configuredMode, isolationDegraded);
636
+ return resolveEffectiveUnitIsolationMode(
637
+ configuredMode,
638
+ isolationDegraded,
639
+ strandedRecoveryIsolationMode,
640
+ );
649
641
  }
650
642
 
651
643
  function getEffectiveUnitIsolationMode(basePath: string): AutoIsolationMode {
652
- return resolveEffectiveUnitIsolationMode(getIsolationMode(basePath), s.isolationDegraded);
644
+ return resolveEffectiveUnitIsolationMode(
645
+ getIsolationMode(basePath),
646
+ s.isolationDegraded,
647
+ s.strandedRecoveryIsolationMode,
648
+ );
653
649
  }
654
650
 
655
651
  /** Crash recovery prompt — set by startAuto, consumed by the main loop */
@@ -1462,8 +1458,8 @@ export async function stopAuto(
1462
1458
  ): Promise<void> {
1463
1459
  if (!s.active && !s.paused) return;
1464
1460
  const loadedPreferences = loadEffectiveGSDPreferences(s.basePath || undefined)?.preferences;
1465
- const stopNotificationPrefix = formatAutoStopNotificationPrefix(reason);
1466
- const displayReason = formatAutoStopDisplayReason(reason);
1461
+ const stopNotificationPrefix = formatStopNoticePrefix(reason);
1462
+ const displayReason = stopNoticeDisplayReason(reason);
1467
1463
  const isHeadlessStop = process.env.GSD_HEADLESS === "1";
1468
1464
  const completionStopRequested = Boolean(options.completionWidget);
1469
1465
  const preserveCloseoutTranscript = !isHeadlessStop && (