gsd-pi 2.77.0-dev.1d17f366c → 2.77.0-dev.2daa994b6

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 (368) hide show
  1. package/dist/headless.js +25 -4
  2. package/dist/resource-loader.d.ts +40 -0
  3. package/dist/resource-loader.js +32 -13
  4. package/dist/resources/extensions/browser-tools/capture.js +9 -0
  5. package/dist/resources/extensions/browser-tools/tests/browser-tools-integration.test.mjs +8 -59
  6. package/dist/resources/extensions/browser-tools/tests/browser-tools-unit.test.cjs +36 -24
  7. package/dist/resources/extensions/browser-tools/tests/capture-sharp-optional.test.cjs +69 -71
  8. package/dist/resources/extensions/browser-tools/tools/forms.js +5 -1
  9. package/dist/resources/extensions/browser-tools/tools/intent.js +5 -1
  10. package/dist/resources/extensions/gsd/auto/phases.js +5 -18
  11. package/dist/resources/extensions/gsd/auto/session.js +6 -0
  12. package/dist/resources/extensions/gsd/auto-dispatch.js +37 -8
  13. package/dist/resources/extensions/gsd/auto-post-unit.js +79 -0
  14. package/dist/resources/extensions/gsd/auto-prompts.js +372 -104
  15. package/dist/resources/extensions/gsd/auto-start.js +75 -24
  16. package/dist/resources/extensions/gsd/auto.js +34 -0
  17. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +9 -1
  18. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +7 -1
  19. package/dist/resources/extensions/gsd/component-loader.js +447 -0
  20. package/dist/resources/extensions/gsd/component-types.js +69 -0
  21. package/dist/resources/extensions/gsd/context-store.js +23 -7
  22. package/dist/resources/extensions/gsd/detection.js +49 -1
  23. package/dist/resources/extensions/gsd/docs/preferences-reference.md +1 -1
  24. package/dist/resources/extensions/gsd/forensics.js +106 -0
  25. package/dist/resources/extensions/gsd/gsd-db.js +1 -1
  26. package/dist/resources/extensions/gsd/guided-flow.js +2 -4
  27. package/dist/resources/extensions/gsd/memory-extractor.js +7 -1
  28. package/dist/resources/extensions/gsd/milestone-scope-classifier.js +299 -0
  29. package/dist/resources/extensions/gsd/model-cost-table.js +3 -0
  30. package/dist/resources/extensions/gsd/model-router.js +6 -0
  31. package/dist/resources/extensions/gsd/preferences-validation.js +23 -0
  32. package/dist/resources/extensions/gsd/prompt-cache-optimizer.js +4 -0
  33. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +5 -1
  34. package/dist/resources/extensions/gsd/prompts/plan-slice.md +15 -2
  35. package/dist/resources/extensions/gsd/service-tier.js +5 -2
  36. package/dist/resources/extensions/gsd/skill-manifest.js +168 -0
  37. package/dist/resources/extensions/gsd/slice-cadence.js +238 -0
  38. package/dist/resources/extensions/gsd/tools/validate-milestone.js +7 -2
  39. package/dist/resources/extensions/gsd/unit-context-composer.js +147 -0
  40. package/dist/resources/extensions/gsd/unit-context-manifest.js +334 -0
  41. package/dist/resources/extensions/gsd/worktree-manager.js +51 -0
  42. package/dist/resources/extensions/gsd/worktree-resolver.js +86 -7
  43. package/dist/resources/extensions/gsd/worktree-telemetry.js +198 -0
  44. package/dist/resources/extensions/mcp-client/index.js +3 -1
  45. package/dist/resources/extensions/ollama/index.js +5 -1
  46. package/dist/resources/extensions/remote-questions/manager.js +11 -5
  47. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  48. package/dist/web/standalone/.next/BUILD_ID +1 -1
  49. package/dist/web/standalone/.next/app-path-routes-manifest.json +17 -17
  50. package/dist/web/standalone/.next/build-manifest.json +2 -2
  51. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  52. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  53. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  54. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  55. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  56. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  57. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  58. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  59. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  60. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  61. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  62. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  63. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  64. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  65. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  66. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  67. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  68. package/dist/web/standalone/.next/server/app/index.html +1 -1
  69. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  70. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  71. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  72. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  73. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  74. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  75. package/dist/web/standalone/.next/server/app-paths-manifest.json +17 -17
  76. package/dist/web/standalone/.next/server/chunks/6897.js +1 -1
  77. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  78. package/dist/web/standalone/.next/server/middleware-manifest.json +5 -5
  79. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  80. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  81. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  82. package/package.json +1 -3
  83. package/packages/mcp-server/src/mcp-server.test.ts +25 -3
  84. package/packages/mcp-server/src/readers/graph.test.ts +87 -15
  85. package/packages/mcp-server/src/workflow-tools.test.ts +80 -39
  86. package/packages/native/package.json +1 -1
  87. package/packages/native/src/__tests__/_test-coverage-guard.test.mjs +98 -0
  88. package/packages/native/src/__tests__/module-compat.test.mjs +59 -27
  89. package/packages/native/src/__tests__/ps.test.mjs +14 -8
  90. package/packages/native/src/__tests__/stream-process.test.mjs +23 -2
  91. package/packages/native/src/__tests__/truncate.test.mjs +17 -2
  92. package/packages/pi-agent-core/src/agent-loop.test.ts +5 -15
  93. package/packages/pi-agent-core/src/agent.test.ts +96 -102
  94. package/packages/pi-agent-core/tsconfig.tsbuildinfo +1 -1
  95. package/packages/pi-ai/dist/models/generated/index.d.ts +34 -0
  96. package/packages/pi-ai/dist/models/generated/index.d.ts.map +1 -1
  97. package/packages/pi-ai/dist/models/generated/openai-codex.d.ts +17 -0
  98. package/packages/pi-ai/dist/models/generated/openai-codex.d.ts.map +1 -1
  99. package/packages/pi-ai/dist/models/generated/openai-codex.js +17 -0
  100. package/packages/pi-ai/dist/models/generated/openai-codex.js.map +1 -1
  101. package/packages/pi-ai/dist/models/generated/openai.d.ts +17 -0
  102. package/packages/pi-ai/dist/models/generated/openai.d.ts.map +1 -1
  103. package/packages/pi-ai/dist/models/generated/openai.js +17 -0
  104. package/packages/pi-ai/dist/models/generated/openai.js.map +1 -1
  105. package/packages/pi-ai/dist/models.generated.test.js +43 -70
  106. package/packages/pi-ai/dist/models.generated.test.js.map +1 -1
  107. package/packages/pi-ai/dist/models.test.js +29 -11
  108. package/packages/pi-ai/dist/models.test.js.map +1 -1
  109. package/packages/pi-ai/scripts/generate-models.ts +44 -0
  110. package/packages/pi-ai/src/models/generated/openai-codex.ts +17 -0
  111. package/packages/pi-ai/src/models/generated/openai.ts +17 -0
  112. package/packages/pi-ai/src/models.generated.test.ts +46 -73
  113. package/packages/pi-ai/src/models.test.ts +39 -11
  114. package/packages/pi-ai/tsconfig.tsbuildinfo +1 -1
  115. package/packages/pi-coding-agent/dist/core/agent-session-abort-order.test.js +96 -32
  116. package/packages/pi-coding-agent/dist/core/agent-session-abort-order.test.js.map +1 -1
  117. package/packages/pi-coding-agent/dist/core/agent-session-model-switch.test.js +75 -12
  118. package/packages/pi-coding-agent/dist/core/agent-session-model-switch.test.js.map +1 -1
  119. package/packages/pi-coding-agent/dist/core/agent-session-tool-refresh.test.js +99 -31
  120. package/packages/pi-coding-agent/dist/core/agent-session-tool-refresh.test.js.map +1 -1
  121. package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts +5 -0
  122. package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
  123. package/packages/pi-coding-agent/dist/core/extensions/loader.js +61 -0
  124. package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
  125. package/packages/pi-coding-agent/dist/core/lsp/lsp-integration.test.js +30 -4
  126. package/packages/pi-coding-agent/dist/core/lsp/lsp-integration.test.js.map +1 -1
  127. package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js +17 -0
  128. package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js.map +1 -1
  129. package/packages/pi-coding-agent/dist/core/resource-loader-cache-reset.test.js +76 -18
  130. package/packages/pi-coding-agent/dist/core/resource-loader-cache-reset.test.js.map +1 -1
  131. package/packages/pi-coding-agent/dist/core/retry-handler.d.ts.map +1 -1
  132. package/packages/pi-coding-agent/dist/core/retry-handler.js +2 -6
  133. package/packages/pi-coding-agent/dist/core/retry-handler.js.map +1 -1
  134. package/packages/pi-coding-agent/dist/core/retry-handler.test.js +5 -1
  135. package/packages/pi-coding-agent/dist/core/retry-handler.test.js.map +1 -1
  136. package/packages/pi-coding-agent/dist/core/retryable-error-regex.d.ts +18 -0
  137. package/packages/pi-coding-agent/dist/core/retryable-error-regex.d.ts.map +1 -0
  138. package/packages/pi-coding-agent/dist/core/retryable-error-regex.js +18 -0
  139. package/packages/pi-coding-agent/dist/core/retryable-error-regex.js.map +1 -0
  140. package/packages/pi-coding-agent/dist/core/system-prompt.d.ts +20 -0
  141. package/packages/pi-coding-agent/dist/core/system-prompt.d.ts.map +1 -1
  142. package/packages/pi-coding-agent/dist/core/system-prompt.js +16 -2
  143. package/packages/pi-coding-agent/dist/core/system-prompt.js.map +1 -1
  144. package/packages/pi-coding-agent/dist/index.d.ts +1 -0
  145. package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
  146. package/packages/pi-coding-agent/dist/index.js +1 -0
  147. package/packages/pi-coding-agent/dist/index.js.map +1 -1
  148. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js +36 -5
  149. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js.map +1 -1
  150. package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.test.js +20 -13
  151. package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.test.js.map +1 -1
  152. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  153. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +30 -12
  154. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
  155. package/packages/pi-coding-agent/dist/tests/system-prompt-skill-filter.test.d.ts +2 -0
  156. package/packages/pi-coding-agent/dist/tests/system-prompt-skill-filter.test.d.ts.map +1 -0
  157. package/packages/pi-coding-agent/dist/tests/system-prompt-skill-filter.test.js +130 -0
  158. package/packages/pi-coding-agent/dist/tests/system-prompt-skill-filter.test.js.map +1 -0
  159. package/packages/pi-coding-agent/src/core/agent-session-abort-order.test.ts +113 -37
  160. package/packages/pi-coding-agent/src/core/agent-session-model-switch.test.ts +89 -17
  161. package/packages/pi-coding-agent/src/core/agent-session-tool-refresh.test.ts +112 -43
  162. package/packages/pi-coding-agent/src/core/extensions/loader.ts +58 -0
  163. package/packages/pi-coding-agent/src/core/lsp/lsp-integration.test.ts +35 -4
  164. package/packages/pi-coding-agent/src/core/model-registry-auth-mode.test.ts +20 -0
  165. package/packages/pi-coding-agent/src/core/resource-loader-cache-reset.test.ts +93 -28
  166. package/packages/pi-coding-agent/src/core/retry-handler.test.ts +5 -1
  167. package/packages/pi-coding-agent/src/core/retry-handler.ts +2 -8
  168. package/packages/pi-coding-agent/src/core/retryable-error-regex.ts +18 -0
  169. package/packages/pi-coding-agent/src/core/system-prompt.ts +35 -1
  170. package/packages/pi-coding-agent/src/index.ts +1 -0
  171. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/tool-execution.test.ts +49 -3
  172. package/packages/pi-coding-agent/src/modes/interactive/components/dynamic-border.test.ts +26 -20
  173. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +48 -9
  174. package/packages/pi-coding-agent/src/tests/system-prompt-skill-filter.test.ts +157 -0
  175. package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
  176. package/packages/pi-tui/dist/__tests__/autocomplete.test.js +18 -8
  177. package/packages/pi-tui/dist/__tests__/autocomplete.test.js.map +1 -1
  178. package/packages/pi-tui/dist/__tests__/overlay-layout.test.js +128 -17
  179. package/packages/pi-tui/dist/__tests__/overlay-layout.test.js.map +1 -1
  180. package/packages/pi-tui/dist/__tests__/stdin-buffer.test.js +36 -12
  181. package/packages/pi-tui/dist/__tests__/stdin-buffer.test.js.map +1 -1
  182. package/packages/pi-tui/dist/__tests__/tui.test.js +18 -30
  183. package/packages/pi-tui/dist/__tests__/tui.test.js.map +1 -1
  184. package/packages/pi-tui/dist/components/__tests__/input.test.js +10 -3
  185. package/packages/pi-tui/dist/components/__tests__/input.test.js.map +1 -1
  186. package/packages/pi-tui/dist/components/__tests__/loader.test.js +53 -9
  187. package/packages/pi-tui/dist/components/__tests__/loader.test.js.map +1 -1
  188. package/packages/pi-tui/dist/components/__tests__/markdown-maxlines.test.js +6 -2
  189. package/packages/pi-tui/dist/components/__tests__/markdown-maxlines.test.js.map +1 -1
  190. package/packages/pi-tui/dist/components/image.test.js +6 -5
  191. package/packages/pi-tui/dist/components/image.test.js.map +1 -1
  192. package/packages/pi-tui/src/__tests__/autocomplete.test.ts +24 -8
  193. package/packages/pi-tui/src/__tests__/overlay-layout.test.ts +140 -17
  194. package/packages/pi-tui/src/__tests__/stdin-buffer.test.ts +41 -12
  195. package/packages/pi-tui/src/__tests__/tui.test.ts +18 -37
  196. package/packages/pi-tui/src/components/__tests__/input.test.ts +19 -3
  197. package/packages/pi-tui/src/components/__tests__/loader.test.ts +112 -35
  198. package/packages/pi-tui/src/components/__tests__/markdown-maxlines.test.ts +9 -2
  199. package/packages/pi-tui/src/components/image.test.ts +10 -5
  200. package/packages/pi-tui/tsconfig.tsbuildinfo +1 -1
  201. package/packages/rpc-client/dist/rpc-client.test.js +101 -51
  202. package/packages/rpc-client/dist/rpc-client.test.js.map +1 -1
  203. package/packages/rpc-client/src/rpc-client.test.ts +109 -52
  204. package/packages/rpc-client/tsconfig.tsbuildinfo +1 -1
  205. package/scripts/install.js +15 -1
  206. package/src/resources/extensions/browser-tools/capture.ts +12 -0
  207. package/src/resources/extensions/browser-tools/tests/browser-tools-integration.test.mjs +8 -59
  208. package/src/resources/extensions/browser-tools/tests/browser-tools-unit.test.cjs +36 -24
  209. package/src/resources/extensions/browser-tools/tests/capture-sharp-optional.test.cjs +69 -71
  210. package/src/resources/extensions/browser-tools/tools/forms.ts +5 -1
  211. package/src/resources/extensions/browser-tools/tools/intent.ts +5 -1
  212. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +80 -72
  213. package/src/resources/extensions/github-sync/tests/cli.test.ts +76 -7
  214. package/src/resources/extensions/github-sync/tests/templates.test.ts +33 -1
  215. package/src/resources/extensions/gsd/auto/phases.ts +6 -17
  216. package/src/resources/extensions/gsd/auto/session.ts +7 -0
  217. package/src/resources/extensions/gsd/auto-dispatch.ts +40 -8
  218. package/src/resources/extensions/gsd/auto-post-unit.ts +81 -0
  219. package/src/resources/extensions/gsd/auto-prompts.ts +385 -93
  220. package/src/resources/extensions/gsd/auto-start.ts +97 -4
  221. package/src/resources/extensions/gsd/auto.ts +37 -0
  222. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +9 -1
  223. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +7 -1
  224. package/src/resources/extensions/gsd/component-loader.ts +598 -0
  225. package/src/resources/extensions/gsd/component-types.ts +362 -0
  226. package/src/resources/extensions/gsd/context-store.ts +25 -8
  227. package/src/resources/extensions/gsd/detection.ts +58 -1
  228. package/src/resources/extensions/gsd/docs/preferences-reference.md +1 -1
  229. package/src/resources/extensions/gsd/forensics.ts +118 -1
  230. package/src/resources/extensions/gsd/git-service.ts +16 -0
  231. package/src/resources/extensions/gsd/gsd-db.ts +1 -1
  232. package/src/resources/extensions/gsd/guided-flow.ts +2 -4
  233. package/src/resources/extensions/gsd/journal.ts +11 -1
  234. package/src/resources/extensions/gsd/memory-extractor.ts +11 -3
  235. package/src/resources/extensions/gsd/milestone-scope-classifier.ts +366 -0
  236. package/src/resources/extensions/gsd/model-cost-table.ts +3 -0
  237. package/src/resources/extensions/gsd/model-router.ts +6 -0
  238. package/src/resources/extensions/gsd/preferences-validation.ts +21 -0
  239. package/src/resources/extensions/gsd/prompt-cache-optimizer.ts +4 -0
  240. package/src/resources/extensions/gsd/prompts/complete-milestone.md +5 -1
  241. package/src/resources/extensions/gsd/prompts/plan-slice.md +15 -2
  242. package/src/resources/extensions/gsd/service-tier.ts +5 -2
  243. package/src/resources/extensions/gsd/skill-manifest.ts +175 -0
  244. package/src/resources/extensions/gsd/slice-cadence.ts +299 -0
  245. package/src/resources/extensions/gsd/tests/artifacts-table-preserved-on-cache-invalidate.test.ts +2 -1
  246. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +25 -292
  247. package/src/resources/extensions/gsd/tests/auto-remediate-slice-status.test.ts +4 -1
  248. package/src/resources/extensions/gsd/tests/auto-retry-mcp-churn-fixes.test.ts +8 -194
  249. package/src/resources/extensions/gsd/tests/auto-start-clean-runtime-db-gated.test.ts +3 -2
  250. package/src/resources/extensions/gsd/tests/auto-start-cold-db-bootstrap.test.ts +2 -2
  251. package/src/resources/extensions/gsd/tests/auto-start-needs-discussion.test.ts +15 -58
  252. package/src/resources/extensions/gsd/tests/auto-start-worktree-db-path.test.ts +2 -2
  253. package/src/resources/extensions/gsd/tests/auto-thinking-restore.test.ts +3 -2
  254. package/src/resources/extensions/gsd/tests/auto-warning-noise-regression.test.ts +3 -2
  255. package/src/resources/extensions/gsd/tests/bootstrap-derive-state-db-open.test.ts +2 -1
  256. package/src/resources/extensions/gsd/tests/cache-staleness-regression.test.ts +17 -21
  257. package/src/resources/extensions/gsd/tests/canonical-milestone-root.test.ts +108 -0
  258. package/src/resources/extensions/gsd/tests/complete-milestone-excerpt.test.ts +263 -0
  259. package/src/resources/extensions/gsd/tests/complete-slice-composer.test.ts +192 -0
  260. package/src/resources/extensions/gsd/tests/complete-slice-verification-gate.test.ts +2 -1
  261. package/src/resources/extensions/gsd/tests/complete-task.test.ts +8 -4
  262. package/src/resources/extensions/gsd/tests/component-loader.test.ts +589 -0
  263. package/src/resources/extensions/gsd/tests/component-types.test.ts +127 -0
  264. package/src/resources/extensions/gsd/tests/context-store.test.ts +79 -0
  265. package/src/resources/extensions/gsd/tests/copy-planning-artifacts-samepath.test.ts +2 -1
  266. package/src/resources/extensions/gsd/tests/discuss-slice-structured-questions.test.ts +2 -1
  267. package/src/resources/extensions/gsd/tests/discuss-tool-scope-leak.test.ts +2 -1
  268. package/src/resources/extensions/gsd/tests/double-merge-guard.test.ts +4 -3
  269. package/src/resources/extensions/gsd/tests/empty-content-abort-loop.test.ts +4 -3
  270. package/src/resources/extensions/gsd/tests/extension-bootstrap-isolation.test.ts +139 -129
  271. package/src/resources/extensions/gsd/tests/finalize-timeout-guard.test.ts +8 -104
  272. package/src/resources/extensions/gsd/tests/hook-key-parsing.test.ts +4 -55
  273. package/src/resources/extensions/gsd/tests/integration/all-milestones-complete-merge.test.ts +7 -56
  274. package/src/resources/extensions/gsd/tests/integration/doctor-proactive.test.ts +18 -2
  275. package/src/resources/extensions/gsd/tests/integration/queue-completed-milestone-perf.test.ts +10 -4
  276. package/src/resources/extensions/gsd/tests/integration/state-machine-edge-cases.test.ts +1 -1
  277. package/src/resources/extensions/gsd/tests/interactive-routing-bypass.test.ts +9 -3
  278. package/src/resources/extensions/gsd/tests/interrupted-session-ui.test.ts +6 -9
  279. package/src/resources/extensions/gsd/tests/knowledge.test.ts +93 -1
  280. package/src/resources/extensions/gsd/tests/mcp-client-security.test.ts +8 -37
  281. package/src/resources/extensions/gsd/tests/memory-extractor.test.ts +5 -15
  282. package/src/resources/extensions/gsd/tests/merge-conflict-stops-loop.test.ts +227 -55
  283. package/src/resources/extensions/gsd/tests/milestone-scope-classifier.test.ts +187 -0
  284. package/src/resources/extensions/gsd/tests/model-cost-table.test.ts +9 -1
  285. package/src/resources/extensions/gsd/tests/model-router.test.ts +1 -1
  286. package/src/resources/extensions/gsd/tests/native-git-bridge-exec-fallback.test.ts +6 -48
  287. package/src/resources/extensions/gsd/tests/notification-widget.test.ts +6 -3
  288. package/src/resources/extensions/gsd/tests/orphaned-worktree-audit.test.ts +59 -2
  289. package/src/resources/extensions/gsd/tests/parallel-research-dispatch.test.ts +273 -130
  290. package/src/resources/extensions/gsd/tests/pipeline-variant-dispatch.test.ts +301 -0
  291. package/src/resources/extensions/gsd/tests/preferences-worktree-sync.test.ts +2 -1
  292. package/src/resources/extensions/gsd/tests/prompt-cache-optimizer.test.ts +12 -0
  293. package/src/resources/extensions/gsd/tests/prompt-step-ordering.test.ts +15 -4
  294. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +22 -16
  295. package/src/resources/extensions/gsd/tests/queue-draft-detection.test.ts +3 -2
  296. package/src/resources/extensions/gsd/tests/queued-discuss-fast-path.test.ts +4 -5
  297. package/src/resources/extensions/gsd/tests/reassess-default-optin.test.ts +132 -0
  298. package/src/resources/extensions/gsd/tests/recovery-attempts-reset.test.ts +8 -40
  299. package/src/resources/extensions/gsd/tests/regex-hardening.test.ts +136 -256
  300. package/src/resources/extensions/gsd/tests/research-milestone-composer.test.ts +114 -0
  301. package/src/resources/extensions/gsd/tests/restore-tools-after-discuss.test.ts +6 -3
  302. package/src/resources/extensions/gsd/tests/run-uat-composer.test.ts +148 -0
  303. package/src/resources/extensions/gsd/tests/service-tier.test.ts +4 -0
  304. package/src/resources/extensions/gsd/tests/sidecar-queue.test.ts +3 -2
  305. package/src/resources/extensions/gsd/tests/silent-catch-diagnostics.test.ts +55 -95
  306. package/src/resources/extensions/gsd/tests/skill-activation.test.ts +120 -1
  307. package/src/resources/extensions/gsd/tests/skill-manifest.test.ts +112 -0
  308. package/src/resources/extensions/gsd/tests/slice-cadence.test.ts +242 -0
  309. package/src/resources/extensions/gsd/tests/slice-context-injection.test.ts +3 -2
  310. package/src/resources/extensions/gsd/tests/smart-entry-draft.test.ts +2 -1
  311. package/src/resources/extensions/gsd/tests/stop-auto-race-null-unit.test.ts +3 -3
  312. package/src/resources/extensions/gsd/tests/structured-data-formatter.test.ts +11 -92
  313. package/src/resources/extensions/gsd/tests/subagent-model-dispatch.test.ts +7 -6
  314. package/src/resources/extensions/gsd/tests/survivor-branch-complete.test.ts +102 -101
  315. package/src/resources/extensions/gsd/tests/sync-worktree-skip-current.test.ts +4 -3
  316. package/src/resources/extensions/gsd/tests/test-helpers.test.ts +98 -0
  317. package/src/resources/extensions/gsd/tests/test-helpers.ts +153 -0
  318. package/src/resources/extensions/gsd/tests/token-profile.test.ts +8 -1
  319. package/src/resources/extensions/gsd/tests/unit-context-composer.test.ts +355 -0
  320. package/src/resources/extensions/gsd/tests/unit-context-manifest.test.ts +203 -0
  321. package/src/resources/extensions/gsd/tests/uok-gitops-wiring.test.ts +49 -26
  322. package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +1 -0
  323. package/src/resources/extensions/gsd/tests/verify-artifact-tightened.test.ts +144 -80
  324. package/src/resources/extensions/gsd/tests/visualizer-critical-path.test.ts +20 -54
  325. package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +342 -277
  326. package/src/resources/extensions/gsd/tests/worker-model-override.test.ts +37 -29
  327. package/src/resources/extensions/gsd/tests/worktree-db.test.ts +226 -266
  328. package/src/resources/extensions/gsd/tests/worktree-health-monorepo.test.ts +103 -67
  329. package/src/resources/extensions/gsd/tests/worktree-nested-git-safety.test.ts +92 -90
  330. package/src/resources/extensions/gsd/tests/worktree-submodule-safety.test.ts +238 -59
  331. package/src/resources/extensions/gsd/tests/worktree-sync-overwrite-loop.test.ts +113 -161
  332. package/src/resources/extensions/gsd/tests/worktree-telemetry.test.ts +210 -0
  333. package/src/resources/extensions/gsd/tests/zombie-gsd-state.test.ts +80 -96
  334. package/src/resources/extensions/gsd/tools/validate-milestone.ts +8 -2
  335. package/src/resources/extensions/gsd/unit-context-composer.ts +218 -0
  336. package/src/resources/extensions/gsd/unit-context-manifest.ts +492 -0
  337. package/src/resources/extensions/gsd/worktree-manager.ts +53 -0
  338. package/src/resources/extensions/gsd/worktree-resolver.ts +96 -9
  339. package/src/resources/extensions/gsd/worktree-telemetry.ts +322 -0
  340. package/src/resources/extensions/mcp-client/index.ts +3 -1
  341. package/src/resources/extensions/mcp-client/tests/server-name-spaces.test.ts +70 -36
  342. package/src/resources/extensions/ollama/index.ts +5 -1
  343. package/src/resources/extensions/ollama/ollama-auth-mode.test.ts +123 -15
  344. package/src/resources/extensions/ollama/ollama-status-indicator.test.ts +206 -19
  345. package/src/resources/extensions/remote-questions/manager.ts +36 -4
  346. package/src/resources/extensions/remote-questions/tests/command-polling.test.ts +200 -190
  347. package/src/resources/extensions/shared/tests/interview-preview.test.ts +11 -3
  348. package/src/resources/extensions/voice/tests/linux-ready.test.ts +129 -113
  349. package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.d.ts +0 -2
  350. package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.d.ts.map +0 -1
  351. package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.js +0 -289
  352. package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.js.map +0 -1
  353. package/packages/pi-ai/src/utils/oauth/oauth-providers.test.ts +0 -363
  354. package/src/resources/extensions/gsd/tests/auto-start-model-capture.test.ts +0 -143
  355. package/src/resources/extensions/gsd/tests/complete-milestone-false-merge.test.ts +0 -157
  356. package/src/resources/extensions/gsd/tests/dashboard-model-label-ordering.test.ts +0 -107
  357. package/src/resources/extensions/gsd/tests/find-missing-summaries-closed.test.ts +0 -48
  358. package/src/resources/extensions/gsd/tests/forensics-context-persist.test.ts +0 -159
  359. package/src/resources/extensions/gsd/tests/forensics-db-completion.test.ts +0 -96
  360. package/src/resources/extensions/gsd/tests/forensics-dedup.test.ts +0 -79
  361. package/src/resources/extensions/gsd/tests/forensics-hook-key-parse.test.ts +0 -74
  362. package/src/resources/extensions/gsd/tests/forensics-journal.test.ts +0 -162
  363. package/src/resources/extensions/gsd/tests/gitignore-bg-shell.test.ts +0 -38
  364. package/src/resources/extensions/gsd/tests/gsd-no-project-error.test.ts +0 -73
  365. package/src/resources/extensions/gsd/tests/idle-watchdog-stall-override.test.ts +0 -125
  366. package/src/resources/extensions/gsd/tests/import-done-milestones.test.ts +0 -42
  367. /package/dist/web/standalone/.next/static/{vidAVJkURvTJ0_V2-64ro → gYYky7yfxW8txb9vU2TrJ}/_buildManifest.js +0 -0
  368. /package/dist/web/standalone/.next/static/{vidAVJkURvTJ0_V2-64ro → gYYky7yfxW8txb9vU2TrJ}/_ssgManifest.js +0 -0
