@opengsd/gsd-pi 1.4.0 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (329) hide show
  1. package/README.md +11 -11
  2. package/dist/onboarding.js +11 -0
  3. package/dist/resources/.managed-resources-content-hash +1 -1
  4. package/dist/resources/GSD-WORKFLOW.md +20 -25
  5. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +118 -53
  6. package/dist/resources/extensions/cursor-cli/index.js +16 -0
  7. package/dist/resources/extensions/cursor-cli/models.js +63 -0
  8. package/dist/resources/extensions/cursor-cli/package.json +11 -0
  9. package/dist/resources/extensions/cursor-cli/readiness.js +128 -0
  10. package/dist/resources/extensions/cursor-cli/stream-adapter.js +353 -0
  11. package/dist/resources/extensions/gsd/activity-log.js +4 -1
  12. package/dist/resources/extensions/gsd/artifact-verification.js +25 -9
  13. package/dist/resources/extensions/gsd/auto/closeout.js +45 -42
  14. package/dist/resources/extensions/gsd/auto/finalize.js +9 -0
  15. package/dist/resources/extensions/gsd/auto/orchestrator.js +1 -1
  16. package/dist/resources/extensions/gsd/auto/run-unit.js +5 -2
  17. package/dist/resources/extensions/gsd/auto/schedule-wakeup.js +3 -0
  18. package/dist/resources/extensions/gsd/auto-dispatch.js +15 -5
  19. package/dist/resources/extensions/gsd/auto-model-selection.js +36 -18
  20. package/dist/resources/extensions/gsd/auto-prompts.js +76 -2
  21. package/dist/resources/extensions/gsd/auto-start.js +4 -3
  22. package/dist/resources/extensions/gsd/auto-timeout-recovery.js +4 -0
  23. package/dist/resources/extensions/gsd/auto-worktree.js +38 -11
  24. package/dist/resources/extensions/gsd/auto.js +23 -3
  25. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +3 -0
  26. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +114 -5
  27. package/dist/resources/extensions/gsd/bootstrap/schedule-wakeup-tool.js +58 -9
  28. package/dist/resources/extensions/gsd/bootstrap/tool-call-loop-guard.js +10 -2
  29. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +14 -1
  30. package/dist/resources/extensions/gsd/codebase-generator.js +157 -19
  31. package/dist/resources/extensions/gsd/commands/handlers/core.js +40 -4
  32. package/dist/resources/extensions/gsd/commands-prefs-wizard.js +153 -1
  33. package/dist/resources/extensions/gsd/commands-worktree.js +11 -2
  34. package/dist/resources/extensions/gsd/db/queries.js +39 -0
  35. package/dist/resources/extensions/gsd/discussion-handoff.js +8 -3
  36. package/dist/resources/extensions/gsd/docs/preferences-reference.md +8 -2
  37. package/dist/resources/extensions/gsd/doctor-engine-checks.js +206 -14
  38. package/dist/resources/extensions/gsd/doctor-providers.js +38 -10
  39. package/dist/resources/extensions/gsd/doctor.js +93 -3
  40. package/dist/resources/extensions/gsd/exec-sandbox.js +6 -2
  41. package/dist/resources/extensions/gsd/flat-phase-migration.js +74 -23
  42. package/dist/resources/extensions/gsd/git-service.js +14 -18
  43. package/dist/resources/extensions/gsd/gsd-db.js +52 -0
  44. package/dist/resources/extensions/gsd/guidance.js +1 -0
  45. package/dist/resources/extensions/gsd/guided-flow.js +40 -2
  46. package/dist/resources/extensions/gsd/key-manager.js +1 -0
  47. package/dist/resources/extensions/gsd/markdown-renderer.js +25 -8
  48. package/dist/resources/extensions/gsd/migrate-external.js +24 -7
  49. package/dist/resources/extensions/gsd/milestone-merge-transaction.js +3 -3
  50. package/dist/resources/extensions/gsd/preferences-diagnostics.js +24 -1
  51. package/dist/resources/extensions/gsd/preferences-models.js +6 -4
  52. package/dist/resources/extensions/gsd/preferences-validation.js +25 -2
  53. package/dist/resources/extensions/gsd/preferences.js +29 -2
  54. package/dist/resources/extensions/gsd/prompts/plan-slice.md +4 -2
  55. package/dist/resources/extensions/gsd/redact-secrets.js +37 -0
  56. package/dist/resources/extensions/gsd/repo-identity.js +16 -0
  57. package/dist/resources/extensions/gsd/repository-registry.js +6 -1
  58. package/dist/resources/extensions/gsd/root-write-leak-guard.js +4 -2
  59. package/dist/resources/extensions/gsd/safe-fs.js +23 -2
  60. package/dist/resources/extensions/gsd/skills/gsd-headless/SKILL.md +7 -9
  61. package/dist/resources/extensions/gsd/skills/gsd-headless/references/commands.md +1 -0
  62. package/dist/resources/extensions/gsd/tools/plan-task.js +76 -8
  63. package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +40 -0
  64. package/dist/resources/extensions/gsd/unit-context-manifest.js +5 -4
  65. package/dist/resources/extensions/gsd/unit-registry.js +27 -2
  66. package/dist/resources/extensions/gsd/unit-runtime.js +21 -0
  67. package/dist/resources/extensions/gsd/workspace.js +26 -8
  68. package/dist/resources/extensions/gsd/worktree-lifecycle.js +67 -5
  69. package/dist/resources/extensions/gsd/worktree-manager.js +97 -4
  70. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  71. package/dist/web/standalone/.next/BUILD_ID +1 -1
  72. package/dist/web/standalone/.next/app-path-routes-manifest.json +7 -7
  73. package/dist/web/standalone/.next/build-manifest.json +2 -2
  74. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  75. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  76. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  77. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  78. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  79. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  80. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  81. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  82. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  83. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  84. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  85. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  86. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  87. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  88. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  89. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  90. package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
  91. package/dist/web/standalone/.next/server/app/index.html +1 -1
  92. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  93. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  94. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  95. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  96. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  97. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  98. package/dist/web/standalone/.next/server/app-paths-manifest.json +7 -7
  99. package/dist/web/standalone/.next/server/chunks/8357.js +1 -1
  100. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  101. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  102. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  103. package/dist/web/standalone/node_modules/@gsd/native/package.json +1 -1
  104. package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
  105. package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
  106. package/dist/wizard.d.ts +8 -0
  107. package/dist/wizard.js +8 -1
  108. package/integrations/hermes/CONTEXT.md +47 -0
  109. package/integrations/hermes/README.md +21 -0
  110. package/integrations/hermes/__init__.py +31 -0
  111. package/integrations/hermes/docs/issue-1162-grilling.md +197 -0
  112. package/integrations/hermes/docs/setup.md +4 -0
  113. package/integrations/hermes/open_gsd_hermes/commands.py +141 -0
  114. package/integrations/hermes/open_gsd_hermes/config.py +2 -0
  115. package/integrations/hermes/open_gsd_hermes/gsd_client.py +594 -31
  116. package/integrations/hermes/open_gsd_hermes/notifications.py +10 -1
  117. package/integrations/hermes/plugin.yaml +4 -0
  118. package/integrations/hermes/tests/test_config.py +6 -0
  119. package/integrations/hermes/tests/test_directory_loader.py +37 -0
  120. package/integrations/hermes/tests/test_gsd_client_cache.py +3 -0
  121. package/integrations/hermes/tests/test_gsd_client_execute.py +35 -0
  122. package/integrations/hermes/tests/test_gsd_client_process_cwd.py +22 -0
  123. package/integrations/hermes/tests/test_gsd_client_timeout.py +98 -0
  124. package/integrations/hermes/tests/test_milestone_client.py +447 -0
  125. package/integrations/hermes/tests/test_new_milestone_command.py +313 -0
  126. package/integrations/hermes/tests/test_supervisor_fsm.py +40 -0
  127. package/package.json +8 -8
  128. package/packages/cloud-mcp-gateway/dist/auth-store.d.ts +1 -0
  129. package/packages/cloud-mcp-gateway/dist/auth-store.d.ts.map +1 -1
  130. package/packages/cloud-mcp-gateway/dist/auth-store.js +21 -1
  131. package/packages/cloud-mcp-gateway/dist/auth-store.js.map +1 -1
  132. package/packages/cloud-mcp-gateway/dist/auth-store.test.js +34 -0
  133. package/packages/cloud-mcp-gateway/dist/auth-store.test.js.map +1 -1
  134. package/packages/cloud-mcp-gateway/package.json +2 -2
  135. package/packages/contracts/package.json +1 -1
  136. package/packages/daemon/dist/cloud-runtime.d.ts +2 -0
  137. package/packages/daemon/dist/cloud-runtime.d.ts.map +1 -1
  138. package/packages/daemon/dist/cloud-runtime.js +25 -1
  139. package/packages/daemon/dist/cloud-runtime.js.map +1 -1
  140. package/packages/daemon/dist/cloud-runtime.test.js +51 -0
  141. package/packages/daemon/dist/cloud-runtime.test.js.map +1 -1
  142. package/packages/daemon/dist/message-batcher.d.ts +7 -0
  143. package/packages/daemon/dist/message-batcher.d.ts.map +1 -1
  144. package/packages/daemon/dist/message-batcher.js +13 -0
  145. package/packages/daemon/dist/message-batcher.js.map +1 -1
  146. package/packages/daemon/dist/message-batcher.test.js +48 -0
  147. package/packages/daemon/dist/message-batcher.test.js.map +1 -1
  148. package/packages/daemon/dist/session-manager.js +1 -1
  149. package/packages/daemon/dist/session-manager.js.map +1 -1
  150. package/packages/daemon/package.json +5 -5
  151. package/packages/gsd-agent-core/dist/compaction/compaction.d.ts +13 -1
  152. package/packages/gsd-agent-core/dist/compaction/compaction.d.ts.map +1 -1
  153. package/packages/gsd-agent-core/dist/compaction/compaction.js +15 -1
  154. package/packages/gsd-agent-core/dist/compaction/compaction.js.map +1 -1
  155. package/packages/gsd-agent-core/dist/session/agent-session-navigation.d.ts.map +1 -1
  156. package/packages/gsd-agent-core/dist/session/agent-session-navigation.js +4 -1
  157. package/packages/gsd-agent-core/dist/session/agent-session-navigation.js.map +1 -1
  158. package/packages/gsd-agent-core/package.json +5 -5
  159. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-segment-walker.d.ts.map +1 -1
  160. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-segment-walker.js +1 -0
  161. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-segment-walker.js.map +1 -1
  162. package/packages/gsd-agent-modes/dist/modes/rpc/rpc-client.js +1 -1
  163. package/packages/gsd-agent-modes/dist/modes/rpc/rpc-client.js.map +1 -1
  164. package/packages/gsd-agent-modes/package.json +7 -7
  165. package/packages/mcp-server/dist/server.d.ts +18 -5
  166. package/packages/mcp-server/dist/server.d.ts.map +1 -1
  167. package/packages/mcp-server/dist/server.js +46 -5
  168. package/packages/mcp-server/dist/server.js.map +1 -1
  169. package/packages/mcp-server/dist/session-manager.js +1 -1
  170. package/packages/mcp-server/dist/session-manager.js.map +1 -1
  171. package/packages/mcp-server/package.json +4 -4
  172. package/packages/native/package.json +1 -1
  173. package/packages/pi-agent-core/package.json +1 -1
  174. package/packages/pi-ai/dist/env-api-keys.d.ts.map +1 -1
  175. package/packages/pi-ai/dist/env-api-keys.js +3 -0
  176. package/packages/pi-ai/dist/env-api-keys.js.map +1 -1
  177. package/packages/pi-ai/dist/types.d.ts +2 -2
  178. package/packages/pi-ai/dist/types.d.ts.map +1 -1
  179. package/packages/pi-ai/dist/types.js.map +1 -1
  180. package/packages/pi-ai/package.json +1 -1
  181. package/packages/pi-coding-agent/dist/core/model-resolver.d.ts.map +1 -1
  182. package/packages/pi-coding-agent/dist/core/model-resolver.js +1 -0
  183. package/packages/pi-coding-agent/dist/core/model-resolver.js.map +1 -1
  184. package/packages/pi-coding-agent/dist/core/session-manager-types.d.ts +1 -0
  185. package/packages/pi-coding-agent/dist/core/session-manager-types.d.ts.map +1 -1
  186. package/packages/pi-coding-agent/dist/core/session-manager-types.js.map +1 -1
  187. package/packages/pi-coding-agent/dist/core/session-manager.d.ts.map +1 -1
  188. package/packages/pi-coding-agent/dist/core/session-manager.js +3 -0
  189. package/packages/pi-coding-agent/dist/core/session-manager.js.map +1 -1
  190. package/packages/pi-coding-agent/package.json +7 -7
  191. package/packages/pi-tui/package.json +2 -2
  192. package/packages/rpc-client/dist/rpc-client.js +1 -1
  193. package/packages/rpc-client/dist/rpc-client.js.map +1 -1
  194. package/packages/rpc-client/package.json +2 -2
  195. package/pkg/package.json +1 -1
  196. package/scripts/link-workspace-packages.cjs +14 -4
  197. package/src/resources/GSD-WORKFLOW.md +20 -25
  198. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +130 -51
  199. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +371 -39
  200. package/src/resources/extensions/cursor-cli/index.ts +18 -0
  201. package/src/resources/extensions/cursor-cli/models.ts +65 -0
  202. package/src/resources/extensions/cursor-cli/package.json +11 -0
  203. package/src/resources/extensions/cursor-cli/readiness.ts +143 -0
  204. package/src/resources/extensions/cursor-cli/stream-adapter.ts +440 -0
  205. package/src/resources/extensions/cursor-cli/tests/index.test.ts +54 -0
  206. package/src/resources/extensions/cursor-cli/tests/readiness.test.ts +29 -0
  207. package/src/resources/extensions/cursor-cli/tests/stream-adapter.test.ts +166 -0
  208. package/src/resources/extensions/gsd/activity-log.ts +4 -1
  209. package/src/resources/extensions/gsd/artifact-verification.ts +25 -9
  210. package/src/resources/extensions/gsd/auto/closeout.ts +54 -56
  211. package/src/resources/extensions/gsd/auto/finalize.ts +8 -0
  212. package/src/resources/extensions/gsd/auto/loop-deps.ts +1 -0
  213. package/src/resources/extensions/gsd/auto/orchestrator.ts +1 -1
  214. package/src/resources/extensions/gsd/auto/run-unit.ts +5 -2
  215. package/src/resources/extensions/gsd/auto/schedule-wakeup.ts +8 -0
  216. package/src/resources/extensions/gsd/auto-dispatch.ts +17 -5
  217. package/src/resources/extensions/gsd/auto-model-selection.ts +49 -18
  218. package/src/resources/extensions/gsd/auto-prompts.ts +73 -2
  219. package/src/resources/extensions/gsd/auto-start.ts +4 -3
  220. package/src/resources/extensions/gsd/auto-timeout-recovery.ts +4 -0
  221. package/src/resources/extensions/gsd/auto-worktree.ts +50 -11
  222. package/src/resources/extensions/gsd/auto.ts +22 -3
  223. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +3 -0
  224. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +128 -5
  225. package/src/resources/extensions/gsd/bootstrap/schedule-wakeup-tool.ts +80 -12
  226. package/src/resources/extensions/gsd/bootstrap/tool-call-loop-guard.ts +12 -2
  227. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +23 -1
  228. package/src/resources/extensions/gsd/codebase-generator.ts +179 -18
  229. package/src/resources/extensions/gsd/commands/handlers/core.ts +39 -3
  230. package/src/resources/extensions/gsd/commands-prefs-wizard.ts +135 -1
  231. package/src/resources/extensions/gsd/commands-worktree.ts +13 -2
  232. package/src/resources/extensions/gsd/db/queries.ts +40 -0
  233. package/src/resources/extensions/gsd/discussion-handoff.ts +10 -2
  234. package/src/resources/extensions/gsd/docs/preferences-reference.md +8 -2
  235. package/src/resources/extensions/gsd/doctor-engine-checks.ts +269 -18
  236. package/src/resources/extensions/gsd/doctor-providers.ts +39 -10
  237. package/src/resources/extensions/gsd/doctor-types.ts +5 -1
  238. package/src/resources/extensions/gsd/doctor.ts +95 -3
  239. package/src/resources/extensions/gsd/exec-sandbox.ts +6 -2
  240. package/src/resources/extensions/gsd/flat-phase-migration.ts +98 -22
  241. package/src/resources/extensions/gsd/git-service.ts +14 -17
  242. package/src/resources/extensions/gsd/gsd-db.ts +72 -0
  243. package/src/resources/extensions/gsd/guidance.ts +2 -0
  244. package/src/resources/extensions/gsd/guided-flow.ts +45 -2
  245. package/src/resources/extensions/gsd/key-manager.ts +1 -0
  246. package/src/resources/extensions/gsd/markdown-renderer.ts +25 -7
  247. package/src/resources/extensions/gsd/migrate-external.ts +23 -6
  248. package/src/resources/extensions/gsd/milestone-merge-transaction.ts +4 -4
  249. package/src/resources/extensions/gsd/model-router.ts +4 -5
  250. package/src/resources/extensions/gsd/preferences-diagnostics.ts +24 -0
  251. package/src/resources/extensions/gsd/preferences-models.ts +6 -3
  252. package/src/resources/extensions/gsd/preferences-validation.ts +28 -2
  253. package/src/resources/extensions/gsd/preferences.ts +38 -4
  254. package/src/resources/extensions/gsd/prompts/plan-slice.md +4 -2
  255. package/src/resources/extensions/gsd/redact-secrets.ts +40 -0
  256. package/src/resources/extensions/gsd/repo-identity.ts +17 -0
  257. package/src/resources/extensions/gsd/repository-registry.ts +7 -1
  258. package/src/resources/extensions/gsd/root-write-leak-guard.ts +3 -2
  259. package/src/resources/extensions/gsd/safe-fs.ts +22 -2
  260. package/src/resources/extensions/gsd/skills/gsd-headless/SKILL.md +7 -9
  261. package/src/resources/extensions/gsd/skills/gsd-headless/references/commands.md +1 -0
  262. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +131 -5
  263. package/src/resources/extensions/gsd/tests/auto-migrating-recovery.test.ts +18 -0
  264. package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +178 -0
  265. package/src/resources/extensions/gsd/tests/auto-phases-lifecycle.test.ts +43 -1
  266. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +54 -0
  267. package/src/resources/extensions/gsd/tests/auto-remote-session-lock-cleanup.test.ts +41 -0
  268. package/src/resources/extensions/gsd/tests/auto-runtime-state.test.ts +41 -0
  269. package/src/resources/extensions/gsd/tests/auto-session-scope.test.ts +4 -4
  270. package/src/resources/extensions/gsd/tests/auto-start-bootstrap-await-3420.test.ts +3 -0
  271. package/src/resources/extensions/gsd/tests/auto-timeout-recovery-harness-abort.test.ts +55 -0
  272. package/src/resources/extensions/gsd/tests/auto-warning-noise-regression.test.ts +8 -0
  273. package/src/resources/extensions/gsd/tests/batch-task-query.test.ts +85 -0
  274. package/src/resources/extensions/gsd/tests/check-auto-start-ready-guard.test.ts +29 -0
  275. package/src/resources/extensions/gsd/tests/codebase-generator.test.ts +365 -0
  276. package/src/resources/extensions/gsd/tests/complete-milestone-excerpt.test.ts +62 -1
  277. package/src/resources/extensions/gsd/tests/doctor-providers.test.ts +138 -1
  278. package/src/resources/extensions/gsd/tests/doctor-scope-db-unavailable.test.ts +397 -5
  279. package/src/resources/extensions/gsd/tests/doctor-workspace.test.ts +207 -0
  280. package/src/resources/extensions/gsd/tests/flat-phase-migration.test.ts +38 -1
  281. package/src/resources/extensions/gsd/tests/flat-rate-routing-guard.test.ts +87 -19
  282. package/src/resources/extensions/gsd/tests/gate-1b-orphan-discrimination.test.ts +89 -0
  283. package/src/resources/extensions/gsd/tests/guided-flow-session-isolation.test.ts +9 -0
  284. package/src/resources/extensions/gsd/tests/integration/state-machine-edge-cases.test.ts +4 -2
  285. package/src/resources/extensions/gsd/tests/markdown-renderer.test.ts +122 -0
  286. package/src/resources/extensions/gsd/tests/merge-conflict-stops-loop.test.ts +4 -4
  287. package/src/resources/extensions/gsd/tests/migrate-external-worktree.test.ts +116 -0
  288. package/src/resources/extensions/gsd/tests/milestone-merge-stash-restore.test.ts +152 -22
  289. package/src/resources/extensions/gsd/tests/milestone-merge-transaction.test.ts +1 -1
  290. package/src/resources/extensions/gsd/tests/pending-autostart-scope.test.ts +2 -2
  291. package/src/resources/extensions/gsd/tests/phases-merge-error-stops-auto.test.ts +50 -6
  292. package/src/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +17 -0
  293. package/src/resources/extensions/gsd/tests/plan-task.test.ts +264 -1
  294. package/src/resources/extensions/gsd/tests/preferences.test.ts +150 -0
  295. package/src/resources/extensions/gsd/tests/prefs-wizard-coverage.test.ts +142 -0
  296. package/src/resources/extensions/gsd/tests/recovery-verify-logs.test.ts +4 -4
  297. package/src/resources/extensions/gsd/tests/redact-secrets.test.ts +56 -0
  298. package/src/resources/extensions/gsd/tests/register-hooks-flat-phase-migration.test.ts +73 -0
  299. package/src/resources/extensions/gsd/tests/register-hooks-loop-guard-auto.test.ts +275 -0
  300. package/src/resources/extensions/gsd/tests/repository-registry.test.ts +41 -2
  301. package/src/resources/extensions/gsd/tests/require-slice-discussion-dispatch.test.ts +55 -0
  302. package/src/resources/extensions/gsd/tests/root-write-leak-guard.test.ts +31 -9
  303. package/src/resources/extensions/gsd/tests/schedule-wakeup-tool.test.ts +167 -0
  304. package/src/resources/extensions/gsd/tests/stalled-tool-recovery.test.ts +49 -0
  305. package/src/resources/extensions/gsd/tests/state-corruption-2945.test.ts +1 -1
  306. package/src/resources/extensions/gsd/tests/status-per-repo.test.ts +94 -0
  307. package/src/resources/extensions/gsd/tests/token-tool-gating.test.ts +35 -0
  308. package/src/resources/extensions/gsd/tests/tool-call-loop-guard.test.ts +63 -0
  309. package/src/resources/extensions/gsd/tests/unit-context-manifest.test.ts +66 -18
  310. package/src/resources/extensions/gsd/tests/unit-registry.test.ts +56 -0
  311. package/src/resources/extensions/gsd/tests/unit-runtime.test.ts +30 -0
  312. package/src/resources/extensions/gsd/tests/uok-gitops-turn-action.test.ts +52 -0
  313. package/src/resources/extensions/gsd/tests/validator-scope-parity.test.ts +53 -1
  314. package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +208 -1
  315. package/src/resources/extensions/gsd/tests/workspace.test.ts +18 -4
  316. package/src/resources/extensions/gsd/tests/worktree-journal-events.test.ts +4 -4
  317. package/src/resources/extensions/gsd/tests/worktree-lifecycle.test.ts +2 -2
  318. package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +31 -1
  319. package/src/resources/extensions/gsd/tests/worktree-teardown-safety.test.ts +32 -0
  320. package/src/resources/extensions/gsd/tools/plan-task.ts +107 -9
  321. package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +58 -0
  322. package/src/resources/extensions/gsd/unit-context-manifest.ts +11 -5
  323. package/src/resources/extensions/gsd/unit-registry.ts +34 -2
  324. package/src/resources/extensions/gsd/unit-runtime.ts +42 -0
  325. package/src/resources/extensions/gsd/workspace.ts +42 -8
  326. package/src/resources/extensions/gsd/worktree-lifecycle.ts +135 -11
  327. package/src/resources/extensions/gsd/worktree-manager.ts +143 -5
  328. /package/dist/web/standalone/.next/static/{zQLcXm0Za8lexoXtgDpMG → bCg3aYrFN1DKZr2dqgE7L}/_buildManifest.js +0 -0
  329. /package/dist/web/standalone/.next/static/{zQLcXm0Za8lexoXtgDpMG → bCg3aYrFN1DKZr2dqgE7L}/_ssgManifest.js +0 -0
