gsd-pi 2.77.0-dev.58d3d4d6c → 2.77.0-dev.cfd69e714

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 (429) hide show
  1. package/README.md +1 -1
  2. package/dist/claude-cli-check.js +5 -1
  3. package/dist/headless.js +49 -4
  4. package/dist/resource-loader.d.ts +40 -0
  5. package/dist/resource-loader.js +32 -13
  6. package/dist/resources/extensions/browser-tools/capture.js +9 -0
  7. package/dist/resources/extensions/browser-tools/tests/browser-tools-integration.test.mjs +8 -59
  8. package/dist/resources/extensions/browser-tools/tests/browser-tools-unit.test.cjs +36 -24
  9. package/dist/resources/extensions/browser-tools/tests/capture-sharp-optional.test.cjs +69 -71
  10. package/dist/resources/extensions/browser-tools/tools/forms.js +5 -1
  11. package/dist/resources/extensions/browser-tools/tools/intent.js +5 -1
  12. package/dist/resources/extensions/claude-code-cli/readiness.js +5 -1
  13. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +481 -17
  14. package/dist/resources/extensions/gsd/auto/loop.js +43 -0
  15. package/dist/resources/extensions/gsd/auto/phases.js +15 -21
  16. package/dist/resources/extensions/gsd/auto/session.js +0 -2
  17. package/dist/resources/extensions/gsd/auto-dispatch.js +102 -24
  18. package/dist/resources/extensions/gsd/auto-model-selection.js +124 -4
  19. package/dist/resources/extensions/gsd/auto-post-unit.js +71 -64
  20. package/dist/resources/extensions/gsd/auto-prompts.js +329 -102
  21. package/dist/resources/extensions/gsd/auto-recovery.js +195 -23
  22. package/dist/resources/extensions/gsd/auto-start.js +34 -24
  23. package/dist/resources/extensions/gsd/auto-tool-tracking.js +47 -7
  24. package/dist/resources/extensions/gsd/auto-worktree.js +122 -26
  25. package/dist/resources/extensions/gsd/auto.js +31 -20
  26. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +9 -1
  27. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +209 -0
  28. package/dist/resources/extensions/gsd/bootstrap/provider-error-resume.js +3 -6
  29. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +7 -3
  30. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +127 -9
  31. package/dist/resources/extensions/gsd/component-loader.js +447 -0
  32. package/dist/resources/extensions/gsd/component-types.js +69 -0
  33. package/dist/resources/extensions/gsd/detection.js +49 -1
  34. package/dist/resources/extensions/gsd/docs/preferences-reference.md +1 -1
  35. package/dist/resources/extensions/gsd/gate-registry.js +2 -2
  36. package/dist/resources/extensions/gsd/git-constants.js +28 -1
  37. package/dist/resources/extensions/gsd/git-self-heal.js +27 -0
  38. package/dist/resources/extensions/gsd/git-service.js +126 -2
  39. package/dist/resources/extensions/gsd/gsd-db.js +6 -3
  40. package/dist/resources/extensions/gsd/guided-flow.js +17 -5
  41. package/dist/resources/extensions/gsd/memory-extractor.js +7 -1
  42. package/dist/resources/extensions/gsd/milestone-scope-classifier.js +299 -0
  43. package/dist/resources/extensions/gsd/model-cost-table.js +3 -0
  44. package/dist/resources/extensions/gsd/model-router.js +6 -0
  45. package/dist/resources/extensions/gsd/native-git-bridge.js +34 -4
  46. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +6 -2
  47. package/dist/resources/extensions/gsd/prompts/plan-slice.md +15 -2
  48. package/dist/resources/extensions/gsd/safety/git-checkpoint.js +11 -0
  49. package/dist/resources/extensions/gsd/service-tier.js +5 -2
  50. package/dist/resources/extensions/gsd/session-lock.js +19 -10
  51. package/dist/resources/extensions/gsd/skill-manifest.js +168 -0
  52. package/dist/resources/extensions/gsd/slice-parallel-orchestrator.js +278 -8
  53. package/dist/resources/extensions/gsd/state.js +44 -33
  54. package/dist/resources/extensions/gsd/sync-lock.js +98 -42
  55. package/dist/resources/extensions/gsd/unit-context-composer.js +147 -0
  56. package/dist/resources/extensions/gsd/unit-context-manifest.js +370 -0
  57. package/dist/resources/extensions/gsd/uok/gate-runner.js +53 -5
  58. package/dist/resources/extensions/gsd/workflow-mcp.js +6 -0
  59. package/dist/resources/extensions/gsd/worktree-manager.js +34 -8
  60. package/dist/resources/extensions/mcp-client/index.js +3 -1
  61. package/dist/resources/extensions/ollama/index.js +5 -1
  62. package/dist/resources/extensions/remote-questions/manager.js +11 -5
  63. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  64. package/dist/web/standalone/.next/BUILD_ID +1 -1
  65. package/dist/web/standalone/.next/app-path-routes-manifest.json +5 -5
  66. package/dist/web/standalone/.next/build-manifest.json +2 -2
  67. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  68. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  69. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  70. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  71. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  72. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  73. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  74. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  75. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  76. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  77. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  78. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  79. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  80. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  81. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  82. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  83. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  84. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  85. package/dist/web/standalone/.next/server/app/index.html +1 -1
  86. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  87. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  88. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  89. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  90. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  91. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  92. package/dist/web/standalone/.next/server/app-paths-manifest.json +5 -5
  93. package/dist/web/standalone/.next/server/chunks/1926.js +1 -1
  94. package/dist/web/standalone/.next/server/chunks/6897.js +1 -1
  95. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  96. package/dist/web/standalone/.next/server/middleware-manifest.json +5 -5
  97. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  98. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  99. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  100. package/package.json +2 -3
  101. package/packages/daemon/src/logger.ts +4 -3
  102. package/packages/mcp-server/dist/server.d.ts +24 -0
  103. package/packages/mcp-server/dist/server.d.ts.map +1 -1
  104. package/packages/mcp-server/dist/server.js +88 -87
  105. package/packages/mcp-server/dist/server.js.map +1 -1
  106. package/packages/mcp-server/src/mcp-server.test.ts +25 -3
  107. package/packages/mcp-server/src/readers/graph.test.ts +87 -15
  108. package/packages/mcp-server/src/secure-env-collect.test.ts +232 -237
  109. package/packages/mcp-server/src/server.ts +131 -105
  110. package/packages/mcp-server/src/workflow-tools.test.ts +80 -39
  111. package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
  112. package/packages/native/package.json +1 -1
  113. package/packages/native/src/__tests__/_test-coverage-guard.test.mjs +98 -0
  114. package/packages/native/src/__tests__/module-compat.test.mjs +59 -27
  115. package/packages/native/src/__tests__/ps.test.mjs +14 -8
  116. package/packages/native/src/__tests__/stream-process.test.mjs +23 -2
  117. package/packages/native/src/__tests__/truncate.test.mjs +17 -2
  118. package/packages/pi-agent-core/src/agent-loop.test.ts +5 -15
  119. package/packages/pi-agent-core/src/agent.test.ts +96 -102
  120. package/packages/pi-agent-core/tsconfig.tsbuildinfo +1 -1
  121. package/packages/pi-ai/dist/models/capability-patches.d.ts.map +1 -1
  122. package/packages/pi-ai/dist/models/capability-patches.js +9 -2
  123. package/packages/pi-ai/dist/models/capability-patches.js.map +1 -1
  124. package/packages/pi-ai/dist/models/generated/index.d.ts +34 -0
  125. package/packages/pi-ai/dist/models/generated/index.d.ts.map +1 -1
  126. package/packages/pi-ai/dist/models/generated/openai-codex.d.ts +17 -0
  127. package/packages/pi-ai/dist/models/generated/openai-codex.d.ts.map +1 -1
  128. package/packages/pi-ai/dist/models/generated/openai-codex.js +17 -0
  129. package/packages/pi-ai/dist/models/generated/openai-codex.js.map +1 -1
  130. package/packages/pi-ai/dist/models/generated/openai.d.ts +17 -0
  131. package/packages/pi-ai/dist/models/generated/openai.d.ts.map +1 -1
  132. package/packages/pi-ai/dist/models/generated/openai.js +17 -0
  133. package/packages/pi-ai/dist/models/generated/openai.js.map +1 -1
  134. package/packages/pi-ai/dist/models.generated.test.js +43 -70
  135. package/packages/pi-ai/dist/models.generated.test.js.map +1 -1
  136. package/packages/pi-ai/dist/models.test.js +36 -11
  137. package/packages/pi-ai/dist/models.test.js.map +1 -1
  138. package/packages/pi-ai/scripts/generate-models.ts +44 -0
  139. package/packages/pi-ai/src/models/capability-patches.ts +10 -2
  140. package/packages/pi-ai/src/models/generated/openai-codex.ts +17 -0
  141. package/packages/pi-ai/src/models/generated/openai.ts +17 -0
  142. package/packages/pi-ai/src/models.generated.test.ts +46 -73
  143. package/packages/pi-ai/src/models.test.ts +48 -11
  144. package/packages/pi-ai/tsconfig.tsbuildinfo +1 -1
  145. package/packages/pi-coding-agent/dist/core/agent-session-abort-order.test.js +96 -32
  146. package/packages/pi-coding-agent/dist/core/agent-session-abort-order.test.js.map +1 -1
  147. package/packages/pi-coding-agent/dist/core/agent-session-model-switch.test.js +75 -12
  148. package/packages/pi-coding-agent/dist/core/agent-session-model-switch.test.js.map +1 -1
  149. package/packages/pi-coding-agent/dist/core/agent-session-tool-refresh.test.js +99 -31
  150. package/packages/pi-coding-agent/dist/core/agent-session-tool-refresh.test.js.map +1 -1
  151. package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts +5 -0
  152. package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
  153. package/packages/pi-coding-agent/dist/core/extensions/loader.js +61 -0
  154. package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
  155. package/packages/pi-coding-agent/dist/core/lsp/lsp-integration.test.js +30 -4
  156. package/packages/pi-coding-agent/dist/core/lsp/lsp-integration.test.js.map +1 -1
  157. package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js +17 -0
  158. package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js.map +1 -1
  159. package/packages/pi-coding-agent/dist/core/resource-loader-cache-reset.test.js +76 -18
  160. package/packages/pi-coding-agent/dist/core/resource-loader-cache-reset.test.js.map +1 -1
  161. package/packages/pi-coding-agent/dist/core/retry-handler.d.ts.map +1 -1
  162. package/packages/pi-coding-agent/dist/core/retry-handler.js +2 -6
  163. package/packages/pi-coding-agent/dist/core/retry-handler.js.map +1 -1
  164. package/packages/pi-coding-agent/dist/core/retry-handler.test.js +5 -1
  165. package/packages/pi-coding-agent/dist/core/retry-handler.test.js.map +1 -1
  166. package/packages/pi-coding-agent/dist/core/retryable-error-regex.d.ts +18 -0
  167. package/packages/pi-coding-agent/dist/core/retryable-error-regex.d.ts.map +1 -0
  168. package/packages/pi-coding-agent/dist/core/retryable-error-regex.js +18 -0
  169. package/packages/pi-coding-agent/dist/core/retryable-error-regex.js.map +1 -0
  170. package/packages/pi-coding-agent/dist/core/system-prompt.d.ts +20 -0
  171. package/packages/pi-coding-agent/dist/core/system-prompt.d.ts.map +1 -1
  172. package/packages/pi-coding-agent/dist/core/system-prompt.js +16 -2
  173. package/packages/pi-coding-agent/dist/core/system-prompt.js.map +1 -1
  174. package/packages/pi-coding-agent/dist/index.d.ts +1 -0
  175. package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
  176. package/packages/pi-coding-agent/dist/index.js +1 -0
  177. package/packages/pi-coding-agent/dist/index.js.map +1 -1
  178. package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.test.js +20 -13
  179. package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.test.js.map +1 -1
  180. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.d.ts.map +1 -1
  181. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js +18 -3
  182. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js.map +1 -1
  183. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js +125 -0
  184. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js.map +1 -1
  185. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts +2 -0
  186. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts.map +1 -1
  187. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.js.map +1 -1
  188. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +4 -0
  189. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  190. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +105 -13
  191. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  192. package/packages/pi-coding-agent/dist/tests/system-prompt-skill-filter.test.d.ts +2 -0
  193. package/packages/pi-coding-agent/dist/tests/system-prompt-skill-filter.test.d.ts.map +1 -0
  194. package/packages/pi-coding-agent/dist/tests/system-prompt-skill-filter.test.js +130 -0
  195. package/packages/pi-coding-agent/dist/tests/system-prompt-skill-filter.test.js.map +1 -0
  196. package/packages/pi-coding-agent/src/core/agent-session-abort-order.test.ts +113 -37
  197. package/packages/pi-coding-agent/src/core/agent-session-model-switch.test.ts +89 -17
  198. package/packages/pi-coding-agent/src/core/agent-session-tool-refresh.test.ts +112 -43
  199. package/packages/pi-coding-agent/src/core/extensions/loader.ts +58 -0
  200. package/packages/pi-coding-agent/src/core/lsp/lsp-integration.test.ts +35 -4
  201. package/packages/pi-coding-agent/src/core/model-registry-auth-mode.test.ts +20 -0
  202. package/packages/pi-coding-agent/src/core/resource-loader-cache-reset.test.ts +93 -28
  203. package/packages/pi-coding-agent/src/core/retry-handler.test.ts +5 -1
  204. package/packages/pi-coding-agent/src/core/retry-handler.ts +2 -8
  205. package/packages/pi-coding-agent/src/core/retryable-error-regex.ts +18 -0
  206. package/packages/pi-coding-agent/src/core/system-prompt.ts +35 -1
  207. package/packages/pi-coding-agent/src/index.ts +1 -0
  208. package/packages/pi-coding-agent/src/modes/interactive/components/dynamic-border.test.ts +26 -20
  209. package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.test.ts +146 -1
  210. package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.ts +20 -3
  211. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode-state.ts +2 -0
  212. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +119 -13
  213. package/packages/pi-coding-agent/src/tests/system-prompt-skill-filter.test.ts +157 -0
  214. package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
  215. package/packages/pi-tui/dist/__tests__/autocomplete.test.js +18 -8
  216. package/packages/pi-tui/dist/__tests__/autocomplete.test.js.map +1 -1
  217. package/packages/pi-tui/dist/__tests__/overlay-layout.test.js +128 -17
  218. package/packages/pi-tui/dist/__tests__/overlay-layout.test.js.map +1 -1
  219. package/packages/pi-tui/dist/__tests__/stdin-buffer.test.js +36 -12
  220. package/packages/pi-tui/dist/__tests__/stdin-buffer.test.js.map +1 -1
  221. package/packages/pi-tui/dist/__tests__/tui.test.js +18 -30
  222. package/packages/pi-tui/dist/__tests__/tui.test.js.map +1 -1
  223. package/packages/pi-tui/dist/components/__tests__/input.test.js +10 -3
  224. package/packages/pi-tui/dist/components/__tests__/input.test.js.map +1 -1
  225. package/packages/pi-tui/dist/components/__tests__/loader.test.js +53 -9
  226. package/packages/pi-tui/dist/components/__tests__/loader.test.js.map +1 -1
  227. package/packages/pi-tui/dist/components/__tests__/markdown-maxlines.test.js +6 -2
  228. package/packages/pi-tui/dist/components/__tests__/markdown-maxlines.test.js.map +1 -1
  229. package/packages/pi-tui/dist/components/editor.d.ts +14 -0
  230. package/packages/pi-tui/dist/components/editor.d.ts.map +1 -1
  231. package/packages/pi-tui/dist/components/editor.js +19 -0
  232. package/packages/pi-tui/dist/components/editor.js.map +1 -1
  233. package/packages/pi-tui/dist/components/image.test.js +6 -5
  234. package/packages/pi-tui/dist/components/image.test.js.map +1 -1
  235. package/packages/pi-tui/dist/editor-component.d.ts +2 -0
  236. package/packages/pi-tui/dist/editor-component.d.ts.map +1 -1
  237. package/packages/pi-tui/dist/editor-component.js.map +1 -1
  238. package/packages/pi-tui/src/__tests__/autocomplete.test.ts +24 -8
  239. package/packages/pi-tui/src/__tests__/overlay-layout.test.ts +140 -17
  240. package/packages/pi-tui/src/__tests__/stdin-buffer.test.ts +41 -12
  241. package/packages/pi-tui/src/__tests__/tui.test.ts +18 -37
  242. package/packages/pi-tui/src/components/__tests__/input.test.ts +19 -3
  243. package/packages/pi-tui/src/components/__tests__/loader.test.ts +112 -35
  244. package/packages/pi-tui/src/components/__tests__/markdown-maxlines.test.ts +9 -2
  245. package/packages/pi-tui/src/components/editor.ts +22 -0
  246. package/packages/pi-tui/src/components/image.test.ts +10 -5
  247. package/packages/pi-tui/src/editor-component.ts +3 -0
  248. package/packages/pi-tui/tsconfig.tsbuildinfo +1 -1
  249. package/packages/rpc-client/dist/rpc-client.test.js +101 -51
  250. package/packages/rpc-client/dist/rpc-client.test.js.map +1 -1
  251. package/packages/rpc-client/src/rpc-client.test.ts +109 -52
  252. package/packages/rpc-client/tsconfig.tsbuildinfo +1 -1
  253. package/scripts/install.js +15 -1
  254. package/src/resources/extensions/browser-tools/capture.ts +12 -0
  255. package/src/resources/extensions/browser-tools/tests/browser-tools-integration.test.mjs +8 -59
  256. package/src/resources/extensions/browser-tools/tests/browser-tools-unit.test.cjs +36 -24
  257. package/src/resources/extensions/browser-tools/tests/capture-sharp-optional.test.cjs +69 -71
  258. package/src/resources/extensions/browser-tools/tools/forms.ts +5 -1
  259. package/src/resources/extensions/browser-tools/tools/intent.ts +5 -1
  260. package/src/resources/extensions/claude-code-cli/readiness.ts +5 -1
  261. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +518 -19
  262. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +919 -75
  263. package/src/resources/extensions/github-sync/tests/cli.test.ts +76 -7
  264. package/src/resources/extensions/github-sync/tests/templates.test.ts +33 -1
  265. package/src/resources/extensions/gsd/auto/loop.ts +47 -0
  266. package/src/resources/extensions/gsd/auto/phases.ts +16 -20
  267. package/src/resources/extensions/gsd/auto/session.ts +0 -2
  268. package/src/resources/extensions/gsd/auto-dispatch.ts +113 -24
  269. package/src/resources/extensions/gsd/auto-model-selection.ts +131 -4
  270. package/src/resources/extensions/gsd/auto-post-unit.ts +82 -73
  271. package/src/resources/extensions/gsd/auto-prompts.ts +330 -90
  272. package/src/resources/extensions/gsd/auto-recovery.ts +225 -24
  273. package/src/resources/extensions/gsd/auto-start.ts +54 -6
  274. package/src/resources/extensions/gsd/auto-tool-tracking.ts +51 -7
  275. package/src/resources/extensions/gsd/auto-worktree.ts +130 -26
  276. package/src/resources/extensions/gsd/auto.ts +43 -22
  277. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +9 -1
  278. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +221 -0
  279. package/src/resources/extensions/gsd/bootstrap/provider-error-resume.ts +3 -7
  280. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +7 -3
  281. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +158 -9
  282. package/src/resources/extensions/gsd/component-loader.ts +598 -0
  283. package/src/resources/extensions/gsd/component-types.ts +362 -0
  284. package/src/resources/extensions/gsd/detection.ts +58 -1
  285. package/src/resources/extensions/gsd/docs/preferences-reference.md +1 -1
  286. package/src/resources/extensions/gsd/gate-registry.ts +2 -2
  287. package/src/resources/extensions/gsd/git-constants.ts +30 -1
  288. package/src/resources/extensions/gsd/git-self-heal.ts +31 -0
  289. package/src/resources/extensions/gsd/git-service.ts +133 -2
  290. package/src/resources/extensions/gsd/gsd-db.ts +6 -3
  291. package/src/resources/extensions/gsd/guided-flow.ts +20 -5
  292. package/src/resources/extensions/gsd/memory-extractor.ts +11 -3
  293. package/src/resources/extensions/gsd/milestone-scope-classifier.ts +366 -0
  294. package/src/resources/extensions/gsd/model-cost-table.ts +3 -0
  295. package/src/resources/extensions/gsd/model-router.ts +6 -0
  296. package/src/resources/extensions/gsd/native-git-bridge.ts +34 -4
  297. package/src/resources/extensions/gsd/prompts/complete-milestone.md +6 -2
  298. package/src/resources/extensions/gsd/prompts/plan-slice.md +15 -2
  299. package/src/resources/extensions/gsd/safety/git-checkpoint.ts +15 -0
  300. package/src/resources/extensions/gsd/service-tier.ts +5 -2
  301. package/src/resources/extensions/gsd/session-lock.ts +20 -10
  302. package/src/resources/extensions/gsd/skill-manifest.ts +175 -0
  303. package/src/resources/extensions/gsd/slice-parallel-orchestrator.ts +309 -8
  304. package/src/resources/extensions/gsd/state.ts +49 -44
  305. package/src/resources/extensions/gsd/sync-lock.ts +97 -39
  306. package/src/resources/extensions/gsd/tests/artifact-retry-cap.test.ts +270 -0
  307. package/src/resources/extensions/gsd/tests/auto-deterministic-error-classification-4973.test.ts +341 -0
  308. package/src/resources/extensions/gsd/tests/auto-discuss-milestone-deadlock-4973.test.ts +264 -0
  309. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +94 -289
  310. package/src/resources/extensions/gsd/tests/auto-model-selection-tool-poisoning.test.ts +742 -0
  311. package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +78 -0
  312. package/src/resources/extensions/gsd/tests/auto-phases-lifecycle.test.ts +61 -0
  313. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +93 -0
  314. package/src/resources/extensions/gsd/tests/auto-retry-mcp-churn-fixes.test.ts +8 -197
  315. package/src/resources/extensions/gsd/tests/auto-start-needs-discussion.test.ts +15 -58
  316. package/src/resources/extensions/gsd/tests/cache-staleness-regression.test.ts +17 -21
  317. package/src/resources/extensions/gsd/tests/complete-milestone-excerpt.test.ts +263 -0
  318. package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +25 -0
  319. package/src/resources/extensions/gsd/tests/complete-slice-composer.test.ts +192 -0
  320. package/src/resources/extensions/gsd/tests/complete-task.test.ts +16 -8
  321. package/src/resources/extensions/gsd/tests/component-loader.test.ts +589 -0
  322. package/src/resources/extensions/gsd/tests/component-types.test.ts +127 -0
  323. package/src/resources/extensions/gsd/tests/crash-recovery.test.ts +50 -1
  324. package/src/resources/extensions/gsd/tests/derive-state-crossval.test.ts +3 -3
  325. package/src/resources/extensions/gsd/tests/derive-state-db-disk-reconcile.test.ts +40 -0
  326. package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +91 -3
  327. package/src/resources/extensions/gsd/tests/derive-state.test.ts +4 -3
  328. package/src/resources/extensions/gsd/tests/dispatcher-stuck-planning.test.ts +3 -2
  329. package/src/resources/extensions/gsd/tests/extension-bootstrap-isolation.test.ts +139 -129
  330. package/src/resources/extensions/gsd/tests/finalize-timeout-guard.test.ts +9 -105
  331. package/src/resources/extensions/gsd/tests/gate-state-canonicalization.test.ts +102 -0
  332. package/src/resources/extensions/gsd/tests/gate-storage.test.ts +1 -1
  333. package/src/resources/extensions/gsd/tests/hook-key-parsing.test.ts +4 -55
  334. package/src/resources/extensions/gsd/tests/integration/all-milestones-complete-merge.test.ts +7 -57
  335. package/src/resources/extensions/gsd/tests/integration/auto-recovery.test.ts +20 -0
  336. package/src/resources/extensions/gsd/tests/integration/doctor-proactive.test.ts +18 -2
  337. package/src/resources/extensions/gsd/tests/integration/queue-completed-milestone-perf.test.ts +10 -4
  338. package/src/resources/extensions/gsd/tests/integration/state-machine-edge-cases.test.ts +144 -7
  339. package/src/resources/extensions/gsd/tests/integration/state-machine-live-validation.test.ts +4 -0
  340. package/src/resources/extensions/gsd/tests/integration/state-machine-runtime-failures.test.ts +2 -16
  341. package/src/resources/extensions/gsd/tests/interrupted-session-ui.test.ts +6 -9
  342. package/src/resources/extensions/gsd/tests/mcp-client-security.test.ts +8 -37
  343. package/src/resources/extensions/gsd/tests/memory-extractor.test.ts +5 -15
  344. package/src/resources/extensions/gsd/tests/merge-conflict-stops-loop.test.ts +227 -62
  345. package/src/resources/extensions/gsd/tests/milestone-scope-classifier.test.ts +187 -0
  346. package/src/resources/extensions/gsd/tests/model-cost-table.test.ts +9 -1
  347. package/src/resources/extensions/gsd/tests/model-router.test.ts +1 -1
  348. package/src/resources/extensions/gsd/tests/native-git-bridge-exec-fallback.test.ts +6 -49
  349. package/src/resources/extensions/gsd/tests/notification-widget.test.ts +6 -3
  350. package/src/resources/extensions/gsd/tests/parallel-research-dispatch.test.ts +273 -133
  351. package/src/resources/extensions/gsd/tests/pipeline-variant-dispatch.test.ts +301 -0
  352. package/src/resources/extensions/gsd/tests/pre-execution-pause-wiring.test.ts +32 -1
  353. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +23 -24
  354. package/src/resources/extensions/gsd/tests/queue-auto-guard.test.ts +32 -0
  355. package/src/resources/extensions/gsd/tests/ready-phrase-no-files-4573.test.ts +75 -2
  356. package/src/resources/extensions/gsd/tests/reassess-default-optin.test.ts +132 -0
  357. package/src/resources/extensions/gsd/tests/recovery-attempts-reset.test.ts +8 -40
  358. package/src/resources/extensions/gsd/tests/regex-hardening.test.ts +136 -256
  359. package/src/resources/extensions/gsd/tests/research-milestone-composer.test.ts +114 -0
  360. package/src/resources/extensions/gsd/tests/run-uat-composer.test.ts +148 -0
  361. package/src/resources/extensions/gsd/tests/service-tier.test.ts +4 -0
  362. package/src/resources/extensions/gsd/tests/session-lock-regression.test.ts +29 -0
  363. package/src/resources/extensions/gsd/tests/silent-catch-diagnostics.test.ts +55 -95
  364. package/src/resources/extensions/gsd/tests/single-writer-v3-tool-surface.test.ts +158 -0
  365. package/src/resources/extensions/gsd/tests/skill-activation.test.ts +120 -1
  366. package/src/resources/extensions/gsd/tests/skill-manifest.test.ts +112 -0
  367. package/src/resources/extensions/gsd/tests/slice-parallel-orchestrator.test.ts +164 -1
  368. package/src/resources/extensions/gsd/tests/state-machine-full-walkthrough.test.ts +5 -5
  369. package/src/resources/extensions/gsd/tests/structured-data-formatter.test.ts +11 -92
  370. package/src/resources/extensions/gsd/tests/survivor-branch-complete.test.ts +102 -101
  371. package/src/resources/extensions/gsd/tests/sync-lock.test.ts +31 -0
  372. package/src/resources/extensions/gsd/tests/test-helpers.test.ts +12 -61
  373. package/src/resources/extensions/gsd/tests/test-helpers.ts +21 -8
  374. package/src/resources/extensions/gsd/tests/tool-invocation-error-loop-break.test.ts +61 -1
  375. package/src/resources/extensions/gsd/tests/tool-naming.test.ts +8 -1
  376. package/src/resources/extensions/gsd/tests/unit-context-composer.test.ts +355 -0
  377. package/src/resources/extensions/gsd/tests/unit-context-manifest.test.ts +258 -0
  378. package/src/resources/extensions/gsd/tests/uok-gate-runner.test.ts +75 -0
  379. package/src/resources/extensions/gsd/tests/uok-gitops-wiring.test.ts +49 -26
  380. package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +1 -0
  381. package/src/resources/extensions/gsd/tests/verify-artifact-tightened.test.ts +144 -81
  382. package/src/resources/extensions/gsd/tests/visualizer-critical-path.test.ts +20 -54
  383. package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +342 -277
  384. package/src/resources/extensions/gsd/tests/worker-model-override.test.ts +37 -29
  385. package/src/resources/extensions/gsd/tests/worktree-db.test.ts +226 -266
  386. package/src/resources/extensions/gsd/tests/worktree-health-monorepo.test.ts +103 -67
  387. package/src/resources/extensions/gsd/tests/worktree-nested-git-safety.test.ts +92 -90
  388. package/src/resources/extensions/gsd/tests/worktree-submodule-safety.test.ts +238 -59
  389. package/src/resources/extensions/gsd/tests/worktree-sync-overwrite-loop.test.ts +113 -161
  390. package/src/resources/extensions/gsd/tests/write-gate-planning-unit.test.ts +262 -0
  391. package/src/resources/extensions/gsd/tests/write-gate-predicates.test.ts +186 -0
  392. package/src/resources/extensions/gsd/tests/write-gate.test.ts +7 -5
  393. package/src/resources/extensions/gsd/tests/zombie-gsd-state.test.ts +80 -96
  394. package/src/resources/extensions/gsd/types.ts +3 -3
  395. package/src/resources/extensions/gsd/unit-context-composer.ts +218 -0
  396. package/src/resources/extensions/gsd/unit-context-manifest.ts +574 -0
  397. package/src/resources/extensions/gsd/uok/gate-runner.ts +65 -5
  398. package/src/resources/extensions/gsd/workflow-mcp.ts +6 -0
  399. package/src/resources/extensions/gsd/worktree-manager.ts +55 -7
  400. package/src/resources/extensions/mcp-client/index.ts +3 -1
  401. package/src/resources/extensions/mcp-client/tests/server-name-spaces.test.ts +70 -36
  402. package/src/resources/extensions/ollama/index.ts +5 -1
  403. package/src/resources/extensions/ollama/ollama-auth-mode.test.ts +123 -15
  404. package/src/resources/extensions/ollama/ollama-status-indicator.test.ts +206 -19
  405. package/src/resources/extensions/remote-questions/manager.ts +36 -4
  406. package/src/resources/extensions/remote-questions/tests/command-polling.test.ts +200 -190
  407. package/src/resources/extensions/shared/tests/interview-preview.test.ts +11 -3
  408. package/src/resources/extensions/voice/tests/linux-ready.test.ts +129 -113
  409. package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.d.ts +0 -2
  410. package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.d.ts.map +0 -1
  411. package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.js +0 -289
  412. package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.js.map +0 -1
  413. package/packages/pi-ai/src/utils/oauth/oauth-providers.test.ts +0 -363
  414. package/src/resources/extensions/gsd/tests/auto-start-model-capture.test.ts +0 -144
  415. package/src/resources/extensions/gsd/tests/complete-milestone-false-merge.test.ts +0 -157
  416. package/src/resources/extensions/gsd/tests/dashboard-model-label-ordering.test.ts +0 -107
  417. package/src/resources/extensions/gsd/tests/find-missing-summaries-closed.test.ts +0 -48
  418. package/src/resources/extensions/gsd/tests/forensics-context-persist.test.ts +0 -159
  419. package/src/resources/extensions/gsd/tests/forensics-db-completion.test.ts +0 -96
  420. package/src/resources/extensions/gsd/tests/forensics-dedup.test.ts +0 -79
  421. package/src/resources/extensions/gsd/tests/forensics-hook-key-parse.test.ts +0 -75
  422. package/src/resources/extensions/gsd/tests/forensics-journal.test.ts +0 -162
  423. package/src/resources/extensions/gsd/tests/forensics-worktree-telemetry.test.ts +0 -145
  424. package/src/resources/extensions/gsd/tests/gitignore-bg-shell.test.ts +0 -38
  425. package/src/resources/extensions/gsd/tests/gsd-no-project-error.test.ts +0 -73
  426. package/src/resources/extensions/gsd/tests/idle-watchdog-stall-override.test.ts +0 -130
  427. package/src/resources/extensions/gsd/tests/import-done-milestones.test.ts +0 -43
  428. /package/dist/web/standalone/.next/static/{Cev5xrAYA3ZGTRLyjR2fX → SvCJDZPQW104bR1KnBQg1}/_buildManifest.js +0 -0
  429. /package/dist/web/standalone/.next/static/{Cev5xrAYA3ZGTRLyjR2fX → SvCJDZPQW104bR1KnBQg1}/_ssgManifest.js +0 -0