@@ -1,20 +1,89 @@
1
- import test, { describe, it, beforeEach } from "node:test";
1
+ import { describe, it, beforeEach, afterEach } from "node:test";
2
2
  import assert from "node:assert/strict";
3
3
  import { ghIsAvailable, _resetGhCache } from "../cli.ts";
4
4
 
5
- describe("cli", () => {
5
+ describe("github-sync/cli.ghIsAvailable", () => {
6
+ let originalPath: string | undefined;
7
+
6
8
  beforeEach(() => {
7
9
  _resetGhCache();
10
+ originalPath = process.env.PATH;
11
+ });
12
+
13
+ afterEach(() => {
14
+ if (originalPath !== undefined) {
15
+ process.env.PATH = originalPath;
16
+ } else {
17
+ delete process.env.PATH;
18
+ }
19
+ _resetGhCache();
8
20
  });
9
21
 
10
- it("ghIsAvailable returns boolean", () => {
11
- const result = ghIsAvailable();
12
- assert.equal(typeof result, "boolean");
22
+ it("returns true when gh is on PATH, false otherwise", () => {
23
+ // Force gh to be unavailable by setting PATH to an empty-ish string
24
+ // that contains no gh. This is more robust than asserting a raw
25
+ // `typeof === 'boolean'` (which the previous test did — a tautology,
26
+ // since the function's TypeScript signature already guarantees it).
27
+ process.env.PATH = "/nonexistent-path-for-test";
28
+ assert.equal(
29
+ ghIsAvailable(),
30
+ false,
31
+ "with gh not on PATH, ghIsAvailable must return false",
32
+ );
13
33
  });
14
34
 
15
- it("ghIsAvailable caches result", () => {
35
+ it("caches the availability result — PATH changes after first call are ignored", () => {
36
+ // With the original PATH, gh may or may not be present (depends on
37
+ // the dev machine / CI runner). Either way, capture the first
38
+ // result, then mutate PATH so a fresh subprocess spawn would yield
39
+ // a different result. If the function is genuinely caching, the
40
+ // second call returns the same value despite the PATH change.
41
+
42
+ // Prime the cache with whatever the current PATH says.
16
43
  const first = ghIsAvailable();
44
+
45
+ // Change PATH so the `gh` binary is no longer findable — any
46
+ // subsequent subprocess spawn would yield false.
47
+ process.env.PATH = "/nonexistent-path-for-test";
48
+
17
49
  const second = ghIsAvailable();
18
- assert.equal(first, second);
50
+
51
+ assert.equal(
52
+ second,
53
+ first,
54
+ "cached result must not change when PATH changes after the first call. " +
55
+ "Without caching, mutating PATH away from gh would flip the result.",
56
+ );
57
+ });
58
+
59
+ it("re-evaluates after _resetGhCache — cache is the thing being tested", () => {
60
+ // This locks in that `_resetGhCache` actually clears the cache:
61
+ // with it absent, the second assertion wouldn't observe the PATH change.
62
+ process.env.PATH = "/nonexistent-path-for-test";
63
+ const beforeReset = ghIsAvailable(); // false — gh not on PATH
64
+ assert.equal(beforeReset, false);
65
+
66
+ _resetGhCache();
67
+ // Restore PATH so that if a real gh is available, the cached
68
+ // "false" from before-reset must not persist.
69
+ if (originalPath !== undefined) {
70
+ process.env.PATH = originalPath;
71
+ }
72
+
73
+ // After reset, another call re-probes. We don't know whether the
74
+ // real machine has gh, but we know the re-probe happened because
75
+ // before-reset with an empty PATH was false AND if the machine
76
+ // has gh, the post-reset result would be true (i.e. different).
77
+ const afterReset = ghIsAvailable();
78
+ // Invariant: either the dev machine has gh (afterReset=true, differs
79
+ // from beforeReset) or it doesn't (afterReset=false, unchanged).
80
+ // Both are fine — what matters is that _resetGhCache cleared the
81
+ // memoized value so the re-probe ran. That's observable by the
82
+ // fact that with gh present, afterReset=true even though the
83
+ // cached pre-reset value was false.
84
+ assert.ok(
85
+ typeof afterReset === "boolean",
86
+ "re-probe must return a boolean",
87
+ );
19
88
  });
20
89
  });
@@ -105,9 +105,41 @@ describe("templates", () => {
105
105
  assert.ok(comment.includes("duration:"));
106
106
  });
107
107
 
108
- it("handles empty data gracefully", () => {
108
+ it("handles empty data gracefully — no debug-artifact output", () => {
109
+ // Previous version only asserted `typeof === 'string'`, which the
110
+ // function signature already guarantees (tautology).
111
+ //
112
+ // The real invariant is: empty input must produce a string that
113
+ // is safe to post (or skip) without leaking a debug-stringified
114
+ // object. An empty string IS allowed here — callers are expected
115
+ // to gate on truthiness before posting ("skip if empty"). What
116
+ // must NOT happen is leaking 'undefined', '[object Object]',
117
+ // 'null', or a template-placeholder tell like '{{' / '}}'.
109
118
  const comment = formatSummaryComment({});
110
119
  assert.equal(typeof comment, "string");
120
+ assert.doesNotMatch(
121
+ comment,
122
+ /^undefined$|^\[object Object\]$|^null$/,
123
+ "empty-data comment must not be a debug-style stringified artifact",
124
+ );
125
+ assert.doesNotMatch(
126
+ comment,
127
+ /\{\{\s*\w+\s*\}\}/,
128
+ "empty-data comment must not leak unsubstituted {{placeholders}}",
129
+ );
130
+ });
131
+
132
+ it("empty input produces empty comment — callers gate on truthiness", () => {
133
+ // Sister to the previous test: this locks in the current behaviour
134
+ // that empty input returns empty string, so a regression that
135
+ // unexpectedly starts emitting a non-empty default (which would
136
+ // then post spam comments for every bare-data milestone) fails.
137
+ const comment = formatSummaryComment({});
138
+ assert.equal(
139
+ comment,
140
+ "",
141
+ "empty data must return exactly '' so callers can `if (comment)` gate",
142
+ );
111
143
  });
112
144
  });
113
145
 
@@ -26,12 +26,12 @@ import {
26
26
  import { detectStuck } from "./detect-stuck.js";
27
27
  import { runUnit } from "./run-unit.js";
28
28
  import { debugLog } from "../debug-logger.js";
29
- import { PROJECT_FILES } from "../detection.js";
29
+ import { PROJECT_FILES, hasProjectFileInAncestor } from "../detection.js";
30
30
  import { MergeConflictError } from "../git-service.js";
31
31
  import { setCurrentPhase, clearCurrentPhase } from "../../shared/gsd-phase-state.js";
32
32
  import { pauseAutoForProviderError } from "../provider-error-pause.js";
33
33
  import { resumeAutoAfterProviderDelay } from "../bootstrap/provider-error-resume.js";
34
- import { join, basename, dirname, parse as parsePath } from "node:path";
34
+ import { join, basename } from "node:path";
35
35
  import { existsSync, cpSync, readdirSync } from "node:fs";
36
36
  import {
37
37
  logWarning,
@@ -1378,21 +1378,10 @@ export async function runUnitPhase(
1378
1378
  // Monorepo support (#2347): if no project files in the worktree directory,
1379
1379
  // walk parent directories up to the filesystem root. In monorepos,
1380
1380
  // package.json / Cargo.toml etc. live in a parent directory.
1381
- let hasProjectFileInParent = false;
1382
- if (!hasProjectFile && !hasSrcDir && !hasXcodeBundle) {
1383
- let checkDir = dirname(s.basePath);
1384
- const { root } = parsePath(checkDir);
1385
- while (checkDir !== root) {
1386
- // Stop at git repository boundary — ancestors above the repo root
1387
- // (e.g. ~ or /usr/local) may contain unrelated project files.
1388
- if (deps.existsSync(join(checkDir, ".git"))) break;
1389
- if (PROJECT_FILES.some((f) => deps.existsSync(join(checkDir, f)))) {
1390
- hasProjectFileInParent = true;
1391
- break;
1392
- }
1393
- checkDir = dirname(checkDir);
1394
- }
1395
- }
1381
+ const hasProjectFileInParent =
1382
+ !hasProjectFile && !hasSrcDir && !hasXcodeBundle
1383
+ ? hasProjectFileInAncestor(s.basePath, deps.existsSync)
1384
+ : false;
1396
1385
  if (!hasProjectFile && !hasSrcDir && !hasXcodeBundle && !hasProjectFileInParent) {
1397
1386
  // Greenfield projects won't have project files yet — the first task creates them.
1398
1387
  // Log a warning but allow execution to proceed. The .git check above is sufficient
@@ -178,6 +178,12 @@ export class AutoSession {
178
178
  * stopAuto does not attempt the same merge a second time (#2645). */
179
179
  milestoneMergedInPhases = false;
180
180
 
181
+ // #4765 — slice-cadence collapse: main-branch SHAs at the moment each
182
+ // milestone's first slice merge began. Used by resquashMilestoneOnMain at
183
+ // milestone completion to collapse N slice commits into one. Cleared when
184
+ // the milestone finishes (or resquash runs).
185
+ milestoneStartShas: Map<string, string> = new Map();
186
+
181
187
  // ── Dispatch circuit breakers ──────────────────────────────────────
182
188
  rewriteAttemptCount = 0;
183
189
  /** Tracks consecutive bootstrap attempts that found phase === "complete".
@@ -299,6 +305,7 @@ export class AutoSession {
299
305
  this.lastGitActionStatus = null;
300
306
  this.isolationDegraded = false;
301
307
  this.milestoneMergedInPhases = false;
308
+ this.milestoneStartShas = new Map();
302
309
  this.checkpointSha = null;
303
310
 
304
311
  // Signal handler
@@ -59,6 +59,7 @@ import {
59
59
  import { resolveModelWithFallbacksForUnit } from "./preferences-models.js";
60
60
  import { resolveUokFlags } from "./uok/flags.js";
61
61
  import { selectReactiveDispatchBatch } from "./uok/execution-graph.js";
62
+ import { getMilestonePipelineVariant } from "./milestone-scope-classifier.js";
62
63
  import { EXECUTION_ENTRY_PHASES, hasFinalizedMilestoneContext } from "./uok/plan-v2.js";
63
64
 
64
65
  // ─── Types ────────────────────────────────────────────────────────────────
@@ -90,6 +91,18 @@ export interface DispatchContext {
90
91
  modelRegistry?: MinimalModelRegistry;
91
92
  }
92
93
 
94
+ type ReassessmentChecker = typeof checkNeedsReassessment;
95
+
96
+ let reassessmentChecker: ReassessmentChecker = checkNeedsReassessment;
97
+
98
+ export function setReassessmentCheckerForTest(checker: ReassessmentChecker): () => void {
99
+ const previous = reassessmentChecker;
100
+ reassessmentChecker = checker;
101
+ return () => {
102
+ reassessmentChecker = previous;
103
+ };
104
+ }
105
+
93
106
  export interface DispatchRule {
94
107
  /** Human-readable name for debugging and test identification */
95
108
  name: string;
@@ -371,11 +384,15 @@ export const DISPATCH_RULES: DispatchRule[] = [
371
384
  name: "reassess-roadmap (post-completion)",
372
385
  match: async ({ state, mid, midTitle, basePath, prefs }) => {
373
386
  if (prefs?.phases?.skip_reassess) return null;
374
- // Default reassess_after_slice to true reassessment after slice completion
375
- // is essential for roadmap integrity. Opt-out via explicit `false`.
376
- const reassessEnabled = prefs?.phases?.reassess_after_slice ?? true;
387
+ // Default reassess_after_slice to false per ADR-003 §4 most reassess
388
+ // units conclude "roadmap is fine" and burn a session for no change.
389
+ // The plan-slice prompt now carries a reassessment preamble so the
390
+ // next slice's planner does JIT roadmap verification at zero extra
391
+ // cost. Opt-in via explicit `reassess_after_slice: true` (e.g.
392
+ // burn-max profile) when you want the dedicated reassess session.
393
+ const reassessEnabled = prefs?.phases?.reassess_after_slice ?? false;
377
394
  if (!reassessEnabled) return null;
378
- const needsReassess = await checkNeedsReassessment(basePath, mid, state);
395
+ const needsReassess = await reassessmentChecker(basePath, mid, state);
379
396
  if (!needsReassess) return null;
380
397
  return {
381
398
  action: "dispatch",
@@ -482,6 +499,11 @@ export const DISPATCH_RULES: DispatchRule[] = [
482
499
  match: async ({ state, mid, midTitle, basePath, prefs }) => {
483
500
  if (state.phase !== "planning") return null;
484
501
  if (prefs?.phases?.skip_research || prefs?.phases?.skip_slice_research) return null;
502
+ // #4781 phase 2: trivial-scope milestones skip dedicated slice research.
503
+ // plan-slice absorbs the lightweight discovery a trivial deliverable
504
+ // needs. Null result (DB unavailable / unknown) falls through to today's
505
+ // behavior.
506
+ if (await getMilestonePipelineVariant(mid) === "trivial") return null;
485
507
 
486
508
  // Load roadmap to find all slices
487
509
  const roadmapFile = resolveMilestoneFile(basePath, mid, "ROADMAP");
@@ -538,6 +560,8 @@ export const DISPATCH_RULES: DispatchRule[] = [
538
560
  // Phase skip: skip research when preference or profile says so
539
561
  if (prefs?.phases?.skip_research || prefs?.phases?.skip_slice_research)
540
562
  return null;
563
+ // #4781 phase 2: trivial-scope milestones skip dedicated slice research.
564
+ if (await getMilestonePipelineVariant(mid) === "trivial") return null;
541
565
  if (!state.activeSlice) return missingSliceStop(mid, state.phase);
542
566
  const sid = state.activeSlice!.id;
543
567
  const sTitle = state.activeSlice!.title;
@@ -890,8 +914,13 @@ export const DISPATCH_RULES: DispatchRule[] = [
890
914
  };
891
915
  }
892
916
 
893
- // Skip preference: write a minimal pass-through VALIDATION file
894
- if (prefs?.phases?.skip_milestone_validation) {
917
+ // #4781 phase 2: trivial-scope milestones skip the dedicated validate
918
+ // unit — complete-milestone's own verification steps (3/4/5 in the
919
+ // closer prompt) are sufficient proof for contained deliverables.
920
+ const trivialVariant = await getMilestonePipelineVariant(mid) === "trivial";
921
+
922
+ // Skip preference OR trivial scope: write a minimal pass-through VALIDATION file.
923
+ if (prefs?.phases?.skip_milestone_validation || trivialVariant) {
895
924
  const mDir = resolveMilestonePath(basePath, mid);
896
925
  if (mDir) {
897
926
  if (!existsSync(mDir)) mkdirSync(mDir, { recursive: true });
@@ -899,15 +928,18 @@ export const DISPATCH_RULES: DispatchRule[] = [
899
928
  mDir,
900
929
  buildMilestoneFileName(mid, "VALIDATION"),
901
930
  );
931
+ const skipSource = trivialVariant
932
+ ? "trivial-scope pipeline variant (#4781)"
933
+ : "`skip_milestone_validation` preference";
902
934
  const content = [
903
935
  "---",
904
936
  "verdict: pass",
905
937
  "remediation_round: 0",
906
938
  "---",
907
939
  "",
908
- "# Milestone Validation (skipped by preference)",
940
+ "# Milestone Validation (skipped)",
909
941
  "",
910
- "Milestone validation was skipped via `skip_milestone_validation` preference.",
942
+ `Milestone validation was skipped via ${skipSource}.`,
911
943
  ].join("\n");
912
944
  writeFileSync(validationPath, content, "utf-8");
913
945
  }
@@ -618,6 +618,87 @@ export async function postUnitPreVerification(pctx: PostUnitContext, opts?: PreV
618
618
  clearReactiveState(s.basePath, mid, sid);
619
619
  }
620
620
  });
621
+
622
+ // #4765 — slice-cadence collapse. When `git.collapse_cadence: "slice"`
623
+ // is set, squash-merge the slice's commits from the milestone branch
624
+ // onto main right here, so orphan risk shrinks from milestone-size to
625
+ // slice-size. Only runs in worktree isolation mode — the feature needs
626
+ // a milestone branch to squash from.
627
+ let sliceMergeStopped = false;
628
+ await runSafely("postUnit", "slice-cadence-merge", async () => {
629
+ const prefsResult = loadEffectiveGSDPreferences(s.basePath);
630
+ const prefs = prefsResult?.preferences;
631
+ const { getCollapseCadence, mergeSliceToMain } = await import("./slice-cadence.js");
632
+ if (getCollapseCadence(prefs) !== "slice") return;
633
+ if (prefs?.git?.isolation !== "worktree") return;
634
+ if (s.isolationDegraded) return;
635
+
636
+ const projectRoot = s.originalBasePath || s.basePath;
637
+ const { milestone: mid, slice: sid } = parseUnitId(unit.id);
638
+ if (!mid || !sid) return;
639
+
640
+ // Record the milestone start SHA before the first slice merge, so
641
+ // resquashMilestoneOnMain has a target at milestone completion.
642
+ // Resolve main branch dynamically — hard-coding "main" breaks repos
643
+ // that use "master" or a custom default branch.
644
+ if (!s.milestoneStartShas.has(mid)) {
645
+ try {
646
+ const { nativeDetectMainBranch } = await import("./native-git-bridge.js");
647
+ const mainBranch = nativeDetectMainBranch(projectRoot);
648
+ const { execFileSync } = await import("node:child_process");
649
+ const sha = execFileSync("git", ["rev-parse", mainBranch], {
650
+ cwd: projectRoot, stdio: ["ignore", "pipe", "pipe"], encoding: "utf-8",
651
+ }).trim();
652
+ if (sha) s.milestoneStartShas.set(mid, sha);
653
+ } catch (err) {
654
+ logWarning("engine", `slice-cadence: failed to record milestone start SHA: ${err instanceof Error ? err.message : String(err)}`);
655
+ }
656
+ }
657
+
658
+ try {
659
+ const result = mergeSliceToMain(projectRoot, mid, sid);
660
+ if (result.skipped) {
661
+ logWarning("engine", `slice-cadence: merge skipped for ${sid} — ${result.skippedReason}`);
662
+ return;
663
+ }
664
+ ctx.ui.notify(
665
+ `slice-cadence: ${sid} merged to main (${result.durationMs}ms).`,
666
+ "info",
667
+ );
668
+ } catch (err) {
669
+ const { MergeConflictError } = await import("./git-service.js");
670
+ if (err instanceof MergeConflictError) {
671
+ ctx.ui.notify(
672
+ `slice-cadence merge conflict in ${sid}: ${err.conflictedFiles.join(", ")}. ` +
673
+ `Resolve manually on main and run \`/gsd auto\` to resume.`,
674
+ "error",
675
+ );
676
+ // Stop auto AND signal the outer postUnit flow to exit early.
677
+ // Without the flag, subsequent hooks (triage, rogue detection,
678
+ // DB writes) would keep running against a conflicted main
679
+ // checkout after the loop was already told to stop.
680
+ const { stopAuto } = await import("./auto.js");
681
+ await stopAuto(ctx, undefined, `slice-merge-conflict on ${sid}`);
682
+ sliceMergeStopped = true;
683
+ return;
684
+ }
685
+ logError("engine", `slice-cadence merge failed for ${sid}`, {
686
+ error: err instanceof Error ? err.message : String(err),
687
+ });
688
+ // Non-conflict failures (dirty main, rev-walk error, etc.) can
689
+ // leave the checkout in an unexpected state. Stop auto-mode so
690
+ // the next slice doesn't dispatch on top of it.
691
+ const { stopAuto } = await import("./auto.js");
692
+ await stopAuto(ctx, undefined, `slice-merge-error on ${sid}`);
693
+ sliceMergeStopped = true;
694
+ }
695
+ });
696
+ // Exit early after stopAuto so the rest of post-unit processing
697
+ // (triage, rogue detection, hook dispatch, DB writes) doesn't run
698
+ // against a conflicted main checkout. Return "dispatched" to match
699
+ // the convention used by other stop/pauseAuto paths in this function
700
+ // (see signal handling earlier: stop/pause also return "dispatched").
701
+ if (sliceMergeStopped) return "dispatched";
621
702
  }
622
703
 
623
704
  // Post-triage: execute actionable resolutions