@@ -596,7 +596,7 @@ export const DISPATCH_RULES = [
596
596
  },
597
597
  },
598
598
  {
599
- name: "uat-verdict-gate (non-PASS blocks progression)",
599
+ name: "uat-verdict-gate (non-PASS observed; closeout enforces)",
600
600
  match: async ({ mid, basePath, prefs }) => {
601
601
  // Only applies when UAT dispatch is enabled
602
602
  if (!prefs?.uat_dispatch)
@@ -611,9 +611,9 @@ export const DISPATCH_RULES = [
611
611
  continue;
612
612
  const { verdict, uatType } = result;
613
613
  if (!isAcceptableUatVerdict(verdict, uatType)) {
614
- // Do not hard-stop auto-mode on non-PASS verdicts. Allow progression
615
- // so follow-up slices can remediate, while complete-milestone still
616
- // enforces manual UAT PASS sign-off before closure.
614
+ // Observe non-PASS verdicts without hard-stopping auto-mode. Allow
615
+ // progression so follow-up slices can remediate, while
616
+ // complete-milestone still enforces manual UAT PASS sign-off before closure.
617
617
  continue;
618
618
  }
619
619
  }
@@ -902,9 +902,19 @@ export const DISPATCH_RULES = [
902
902
  const sliceContextFile = resolveSliceFile(basePath, mid, state.activeSlice.id, "CONTEXT");
903
903
  if (sliceContextFile && existsSync(sliceContextFile))
904
904
  return null; // discussion already done, proceed
905
+ const closedSliceIds = getClosedSliceIds(mid);
906
+ const justClosedSliceId = closedSliceIds[closedSliceIds.length - 1];
907
+ let priorVerdictWarning = "";
908
+ if (justClosedSliceId) {
909
+ const prior = await readUatGateVerdict(basePath, mid, justClosedSliceId);
910
+ if (prior && !isAcceptableUatVerdict(prior.verdict, prior.uatType)) {
911
+ priorVerdictWarning =
912
+ ` Note: the slice just closed (${justClosedSliceId}) recorded a non-PASS UAT verdict (${prior.verdict.toUpperCase()}); review before continuing.`;
913
+ }
914
+ }
905
915
  return {
906
916
  action: "stop",
907
- reason: `Slice ${state.activeSlice.id} requires discussion before planning (require_slice_discussion is enabled). Run /gsd discuss to discuss this slice, then /gsd auto to resume.`,
917
+ reason: `Slice ${state.activeSlice.id} requires discussion before planning (require_slice_discussion is enabled). Run /gsd discuss to discuss this slice, then /gsd auto to resume.${priorVerdictWarning}`,
908
918
  level: "warning",
909
919
  };
910
920
  },
@@ -326,7 +326,7 @@ export function floorThinkingLevelForUnit(unitType, level) {
326
326
  return EXECUTE_TASK_MIN_THINKING_LEVEL;
327
327
  }
328
328
  export function resolvePreferredModelConfig(unitType, autoModeStartModel, isAutoMode = true, basePath, availableModelIds, preferredModelId) {
329
- const explicitConfig = resolveModelWithFallbacksForUnit(unitType, basePath, availableModelIds, preferredModelId);
329
+ const explicitConfig = resolveModelWithFallbacksForUnit(unitType, basePath, availableModelIds, preferredModelId, true);
330
330
  if (explicitConfig) {
331
331
  return {
332
332
  ...explicitConfig,
@@ -338,23 +338,45 @@ export function resolvePreferredModelConfig(unitType, autoModeStartModel, isAuto
338
338
  if (!isAutoMode)
339
339
  return undefined;
340
340
  const routingConfig = resolveDynamicRoutingConfig();
341
- if (!routingConfig.enabled || !routingConfig.tier_models)
341
+ if (!routingConfig.enabled)
342
342
  return undefined;
343
- // Don't synthesize a routing config for flat-rate providers (#3453).
344
- // Users can opt into routing for flat-rate subscriptions (e.g. claude-code)
345
- // via dynamic_routing.allow_flat_rate_providers (#4386).
346
- if (!routingConfig.allow_flat_rate_providers &&
343
+ // Only an explicit flat-rate opt-out suppresses synthesized routing.
344
+ // `dynamic_routing.enabled: true` is user intent to run routing even when
345
+ // the start provider is a flat-rate subscription. Applied ahead of both the
346
+ // tier-pin and profile-derived synthesis paths so the opt-out is uniform.
347
+ if (routingConfig.allow_flat_rate_providers === false &&
347
348
  autoModeStartModel &&
348
349
  isFlatRateProvider(autoModeStartModel.provider, autoModeStartModel.flatRateCtx)) {
349
350
  return undefined;
350
351
  }
351
- const ceilingModel = routingConfig.tier_models.heavy
352
- ?? (autoModeStartModel ? `${autoModeStartModel.provider}/${autoModeStartModel.id}` : undefined);
353
- if (!ceilingModel)
352
+ // With explicit `tier_models` pins, synthesize a heavy-tier ceiling that
353
+ // dynamic routing downgrades from per unit.
354
+ if (routingConfig.tier_models) {
355
+ const ceilingModel = routingConfig.tier_models.heavy
356
+ ?? (autoModeStartModel ? `${autoModeStartModel.provider}/${autoModeStartModel.id}` : undefined);
357
+ if (!ceilingModel)
358
+ return undefined;
359
+ return {
360
+ primary: ceilingModel,
361
+ fallbacks: [],
362
+ source: "synthesized",
363
+ };
364
+ }
365
+ // No `tier_models` pins: fall back to the token-profile's per-phase model so
366
+ // profile tier resolution survives. `skipProfileDefaults` is left false here
367
+ // (unlike the explicit-detection call above) precisely because we WANT the
368
+ // profile defaults this time — they are the routing ceiling, not a hard
369
+ // user selection. Labeling the result `synthesized` (not `explicit`) keeps
370
+ // dynamic routing and fallbacks enabled downstream. Without this branch,
371
+ // `resolvePreferredModelConfig` returns undefined for profile-only configs
372
+ // and auto dispatch bleeds the start model onto every unit, silently
373
+ // discarding the profile (#1115: routing/fallbacks disabled when a
374
+ // token_profile is set but no tier_models are pinned).
375
+ const profileConfig = resolveModelWithFallbacksForUnit(unitType, basePath, availableModelIds, preferredModelId, false);
376
+ if (!profileConfig)
354
377
  return undefined;
355
378
  return {
356
- primary: ceilingModel,
357
- fallbacks: [],
379
+ ...profileConfig,
358
380
  source: "synthesized",
359
381
  };
360
382
  }
@@ -510,15 +532,11 @@ autoModeStartThinkingLevel) {
510
532
  ]));
511
533
  }
512
534
  }
513
- // Disable routing for flat-rate providers like GitHub Copilot (#3453).
514
- // All models cost the same per request, so downgrading to a cheaper
515
- // model provides no cost benefit — it only degrades quality.
535
+ // Disable routing for flat-rate providers only when the user explicitly
536
+ // opts out with dynamic_routing.allow_flat_rate_providers: false.
516
537
  // Fail-closed: if primary model can't be resolved, fall back to
517
538
  // provider-level signals rather than allowing unwanted downgrades.
518
- // Opt-in: dynamic_routing.allow_flat_rate_providers skips the bypass so
519
- // claude-code subscribers can still get intelligent per-task selection
520
- // across their subscription (#4386).
521
- if (routingConfig.enabled && !routingConfig.allow_flat_rate_providers) {
539
+ if (routingConfig.enabled && routingConfig.allow_flat_rate_providers === false) {
522
540
  const primaryModel = resolveModelId(modelConfig.primary, routingEligibleModels, ctx.model?.provider);
523
541
  if (primaryModel) {
524
542
  const primaryFlatRateCtx = buildFlatRateContext(primaryModel.provider, ctx, prefs);
@@ -12,12 +12,13 @@ import { hasVerdict, extractVerdict } from "./verdict-parser.js";
12
12
  import { loadPrompt, inlineTemplate } from "./prompt-loader.js";
13
13
  import { resolveMilestoneFile, resolveSliceFile, resolveSlicePath, resolveTasksDir, resolveTaskFiles, resolveTaskFile, relMilestoneFile, relSliceFile, relSlicePath, relMilestonePath, relTaskFile, resolveGsdRootFile, relGsdRootFile, resolveRuntimeFile, } from "./paths.js";
14
14
  import { resolveInlineLevel, loadEffectiveGSDPreferences } from "./preferences.js";
15
+ import { createRepositoryRegistryFromPreferences } from "./repository-registry.js";
15
16
  import { isContextModeEnabled } from "./preferences-types.js";
16
17
  import { parseRoadmap } from "./parsers-legacy.js";
17
- import { join } from "node:path";
18
+ import { join, relative, sep } from "node:path";
18
19
  import { existsSync } from "node:fs";
19
20
  import { computeBudgets, resolveExecutorContextWindow, truncateAtSectionBoundary } from "./context-budget.js";
20
- import { getPendingGatesForTurn } from "./gsd-db.js";
21
+ import { getGateResults, getPendingGatesForTurn } from "./gsd-db.js";
21
22
  import { assertGateCoverage, getGatesForTurn, } from "./gate-registry.js";
22
23
  import { formatDecisionsCompact, formatRequirementsCompact } from "./structured-data-formatter.js";
23
24
  import { readPhaseAnchor, formatAnchorForPrompt } from "./phase-anchor.js";
@@ -295,6 +296,37 @@ function formatExecutorConstraints(sessionContextWindow, modelRegistry, sessionP
295
296
  `- Keep individual tasks completable within a single context window — if a task needs more context than fits, split it`,
296
297
  ].join("\n");
297
298
  }
299
+ /**
300
+ * Builds the Declared Repositories block for parent-workspace planners.
301
+ * Returns "" for single-repo projects so the common-case prompt is unchanged;
302
+ * only parent mode with declared child repos gets the registry list and the
303
+ * targetRepositories assignment instruction.
304
+ */
305
+ export function buildRepoRegistryBlock(base) {
306
+ let registry;
307
+ try {
308
+ registry = createRepositoryRegistryFromPreferences(base, loadEffectiveGSDPreferences(base)?.preferences);
309
+ }
310
+ catch {
311
+ return "";
312
+ }
313
+ const hasChildRepo = registry.repositories.some((repo) => repo.id !== "project");
314
+ if (registry.mode !== "parent" || !hasChildRepo)
315
+ return "";
316
+ const lines = registry.repositories.map((repo) => {
317
+ const relPath = relative(registry.projectRoot, repo.root).split(sep).join("/") || ".";
318
+ const role = repo.role ? ` — ${repo.role}` : "";
319
+ const root = repo.id === "project" ? " (root)" : ` (${relPath})`;
320
+ return `- \`${repo.id}\`${root}${role}`;
321
+ });
322
+ return [
323
+ "### Declared Repositories",
324
+ "",
325
+ ...lines,
326
+ "",
327
+ "This is a parent workspace. Assign each task's `targetRepositories` to the repository id(s) it touches (from the list above). Omit `targetRepositories` to inherit the slice-wide default from `gsd_plan_slice`; set it explicitly when a task differs (e.g. `[\"project\"]` for parent-root work).",
328
+ ].join("\n");
329
+ }
298
330
  /**
299
331
  * Returns a markdown bullet list of known context file paths for the given
300
332
  * milestone (and optionally slice). Falls back to a generic tool-agnostic
@@ -2174,6 +2206,7 @@ async function renderSlicePrompt(options) {
2174
2206
  inlinedContext,
2175
2207
  dependencySummaries: depContent,
2176
2208
  sourceFilePaths: buildSourceFilePaths(base, mid, sid),
2209
+ repoRegistry: buildRepoRegistryBlock(base),
2177
2210
  executorContextConstraints,
2178
2211
  commitInstruction,
2179
2212
  skillActivation: buildSkillActivationBlock({
@@ -2906,6 +2939,47 @@ export async function buildValidateMilestonePrompt(mid, midTitle, base, level) {
2906
2939
  inlined.push(outstandingBlock);
2907
2940
  trackPromptContext(contextTelemetry, "outstanding-items", "inline", outstandingBlock);
2908
2941
  }
2942
+ const persistedGateFlags = [];
2943
+ try {
2944
+ const gateLabels = {
2945
+ Q3: "Threat Surface",
2946
+ Q4: "Requirement Impact",
2947
+ };
2948
+ for (const sid of seenValSlices) {
2949
+ for (const gate of getGateResults(mid, sid, "slice")) {
2950
+ if (gate.gate_id !== "Q3" && gate.gate_id !== "Q4")
2951
+ continue;
2952
+ const findings = gate.findings.trim();
2953
+ const rationale = gate.rationale.trim();
2954
+ const verdict = gate.verdict ?? "";
2955
+ const hasNonPassVerdict = verdict !== "" && verdict !== "pass" && verdict !== "omitted";
2956
+ if (!findings && !hasNonPassVerdict)
2957
+ continue;
2958
+ const displayVerdict = verdict || gate.status;
2959
+ const detail = findings || rationale || "_No findings text recorded; reconcile the persisted non-pass verdict._";
2960
+ persistedGateFlags.push(`- **${sid} / ${gate.gate_id} (${gateLabels[gate.gate_id]}) / ${displayVerdict}:** ${detail}`);
2961
+ if (findings && rationale)
2962
+ persistedGateFlags.push(` - Rationale: ${rationale}`);
2963
+ }
2964
+ }
2965
+ }
2966
+ catch (err) {
2967
+ logWarning("prompt", `buildValidateMilestonePrompt persisted gate flags lookup failed: ${err instanceof Error ? err.message : String(err)}`);
2968
+ }
2969
+ if (persistedGateFlags.length > 0) {
2970
+ const persistedGateFlagsBlock = [
2971
+ "### Persisted Slice-Level Gate Flags (from quality_gates)",
2972
+ "",
2973
+ "These slice gates recorded findings or non-pass verdicts during execution. Reconcile each against the milestone-level evidence before returning MV03/MV04 verdicts.",
2974
+ "",
2975
+ ...persistedGateFlags,
2976
+ ].join("\n");
2977
+ inlined.push(persistedGateFlagsBlock);
2978
+ trackPromptContext(contextTelemetry, "persisted-slice-gate-flags", "inline", persistedGateFlagsBlock);
2979
+ }
2980
+ else {
2981
+ trackPromptContext(contextTelemetry, "persisted-slice-gate-flags", "skipped", null, "none");
2982
+ }
2909
2983
  // Inline existing VALIDATION file if this is a re-validation round
2910
2984
  const validationPath = resolveMilestoneFile(base, mid, "VALIDATION");
2911
2985
  const validationRel = relMilestoneFile(base, mid, "VALIDATION");
@@ -1435,15 +1435,16 @@ export async function bootstrapAutoSession(s, ctx, pi, base, verboseMode, reques
1435
1435
  ? `${s.autoModeStartModel.provider}/${s.autoModeStartModel.id}`
1436
1436
  : ctx.model ? `${ctx.model.provider}/${ctx.model.id}` : "default";
1437
1437
  // Flat-rate providers (e.g. GitHub Copilot, claude-code, user-declared
1438
- // subscription proxies, externalCli CLIs) suppress routing at dispatch
1439
- // time (#3453) reflect that in the banner. Thread the same
1438
+ // subscription proxies, externalCli CLIs) suppress routing only when the
1439
+ // user explicitly opts out with allow_flat_rate_providers: false. Reflect
1440
+ // that in the banner. Thread the same
1440
1441
  // FlatRateContext used by selectAndApplyModel so user-declared
1441
1442
  // flat-rate providers and externalCli auto-detection are respected.
1442
1443
  const { isFlatRateProvider, buildFlatRateContext } = await import("./auto-model-selection.js");
1443
1444
  const bannerPrefs = loadEffectiveGSDPreferencesWithRegistry(ctx.modelRegistry, base, resolveProfileAnchorProvider(ctx.model?.provider, s.autoModeStartModel?.provider), s.autoModeStartModel ? `${s.autoModeStartModel.provider}/${s.autoModeStartModel.id}` : undefined)?.preferences;
1444
1445
  const effectiveProvider = s.autoModeStartModel?.provider ?? ctx.model?.provider;
1445
1446
  const effectivelyEnabled = routingConfig.enabled
1446
- && (routingConfig.allow_flat_rate_providers
1447
+ && (routingConfig.allow_flat_rate_providers !== false
1447
1448
  || !(effectiveProvider && isFlatRateProvider(effectiveProvider, buildFlatRateContext(effectiveProvider, ctx, bannerPrefs))));
1448
1449
  // The actual ceiling may come from tier_models.heavy, not the start model.
1449
1450
  const effectiveCeiling = (routingConfig.enabled && routingConfig.tier_models?.heavy)
@@ -56,6 +56,8 @@ export async function recoverTimedOutUnit(ctx, pi, unitType, unitId, reason, rct
56
56
  lastProgressAt: Date.now(),
57
57
  progressCount: (runtime?.progressCount ?? 0) + 1,
58
58
  lastProgressKind: reason === "idle" ? "idle-recovery-retry" : "hard-recovery-retry",
59
+ // This steering retry is a fresh chance for the same unit to save a valid result.
60
+ harnessAbort: undefined,
59
61
  });
60
62
  const steeringLines = isEscalation
61
63
  ? [
@@ -151,6 +153,8 @@ export async function recoverTimedOutUnit(ctx, pi, unitType, unitId, reason, rct
151
153
  lastProgressAt: Date.now(),
152
154
  progressCount: (runtime?.progressCount ?? 0) + 1,
153
155
  lastProgressKind: reason === "idle" ? "idle-recovery-retry" : "hard-recovery-retry",
156
+ // This steering retry is a fresh chance for the same unit to save a valid result.
157
+ harnessAbort: undefined,
154
158
  });
155
159
  const steeringLines = isEscalation
156
160
  ? [
@@ -986,10 +986,10 @@ export function teardownAutoWorktree(originalBasePath, milestoneId, opts = {}) {
986
986
  const branch = autoWorktreeBranch(milestoneId);
987
987
  const { preserveBranch = false, preserveWorktree = false } = opts;
988
988
  const previousCwd = safeCwd(originalBasePath);
989
+ let clearActiveWorkspace = true;
989
990
  // Wrap the entire teardown body in a single try/finally so activeWorkspace
990
- // is ALWAYS cleared — even if process.chdir throws (e.g. originalBasePath
991
- // was deleted before teardown ran). Previously the finally only covered
992
- // removeWorktree, leaving the registry stale on a chdir failure (H3 fix).
991
+ // is cleared on completion or irrecoverable failure — even if process.chdir
992
+ // throws (e.g. originalBasePath was deleted before teardown ran).
993
993
  try {
994
994
  try {
995
995
  process.chdir(originalBasePath);
@@ -1026,17 +1026,23 @@ export function teardownAutoWorktree(originalBasePath, milestoneId, opts = {}) {
1026
1026
  // 3. Remove the worktree unless this exit path explicitly preserves it
1027
1027
  // (slice-parallel dispatch stops the parent loop but keeps the parent
1028
1028
  // milestone worktree for restart/re-entry).
1029
+ let worktreeRemoved = preserveWorktree;
1029
1030
  if (!preserveWorktree) {
1030
- removeWorktree(originalBasePath, milestoneId, {
1031
+ worktreeRemoved = removeWorktree(originalBasePath, milestoneId, {
1031
1032
  branch,
1032
1033
  deleteBranch: !preserveBranch,
1033
1034
  });
1035
+ if (!worktreeRemoved) {
1036
+ logWarning("worktree", `Worktree removal aborted for ${milestoneId}; uncommitted work was preserved. ` +
1037
+ `Manual quarantine recovery may be needed before retrying teardown.`, { worktree: milestoneId });
1038
+ clearActiveWorkspace = false;
1039
+ }
1034
1040
  }
1035
1041
  // Verify cleanup succeeded — warn if the worktree directory is still on disk.
1036
1042
  // On Windows, bash-based cleanup can silently fail when paths contain
1037
1043
  // backslashes (#1436), leaving ~1 GB+ orphaned directories.
1038
1044
  const wtDir = worktreePath(originalBasePath, milestoneId);
1039
- if (!preserveWorktree && existsSync(wtDir)) {
1045
+ if (!preserveWorktree && worktreeRemoved && existsSync(wtDir)) {
1040
1046
  logWarning("reconcile", `Worktree directory still exists after teardown: ${wtDir}. ` +
1041
1047
  `This is likely an orphaned directory consuming disk space. ` +
1042
1048
  `Remove it manually with: rm -rf "${wtDir.replaceAll("\\", "/")}"`, { worktree: milestoneId });
@@ -1057,10 +1063,12 @@ export function teardownAutoWorktree(originalBasePath, milestoneId, opts = {}) {
1057
1063
  }
1058
1064
  }
1059
1065
  finally {
1060
- // Clear module state unconditionally regardless of which step above
1061
- // failed. A stale activeWorkspace causes getActiveAutoWorktreeContext()
1062
- // to return wrong data for subsequent operations.
1063
- setActiveWorkspace(null);
1066
+ // Clear module state when teardown completed or failed irrecoverably.
1067
+ // When removeWorktree returns false (quarantine failure), preserve the
1068
+ // registry so recovery logic still sees the active milestone worktree.
1069
+ if (clearActiveWorkspace) {
1070
+ setActiveWorkspace(null);
1071
+ }
1064
1072
  }
1065
1073
  }
1066
1074
  /**
@@ -1491,15 +1499,28 @@ export function mergeMilestoneToMain(originalBasePath_, milestoneId, roadmapCont
1491
1499
  catch (err) {
1492
1500
  logWarning("worktree", `clearProjectRootStateFiles failed during already-merged cleanup: ${err instanceof Error ? err.message : String(err)}`);
1493
1501
  }
1502
+ let worktreeRemoved = true;
1494
1503
  try {
1495
- removeWorktree(originalBasePath_, milestoneId, {
1504
+ worktreeRemoved = removeWorktree(originalBasePath_, milestoneId, {
1496
1505
  branch: milestoneBranch,
1497
1506
  deleteBranch: false,
1498
1507
  });
1499
1508
  }
1500
1509
  catch (err) {
1510
+ worktreeRemoved = false;
1501
1511
  logWarning("worktree", `worktree removal failed: ${err instanceof Error ? err.message : String(err)}`);
1502
1512
  }
1513
+ if (!worktreeRemoved) {
1514
+ logWarning("worktree", `Skipping milestone branch deletion for ${milestoneBranch}; worktree removal was aborted to preserve uncommitted milestone work.`);
1515
+ nudgeGitBranchCache(previousCwd);
1516
+ try {
1517
+ process.chdir(originalBasePath_);
1518
+ }
1519
+ catch (err) {
1520
+ logWarning("worktree", `chdir to project root after already-merged cleanup failed: ${err instanceof Error ? err.message : String(err)}`);
1521
+ }
1522
+ return { commitMessage, pushed: false, prCreated: false, codeFilesChanged: true };
1523
+ }
1503
1524
  try {
1504
1525
  nativeBranchDelete(originalBasePath_, milestoneBranch);
1505
1526
  }
@@ -1932,16 +1953,22 @@ export function mergeMilestoneToMain(originalBasePath_, milestoneId, roadmapCont
1932
1953
  }
1933
1954
  const finalizeMilestoneCleanup = () => {
1934
1955
  // 12. Remove worktree directory first (must happen before branch deletion)
1956
+ let worktreeRemoved = true;
1935
1957
  try {
1936
- removeWorktree(originalBasePath_, milestoneId, {
1958
+ worktreeRemoved = removeWorktree(originalBasePath_, milestoneId, {
1937
1959
  branch: milestoneBranch,
1938
1960
  deleteBranch: false,
1939
1961
  });
1940
1962
  }
1941
1963
  catch (err) {
1964
+ worktreeRemoved = false;
1942
1965
  // Best-effort -- worktree dir may already be gone
1943
1966
  logWarning("worktree", `worktree removal failed: ${err instanceof Error ? err.message : String(err)}`);
1944
1967
  }
1968
+ if (!worktreeRemoved) {
1969
+ logWarning("worktree", `Skipping milestone branch deletion for ${milestoneBranch}; worktree removal was aborted to preserve uncommitted milestone work.`);
1970
+ return;
1971
+ }
1945
1972
  // 13. Delete milestone branch (after worktree removal so ref is unlocked)
1946
1973
  try {
1947
1974
  nativeBranchDelete(originalBasePath_, milestoneBranch);
@@ -68,7 +68,7 @@ import { MILESTONE_ID_RE } from "./milestone-ids.js";
68
68
  import { updateProgressWidget as _updateProgressWidget, setCompletionProgressWidget, setAutoOutcomeWidget, setAutoActiveStatus, updateSliceProgressCache, clearSliceProgressCache, unitVerb, } from "./auto-dashboard.js";
69
69
  import { registerSigtermHandler as _registerSigtermHandler, deregisterSigtermHandler as _deregisterSigtermHandler, } from "./auto-supervisor.js";
70
70
  import { isDbAvailable, getMilestone, getMilestoneSlices, } from "./gsd-db.js";
71
- import { closeWorkflowDatabase } from "./db-workspace.js";
71
+ import { checkpointWorkflowDatabase, closeWorkflowDatabase } from "./db-workspace.js";
72
72
  import { markLatestActiveForWorkerCanceled } from "./db/unit-dispatches.js";
73
73
  import { writeUnitRuntimeRecord } from "./unit-runtime.js";
74
74
  import { countPendingCaptures } from "./captures.js";
@@ -651,6 +651,11 @@ export function stopAutoRemote(projectRoot) {
651
651
  * The normal stop path stays SIGTERM-only so cooperative sessions can clean up;
652
652
  * this path is only for the explicit "Force start" action.
653
653
  */
654
+ // Grace window between SIGTERM and SIGKILL so the target can run its cleanup
655
+ // handler (lock release, DB snapshot flush, worktree teardown). Polled so a
656
+ // process that exits early is not made to wait the full window.
657
+ const FORCE_STOP_GRACE_MS = 3000;
658
+ const FORCE_STOP_POLL_MS = 100;
654
659
  export function forceStopAutoRemote(projectRoot) {
655
660
  const lock = readCrashLock(projectRoot);
656
661
  if (!lock)
@@ -665,6 +670,14 @@ export function forceStopAutoRemote(projectRoot) {
665
670
  }
666
671
  try {
667
672
  process.kill(lock.pid, "SIGTERM");
673
+ // Poll for graceful exit before escalating. Synchronous sleep keeps this
674
+ // function sync for its callers (Atomics.wait times out with no notify).
675
+ const deadline = Date.now() + FORCE_STOP_GRACE_MS;
676
+ while (Date.now() < deadline) {
677
+ if (!isLockProcessAlive(lock))
678
+ break;
679
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, FORCE_STOP_POLL_MS);
680
+ }
668
681
  if (isLockProcessAlive(lock)) {
669
682
  process.kill(lock.pid, "SIGKILL");
670
683
  }
@@ -1089,6 +1102,12 @@ export async function stopAuto(ctx, pi, reason, options = {}) {
1089
1102
  const installTerminalCloseoutOutcome = completionStopRequested && preserveCloseoutTranscript;
1090
1103
  const preserveCompletionSurface = completionStopRequested || preserveCloseoutTranscript;
1091
1104
  s.completionStopInProgress = preserveCompletionSurface;
1105
+ try {
1106
+ checkpointWorkflowDatabase();
1107
+ }
1108
+ catch (e) {
1109
+ debugLog("stop-checkpoint-failed", { error: e instanceof Error ? e.message : String(e) });
1110
+ }
1092
1111
  playNotificationBell("stop", loadedPreferences?.notifications);
1093
1112
  // #4764 — telemetry: record the exit reason, isolation mode, whether an auto
1094
1113
  // worktree was active, and whether the current milestone was merged before
@@ -1719,7 +1738,7 @@ export function buildWorktreeLifecycleDeps() {
1719
1738
  // C4 (#5627) — GitServiceImpl constructor → gitServiceFactory
1720
1739
  //
1721
1740
  // Final WorktreeLifecycleDeps shape: 3 fields (gitServiceFactory,
1722
- // worktreeProjection, mergeMilestoneToMain transaction runner). Down from 18 at slice-7
1741
+ // worktreeProjection, mergeMilestone transaction runner). Down from 18 at slice-7
1723
1742
  // closure.
1724
1743
  return {
1725
1744
  gitServiceFactory: (basePath) => {
@@ -1727,7 +1746,7 @@ export function buildWorktreeLifecycleDeps() {
1727
1746
  return new GitServiceImpl(basePath, gitConfig);
1728
1747
  },
1729
1748
  worktreeProjection: new WorktreeStateProjection(),
1730
- mergeMilestoneToMain: createMilestoneMergeTransaction(mergeMilestoneToMain),
1749
+ mergeMilestone: createMilestoneMergeTransaction(mergeMilestoneToMain),
1731
1750
  };
1732
1751
  }
1733
1752
  function buildLifecycle() {
@@ -1770,6 +1789,7 @@ function buildLoopDeps(pi, ctx) {
1770
1789
  stopAuto,
1771
1790
  pauseAuto,
1772
1791
  clearUnitTimeout,
1792
+ checkpointWorkflowDatabase,
1773
1793
  updateProgressWidget,
1774
1794
  ...cmux,
1775
1795
  handleLostSessionLock: (ctx, lockStatus) => {
@@ -674,12 +674,14 @@ export function registerDbTools(pi) {
674
674
  inputs: Type.Array(Type.String(), { description: "Array<string> of input files or references; pass [\"path\"] or [], never a single string" }),
675
675
  expectedOutput: Type.Array(Type.String(), { description: "Array<string> of files this task creates or overwrites; pass [\"path\"] or [], never prose or a single string" }),
676
676
  observabilityImpact: Type.Optional(Type.String({ description: "Task observability impact" })),
677
+ targetRepositories: Type.Optional(Type.Array(Type.String(), { description: "Repository id(s) this task touches (parent workspace); must match a Declared Repository. Omit for single-repo projects." })),
677
678
  }), { description: "Optional full task replacement for the slice. Omit for incremental planning, then call gsd_plan_task once per task." })),
678
679
  // ── Enrichment metadata (optional — defaults to empty) ────────────
679
680
  successCriteria: Type.Optional(Type.String({ description: "Slice success criteria block" })),
680
681
  proofLevel: Type.Optional(Type.String({ description: "Slice proof level" })),
681
682
  integrationClosure: Type.Optional(Type.String({ description: "Slice integration closure" })),
682
683
  observabilityImpact: Type.Optional(Type.String({ description: "Slice observability impact" })),
684
+ targetRepositories: Type.Optional(Type.Array(Type.String(), { description: "Slice-wide default repository id(s) (parent workspace); must match a Declared Repository. Omit for single-repo projects." })),
683
685
  // Single-writer v3 audit trail (Stream 2): caller-provided actor identity + causation.
684
686
  actorName: Type.Optional(Type.String({ description: "Caller-provided actor identity for the audit trail (e.g. 'executor-01', 'gsd-orchestrator')" })),
685
687
  triggerReason: Type.Optional(Type.String({ description: "Caller-provided reason this action was triggered (e.g. 'plan-phase complete')" })),
@@ -749,6 +751,7 @@ export function registerDbTools(pi) {
749
751
  inputs: Type.Array(Type.String(), { description: "Array<string> of input files or references; pass [\"path\"] or [], never a single string" }),
750
752
  expectedOutput: Type.Array(Type.String(), { description: "Array<string> of files this task creates or overwrites; pass [\"path\"] or [], never prose or a single string" }),
751
753
  observabilityImpact: Type.Optional(Type.String({ description: "Task observability impact" })),
754
+ targetRepositories: Type.Optional(Type.Array(Type.String(), { description: "Repository id(s) this task touches (parent workspace); must match a Declared Repository. Omit for single-repo projects." })),
752
755
  // Single-writer v3 audit trail (Stream 2): caller-provided actor identity + causation.
753
756
  actorName: Type.Optional(Type.String({ description: "Caller-provided actor identity for the audit trail (e.g. 'executor-01', 'gsd-orchestrator')" })),
754
757
  triggerReason: Type.Optional(Type.String({ description: "Caller-provided reason this action was triggered (e.g. 'plan-phase complete')" })),