@@ -0,0 +1,168 @@
1
+ // GSD2 + skill-manifest — per-unit-type skill allowlist resolver (RFC #4779)
2
+ //
3
+ // Each auto-mode unit type can declare which skills are relevant to it. This
4
+ // trims the set of skills considered for activation in the per-unit prompt,
5
+ // reducing prompt token bloat and sharpening model focus.
6
+ //
7
+ // Contract:
8
+ // - Unknown unit types fall through to "all skills" (current behavior).
9
+ // - A manifest entry referencing a skill that is not installed is a silent
10
+ // no-op at filter time — the filter passes through installed skills only.
11
+ // - The allowlist is an inclusion list: only skills whose normalized name
12
+ // appears in the allowlist are retained. Order is not preserved.
13
+ //
14
+ // Phase 1 scope: seed manifests for a small number of unit types as proof.
15
+ // Additional unit types can be added incrementally; each addition is a pure
16
+ // data change with no wiring cost.
17
+ import { logWarning } from "./workflow-logger.js";
18
+ /** Normalize a skill reference the same way callers do (lowercase, trim). */
19
+ function normalize(name) {
20
+ return name.trim().toLowerCase();
21
+ }
22
+ /**
23
+ * Allowlist per unit type. Keys match unit type identifiers used by auto-mode
24
+ * dispatch. Values are normalized skill names.
25
+ *
26
+ * Wildcard semantics: a unit type absent from this map resolves to `null`
27
+ * (wildcard) — meaning "all installed skills are eligible". Prefer absence
28
+ * over an exhaustive list when uncertain.
29
+ */
30
+ const UNIT_TYPE_SKILL_MANIFEST = {
31
+ // Milestone-level planning / meta flows — predictable skill sets.
32
+ "research-milestone": [
33
+ "write-docs",
34
+ "write-milestone-brief",
35
+ "decompose-into-slices",
36
+ "grill-me",
37
+ "design-an-interface",
38
+ "api-design",
39
+ "observability",
40
+ ],
41
+ "plan-milestone": [
42
+ "write-milestone-brief",
43
+ "decompose-into-slices",
44
+ "design-an-interface",
45
+ "grill-me",
46
+ "write-docs",
47
+ "api-design",
48
+ "tdd",
49
+ "verify-before-complete",
50
+ ],
51
+ "complete-milestone": [
52
+ "verify-before-complete",
53
+ "write-docs",
54
+ "handoff",
55
+ "forensics",
56
+ "observability",
57
+ "security-review",
58
+ ],
59
+ "validate-milestone": [
60
+ "verify-before-complete",
61
+ "review",
62
+ "test",
63
+ "lint",
64
+ "security-review",
65
+ "accessibility",
66
+ "forensics",
67
+ "observability",
68
+ ],
69
+ "reassess-roadmap": [
70
+ "decompose-into-slices",
71
+ "grill-me",
72
+ "write-milestone-brief",
73
+ "write-docs",
74
+ "forensics",
75
+ ],
76
+ // Slice-level research / planning.
77
+ "research-slice": [
78
+ "write-docs",
79
+ "decompose-into-slices",
80
+ "design-an-interface",
81
+ "grill-me",
82
+ "api-design",
83
+ "observability",
84
+ ],
85
+ "plan-slice": [
86
+ "decompose-into-slices",
87
+ "design-an-interface",
88
+ "grill-me",
89
+ "write-docs",
90
+ "api-design",
91
+ "tdd",
92
+ "verify-before-complete",
93
+ ],
94
+ "refine-slice": [
95
+ "decompose-into-slices",
96
+ "design-an-interface",
97
+ "grill-me",
98
+ "write-docs",
99
+ "api-design",
100
+ "tdd",
101
+ "verify-before-complete",
102
+ ],
103
+ "replan-slice": [
104
+ "decompose-into-slices",
105
+ "grill-me",
106
+ "design-an-interface",
107
+ "write-docs",
108
+ "api-design",
109
+ ],
110
+ "run-uat": [
111
+ "verify-before-complete",
112
+ "test",
113
+ "review",
114
+ "accessibility",
115
+ ],
116
+ // `execute-task` intentionally omitted — implementation hot path covers a
117
+ // wide surface of technologies; wildcard fallback preserves today's
118
+ // behavior until per-task skill hints can be derived from task-plan
119
+ // frontmatter. See RFC #4779.
120
+ };
121
+ /**
122
+ * Resolve the skill allowlist for a unit type.
123
+ *
124
+ * @returns Array of normalized skill names when an entry exists, or `null`
125
+ * when the unit type is unknown (wildcard — caller should not filter).
126
+ */
127
+ export function resolveSkillManifest(unitType) {
128
+ if (!unitType)
129
+ return null;
130
+ const entry = UNIT_TYPE_SKILL_MANIFEST[unitType];
131
+ if (!entry)
132
+ return null;
133
+ return entry.map(normalize);
134
+ }
135
+ /**
136
+ * Filter a skill list by the manifest for `unitType`. Pass-through when the
137
+ * manifest is wildcard (unknown unit type) or `unitType` is undefined.
138
+ */
139
+ export function filterSkillsByManifest(skills, unitType) {
140
+ const allowlist = resolveSkillManifest(unitType);
141
+ if (allowlist === null)
142
+ return skills;
143
+ const allowed = new Set(allowlist);
144
+ return skills.filter(skill => allowed.has(normalize(skill.name)));
145
+ }
146
+ /**
147
+ * Dev-mode guard: warn once per process if a manifest entry references a name
148
+ * that is not currently installed. Silent in production.
149
+ */
150
+ const warnedMissing = new Set();
151
+ export function warnIfManifestHasMissingSkills(unitType, installedNames) {
152
+ // Strict mode is intentionally opt-in via exactly "1"; values like "0" or
153
+ // "false" must preserve the normal silent manifest behavior.
154
+ if (process.env.GSD_SKILL_MANIFEST_STRICT !== "1")
155
+ return;
156
+ const allowlist = resolveSkillManifest(unitType);
157
+ if (!allowlist)
158
+ return;
159
+ for (const name of allowlist) {
160
+ const key = `${unitType}:${name}`;
161
+ if (warnedMissing.has(key))
162
+ continue;
163
+ if (!installedNames.has(name)) {
164
+ warnedMissing.add(key);
165
+ logWarning("prompt", `skill-manifest: references uninstalled skill '${name}' for unit '${unitType}'`);
166
+ }
167
+ }
168
+ }
@@ -12,8 +12,9 @@
12
12
  * - Lock env: GSD_SLICE_LOCK (in addition to GSD_MILESTONE_LOCK)
13
13
  * - Conflict check: file overlap between slice plans (slice-parallel-conflict.ts)
14
14
  */
15
- import { spawn } from "node:child_process";
16
- import { appendFileSync, existsSync, mkdirSync, } from "node:fs";
15
+ import { execFileSync, spawn } from "node:child_process";
16
+ import { randomUUID } from "node:crypto";
17
+ import { appendFileSync, existsSync, writeFileSync, readFileSync, mkdirSync, renameSync, unlinkSync, } from "node:fs";
17
18
  import { join, dirname } from "node:path";
18
19
  import { fileURLToPath } from "node:url";
19
20
  import { gsdRoot } from "./paths.js";
@@ -24,12 +25,243 @@ import { getErrorMessage } from "./error-utils.js";
24
25
  import { selectConflictFreeBatch } from "./uok/execution-graph.js";
25
26
  // ─── Module State ──────────────────────────────────────────────────────────
26
27
  let sliceState = null;
28
+ // ─── Persisted State (crash recovery) ──────────────────────────────────────
29
+ //
30
+ // Mirrors parallel-orchestrator.ts. Without persistence, a coordinator crash
31
+ // leaves orphaned worktrees on disk with no way to detect or clean them up
32
+ // on next session start. (Issue #4980 HIGH-8)
33
+ const SLICE_ORCHESTRATOR_STATE_FILE = "slice-orchestrator.json";
34
+ const TMP_SUFFIX = ".tmp";
35
+ function sliceStateFilePath(basePath) {
36
+ return join(gsdRoot(basePath), SLICE_ORCHESTRATOR_STATE_FILE);
37
+ }
38
+ function isPidAlive(pid) {
39
+ if (!Number.isInteger(pid) || pid <= 0)
40
+ return false;
41
+ try {
42
+ process.kill(pid, 0);
43
+ return true;
44
+ }
45
+ catch (err) {
46
+ if (err.code === "EPERM")
47
+ return true;
48
+ return false;
49
+ }
50
+ }
51
+ function readLinuxProcessStartFingerprint(pid) {
52
+ try {
53
+ const stat = readFileSync(`/proc/${pid}/stat`, "utf-8");
54
+ const afterCommand = stat.slice(stat.lastIndexOf(")") + 2).trim();
55
+ const fields = afterCommand.split(/\s+/);
56
+ const startTimeTicks = fields[19];
57
+ return startTimeTicks ? `linux-stat:${startTimeTicks}` : null;
58
+ }
59
+ catch {
60
+ return null;
61
+ }
62
+ }
63
+ function readPsProcessStartFingerprint(pid) {
64
+ try {
65
+ const raw = execFileSync("ps", ["-p", String(pid), "-o", "lstart="], {
66
+ stdio: ["ignore", "pipe", "ignore"],
67
+ encoding: "utf-8",
68
+ }).trim().replace(/\s+/g, " ");
69
+ return raw ? `ps-lstart:${raw}` : null;
70
+ }
71
+ catch {
72
+ return null;
73
+ }
74
+ }
75
+ function readProcessStartFingerprint(pid) {
76
+ if (!Number.isInteger(pid) || pid <= 0)
77
+ return null;
78
+ return readLinuxProcessStartFingerprint(pid) ?? readPsProcessStartFingerprint(pid);
79
+ }
80
+ function linuxProcessEnvContains(pid, key, value) {
81
+ if (process.platform !== "linux")
82
+ return null;
83
+ try {
84
+ const env = readFileSync(`/proc/${pid}/environ`, "utf-8");
85
+ return env.split("\0").includes(`${key}=${value}`);
86
+ }
87
+ catch {
88
+ return null;
89
+ }
90
+ }
91
+ function createWorkerToken(milestoneId, sliceId) {
92
+ return `slice:${milestoneId}:${sliceId}:${Date.now()}:${randomUUID()}`;
93
+ }
94
+ function isRecoveredSliceWorkerAlive(worker) {
95
+ if (!isPidAlive(worker.pid))
96
+ return false;
97
+ if (!worker.processStartFingerprint)
98
+ return false;
99
+ const currentFingerprint = readProcessStartFingerprint(worker.pid);
100
+ if (!currentFingerprint || currentFingerprint !== worker.processStartFingerprint) {
101
+ return false;
102
+ }
103
+ if (worker.workerToken) {
104
+ const envMatches = linuxProcessEnvContains(worker.pid, "GSD_SLICE_WORKER_TOKEN", worker.workerToken);
105
+ if (envMatches === false)
106
+ return false;
107
+ }
108
+ return true;
109
+ }
110
+ /**
111
+ * Persist current slice orchestrator state. Atomic write (tmp + rename) to
112
+ * prevent partial reads if the coordinator dies mid-write.
113
+ */
114
+ function persistSliceState() {
115
+ if (!sliceState)
116
+ return;
117
+ try {
118
+ const dir = gsdRoot(sliceState.basePath);
119
+ if (!existsSync(dir))
120
+ mkdirSync(dir, { recursive: true });
121
+ const persisted = {
122
+ active: sliceState.active,
123
+ workers: [...sliceState.workers.values()].map((w) => ({
124
+ milestoneId: w.milestoneId,
125
+ sliceId: w.sliceId,
126
+ pid: w.pid,
127
+ workerToken: w.workerToken,
128
+ processStartFingerprint: w.processStartFingerprint,
129
+ worktreePath: w.worktreePath,
130
+ startedAt: w.startedAt,
131
+ state: w.state,
132
+ completedUnits: w.completedUnits,
133
+ cost: w.cost,
134
+ })),
135
+ totalCost: sliceState.totalCost,
136
+ budgetCeiling: sliceState.budgetCeiling,
137
+ maxWorkers: sliceState.maxWorkers,
138
+ startedAt: sliceState.startedAt,
139
+ basePath: sliceState.basePath,
140
+ };
141
+ const dest = sliceStateFilePath(sliceState.basePath);
142
+ const tmp = dest + TMP_SUFFIX;
143
+ writeFileSync(tmp, JSON.stringify(persisted, null, 2), "utf-8");
144
+ renameSync(tmp, dest);
145
+ lastPersistTs = Date.now();
146
+ }
147
+ catch {
148
+ /* non-fatal: persistence is best-effort */
149
+ }
150
+ }
151
+ /**
152
+ * Throttled wrapper around `persistSliceState`. Skips if the last successful
153
+ * persist was less than `PERSIST_THROTTLE_MS` ago; otherwise persists
154
+ * immediately. Use this on hot paths (e.g. `message_end` events) where we
155
+ * receive many events per second per worker. Terminal events (worker exit,
156
+ * crash, stop) should call `persistSliceState()` directly to guarantee the
157
+ * final state hits disk regardless of timing.
158
+ */
159
+ const PERSIST_THROTTLE_MS = 1000;
160
+ let lastPersistTs = 0;
161
+ function persistSliceStateThrottled() {
162
+ if (Date.now() - lastPersistTs < PERSIST_THROTTLE_MS)
163
+ return;
164
+ persistSliceState();
165
+ }
166
+ function removeSliceStateFile(basePath) {
167
+ try {
168
+ const p = sliceStateFilePath(basePath);
169
+ if (existsSync(p))
170
+ unlinkSync(p);
171
+ }
172
+ catch {
173
+ /* non-fatal */
174
+ }
175
+ }
176
+ /**
177
+ * Restore slice orchestrator state from disk. Filters dead-PID workers and
178
+ * removes their orphaned worktrees so a clean restart is possible.
179
+ *
180
+ * Returns null if no state file exists or no workers survive.
181
+ */
182
+ export function restoreSliceState(basePath) {
183
+ try {
184
+ const p = sliceStateFilePath(basePath);
185
+ if (!existsSync(p))
186
+ return null;
187
+ const persisted = JSON.parse(readFileSync(p, "utf-8"));
188
+ const survivors = [];
189
+ const dead = [];
190
+ for (const w of persisted.workers) {
191
+ if (w.state === "running" && isRecoveredSliceWorkerAlive(w)) {
192
+ survivors.push(w);
193
+ }
194
+ else if (w.state === "running") {
195
+ dead.push(w);
196
+ }
197
+ else {
198
+ survivors.push(w);
199
+ }
200
+ }
201
+ // Best-effort cleanup of orphaned worktrees from dead workers.
202
+ for (const w of dead) {
203
+ const wtName = `${w.milestoneId}-${w.sliceId}`;
204
+ try {
205
+ removeWorktree(persisted.basePath, wtName, { deleteBranch: true, force: true });
206
+ }
207
+ catch {
208
+ /* worktree may already be gone */
209
+ }
210
+ }
211
+ persisted.workers = survivors;
212
+ if (survivors.length === 0) {
213
+ removeSliceStateFile(basePath);
214
+ return null;
215
+ }
216
+ return persisted;
217
+ }
218
+ catch {
219
+ return null;
220
+ }
221
+ }
27
222
  // ─── Public API ────────────────────────────────────────────────────────────
28
223
  /**
29
224
  * Check whether slice-level parallel is currently active.
225
+ *
226
+ * If in-memory state is unset but a persisted state file exists with at
227
+ * least one live-PID worker, treat as active and rehydrate so a coordinator
228
+ * crash followed by a fresh process is detectable. (Issue #4980 HIGH-8)
30
229
  */
31
- export function isSliceParallelActive() {
32
- return sliceState?.active === true;
230
+ export function isSliceParallelActive(basePath) {
231
+ if (sliceState?.active === true)
232
+ return true;
233
+ if (!basePath)
234
+ return false;
235
+ const restored = restoreSliceState(basePath);
236
+ if (!restored || restored.workers.length === 0)
237
+ return false;
238
+ // Rehydrate in-memory state from disk; processes are detached so we have
239
+ // no ChildProcess handles, only PIDs.
240
+ sliceState = {
241
+ active: restored.active,
242
+ workers: new Map(),
243
+ totalCost: restored.totalCost,
244
+ budgetCeiling: restored.budgetCeiling,
245
+ maxWorkers: restored.maxWorkers,
246
+ startedAt: restored.startedAt,
247
+ basePath: restored.basePath,
248
+ };
249
+ for (const w of restored.workers) {
250
+ sliceState.workers.set(w.sliceId, {
251
+ milestoneId: w.milestoneId,
252
+ sliceId: w.sliceId,
253
+ pid: w.pid,
254
+ process: null,
255
+ worktreePath: w.worktreePath,
256
+ workerToken: w.workerToken ?? "",
257
+ processStartFingerprint: w.processStartFingerprint ?? null,
258
+ startedAt: w.startedAt,
259
+ state: w.state,
260
+ completedUnits: w.completedUnits,
261
+ cost: w.cost,
262
+ });
263
+ }
264
+ return true;
33
265
  }
34
266
  /**
35
267
  * Get current slice orchestrator state (read-only snapshot).
@@ -80,6 +312,8 @@ export async function startSliceParallel(basePath, milestoneId, eligibleSlices,
80
312
  milestoneId,
81
313
  sliceId: slice.id,
82
314
  pid: 0,
315
+ workerToken: createWorkerToken(milestoneId, slice.id),
316
+ processStartFingerprint: null,
83
317
  process: null,
84
318
  worktreePath: wtPath,
85
319
  startedAt: Date.now(),
@@ -95,13 +329,18 @@ export async function startSliceParallel(basePath, milestoneId, eligibleSlices,
95
329
  }
96
330
  else {
97
331
  errors.push({ sid: slice.id, error: "Failed to spawn worker process" });
98
- worker.state = "error";
332
+ sliceState.workers.delete(slice.id);
333
+ try {
334
+ removeWorktree(basePath, wtName, { deleteBranch: true, force: true });
335
+ }
336
+ catch { /* ignore cleanup failures */ }
99
337
  }
100
338
  }
101
339
  catch (err) {
102
340
  errors.push({ sid: slice.id, error: getErrorMessage(err) });
103
341
  // Best-effort cleanup of partially created worktree
104
342
  const wtName = `${milestoneId}-${slice.id}`;
343
+ sliceState.workers.delete(slice.id);
105
344
  try {
106
345
  removeWorktree(basePath, wtName, { deleteBranch: true, force: true });
107
346
  }
@@ -111,6 +350,11 @@ export async function startSliceParallel(basePath, milestoneId, eligibleSlices,
111
350
  // If nothing started, deactivate
112
351
  if (started.length === 0) {
113
352
  sliceState.active = false;
353
+ removeSliceStateFile(basePath);
354
+ }
355
+ else {
356
+ // Persist state for crash recovery (Issue #4980 HIGH-8).
357
+ persistSliceState();
114
358
  }
115
359
  return { started, errors };
116
360
  }
@@ -120,13 +364,17 @@ export async function startSliceParallel(basePath, milestoneId, eligibleSlices,
120
364
  export function stopSliceParallel() {
121
365
  if (!sliceState)
122
366
  return;
367
+ const basePath = sliceState.basePath;
123
368
  for (const worker of sliceState.workers.values()) {
124
- if (worker.process) {
125
- try {
369
+ try {
370
+ if (worker.process) {
126
371
  worker.process.kill("SIGTERM");
127
372
  }
128
- catch { /* already dead */ }
373
+ else if (worker.state === "running" && isRecoveredSliceWorkerAlive(worker)) {
374
+ process.kill(worker.pid, "SIGTERM");
375
+ }
129
376
  }
377
+ catch { /* already dead */ }
130
378
  worker.cleanup?.();
131
379
  worker.cleanup = undefined;
132
380
  worker.process = null;
@@ -139,6 +387,9 @@ export function stopSliceParallel() {
139
387
  catch { /* best-effort cleanup */ }
140
388
  }
141
389
  sliceState.active = false;
390
+ // Clear persisted state — clean shutdown means no recovery on next start.
391
+ // (Issue #4980 HIGH-8)
392
+ removeSliceStateFile(basePath);
142
393
  }
143
394
  /**
144
395
  * Get aggregate cost across all slice workers.
@@ -170,6 +421,7 @@ export function resetSliceOrchestrator() {
170
421
  }
171
422
  }
172
423
  sliceState = null;
424
+ lastPersistTs = 0;
173
425
  }
174
426
  // ─── Internal: Conflict Filtering ──────────────────────────────────────────
175
427
  /**
@@ -254,6 +506,7 @@ function spawnSliceWorker(basePath, milestoneId, sliceId) {
254
506
  GSD_MILESTONE_LOCK: milestoneId,
255
507
  GSD_PROJECT_ROOT: basePath,
256
508
  GSD_PARALLEL_WORKER: "1",
509
+ GSD_SLICE_WORKER_TOKEN: worker.workerToken,
257
510
  },
258
511
  stdio: ["ignore", "pipe", "pipe"],
259
512
  detached: false,
@@ -272,8 +525,17 @@ function spawnSliceWorker(basePath, milestoneId, sliceId) {
272
525
  });
273
526
  worker.process = child;
274
527
  worker.pid = child.pid ?? 0;
528
+ worker.processStartFingerprint = worker.pid > 0
529
+ ? readProcessStartFingerprint(worker.pid)
530
+ : null;
275
531
  if (!child.pid) {
276
532
  worker.process = null;
533
+ worker.pid = 0;
534
+ worker.processStartFingerprint = null;
535
+ try {
536
+ child.kill("SIGTERM");
537
+ }
538
+ catch { /* best-effort */ }
277
539
  return false;
278
540
  }
279
541
  // ── NDJSON stdout monitoring ────────────────────────────────────────
@@ -346,6 +608,9 @@ function spawnSliceWorker(basePath, milestoneId, sliceId) {
346
608
  startedAt: w.startedAt,
347
609
  worktreePath: w.worktreePath,
348
610
  });
611
+ // Persist worker terminal state for crash recovery.
612
+ // (Issue #4980 HIGH-8)
613
+ persistSliceState();
349
614
  });
350
615
  return true;
351
616
  }
@@ -374,6 +639,11 @@ function processSliceWorkerLine(_basePath, _milestoneId, sliceId, line) {
374
639
  sliceState.totalCost += usage.cost;
375
640
  }
376
641
  worker.completedUnits++;
642
+ // Persist cost / progress updates so a crash mid-run preserves them.
643
+ // Throttled (~1/s per process) so high-frequency message_end traffic
644
+ // does not saturate disk I/O. Worker exit / start / stop paths persist
645
+ // unthrottled to guarantee the terminal state lands. (Issue #4980 HIGH-8)
646
+ persistSliceStateThrottled();
377
647
  }
378
648
  }
379
649
  }