gsd-pi 2.82.0-dev.c22380fc3 → 2.82.0-dev.dfbc5f58f

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 (287) hide show
  1. package/README.md +4 -3
  2. package/dist/resources/.managed-resources-content-hash +1 -1
  3. package/dist/resources/GSD-WORKFLOW.md +10 -1
  4. package/dist/resources/extensions/claude-code-cli/partial-builder.js +2 -1
  5. package/dist/resources/extensions/cmux/index.js +5 -0
  6. package/dist/resources/extensions/gsd/auto/infra-errors.js +9 -3
  7. package/dist/resources/extensions/gsd/auto/loop.js +5 -5
  8. package/dist/resources/extensions/gsd/auto/orchestrator.js +11 -0
  9. package/dist/resources/extensions/gsd/auto/phases.js +8 -1
  10. package/dist/resources/extensions/gsd/auto/workflow-memory-pressure.js +12 -0
  11. package/dist/resources/extensions/gsd/auto-dispatch.js +13 -6
  12. package/dist/resources/extensions/gsd/auto-model-selection.js +2 -0
  13. package/dist/resources/extensions/gsd/auto-post-unit.js +233 -127
  14. package/dist/resources/extensions/gsd/auto-prompts.js +2 -2
  15. package/dist/resources/extensions/gsd/auto-recovery.js +31 -1
  16. package/dist/resources/extensions/gsd/auto-start.js +85 -12
  17. package/dist/resources/extensions/gsd/auto-verification.js +28 -22
  18. package/dist/resources/extensions/gsd/auto-worktree.js +111 -1
  19. package/dist/resources/extensions/gsd/auto.js +30 -3
  20. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +4 -1
  21. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +9 -8
  22. package/dist/resources/extensions/gsd/bootstrap/subagent-input.js +21 -9
  23. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +16 -2
  24. package/dist/resources/extensions/gsd/clean-root-preflight.js +170 -8
  25. package/dist/resources/extensions/gsd/commands/catalog.js +4 -1
  26. package/dist/resources/extensions/gsd/commands/handlers/core.js +37 -0
  27. package/dist/resources/extensions/gsd/commands-bootstrap.js +5 -0
  28. package/dist/resources/extensions/gsd/crash-recovery.js +31 -5
  29. package/dist/resources/extensions/gsd/db/unit-dispatches.js +3 -2
  30. package/dist/resources/extensions/gsd/dispatch-guard.js +2 -2
  31. package/dist/resources/extensions/gsd/doctor-runtime-checks.js +28 -11
  32. package/dist/resources/extensions/gsd/doctor.js +2 -28
  33. package/dist/resources/extensions/gsd/export-html.js +27 -425
  34. package/dist/resources/extensions/gsd/git-service.js +39 -1
  35. package/dist/resources/extensions/gsd/gsd-db.js +1 -0
  36. package/dist/resources/extensions/gsd/guided-flow.js +6 -0
  37. package/dist/resources/extensions/gsd/migrate/parsers.js +10 -0
  38. package/dist/resources/extensions/gsd/milestone-actions.js +11 -4
  39. package/dist/resources/extensions/gsd/native-git-bridge.js +48 -12
  40. package/dist/resources/extensions/gsd/post-execution-checks.js +73 -2
  41. package/dist/resources/extensions/gsd/pre-execution-checks.js +28 -1
  42. package/dist/resources/extensions/gsd/prompt-loader.js +1 -1
  43. package/dist/resources/extensions/gsd/prompts/plan-slice.md +3 -3
  44. package/dist/resources/extensions/gsd/prompts/refine-slice.md +1 -1
  45. package/dist/resources/extensions/gsd/state-reconciliation/drift/merge-state.js +6 -1
  46. package/dist/resources/extensions/gsd/status-guards.js +4 -0
  47. package/dist/resources/extensions/gsd/templates/plan.md +8 -5
  48. package/dist/resources/extensions/gsd/templates/task-plan.md +4 -2
  49. package/dist/resources/extensions/gsd/tools/complete-milestone.js +6 -8
  50. package/dist/resources/extensions/gsd/tools/complete-slice.js +6 -8
  51. package/dist/resources/extensions/gsd/tools/plan-milestone.js +7 -1
  52. package/dist/resources/extensions/gsd/tools/plan-slice.js +89 -14
  53. package/dist/resources/extensions/gsd/unit-context-manifest.js +32 -10
  54. package/dist/resources/extensions/gsd/validation.js +23 -1
  55. package/dist/resources/extensions/gsd/verification-gate.js +68 -7
  56. package/dist/resources/extensions/gsd/verification-verdict.js +26 -0
  57. package/dist/resources/extensions/gsd/workflow-projections.js +6 -8
  58. package/dist/resources/extensions/gsd/worktree-lifecycle.js +33 -8
  59. package/dist/resources/extensions/shared/html-shell.js +388 -0
  60. package/dist/resources/extensions/subagent/index.js +448 -78
  61. package/dist/resources/extensions/subagent/launch.js +77 -0
  62. package/dist/resources/extensions/subagent/run-store.js +148 -0
  63. package/dist/resources/extensions/visual-brief/artifact-policy.js +29 -0
  64. package/dist/resources/extensions/visual-brief/extension-manifest.json +8 -0
  65. package/dist/resources/extensions/visual-brief/index.js +5 -0
  66. package/dist/resources/extensions/visual-brief/page-contract.js +124 -0
  67. package/dist/resources/extensions/visual-brief/prompts.js +140 -0
  68. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  69. package/dist/web/standalone/.next/BUILD_ID +1 -1
  70. package/dist/web/standalone/.next/app-path-routes-manifest.json +12 -12
  71. package/dist/web/standalone/.next/build-manifest.json +3 -3
  72. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  73. package/dist/web/standalone/.next/react-loadable-manifest.json +3 -3
  74. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  75. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  76. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  77. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  78. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  79. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  80. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  81. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  82. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  83. package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
  84. package/dist/web/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
  85. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  86. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  87. package/dist/web/standalone/.next/server/app/_not-found.rsc +4 -7
  88. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +4 -7
  89. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  90. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +4 -5
  91. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  92. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  93. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -5
  94. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  95. package/dist/web/standalone/.next/server/app/index.html +1 -1
  96. package/dist/web/standalone/.next/server/app/index.rsc +4 -7
  97. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  98. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +4 -7
  99. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  100. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +4 -5
  101. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -5
  102. package/dist/web/standalone/.next/server/app/page.js +2 -2
  103. package/dist/web/standalone/.next/server/app/page.js.nft.json +1 -1
  104. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  105. package/dist/web/standalone/.next/server/app-paths-manifest.json +12 -12
  106. package/dist/web/standalone/.next/server/chunks/4266.js +2 -0
  107. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  108. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  109. package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
  110. package/dist/web/standalone/.next/server/next-font-manifest.json +1 -1
  111. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  112. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  113. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  114. package/dist/web/standalone/.next/static/chunks/2973.33f26573894b6153.js +2 -0
  115. package/dist/web/standalone/.next/static/chunks/{8359.e059d86b255fce1c.js → 8359.7eb3bb8f8ecf4c01.js} +2 -2
  116. package/dist/web/standalone/.next/static/chunks/app/layout-8c10ec293ae0f1d5.js +1 -0
  117. package/dist/web/standalone/.next/static/chunks/{webpack-de742b64187e13fe.js → webpack-9a4db269f9ed63ad.js} +1 -1
  118. package/dist/web/standalone/.next/static/css/746ee28c929d1880.css +1 -0
  119. package/package.json +4 -4
  120. package/packages/mcp-server/src/workflow-tools.test.ts +1 -1
  121. package/packages/native/tsconfig.json +2 -1
  122. package/packages/native/tsconfig.tsbuildinfo +1 -1
  123. package/packages/pi-ai/dist/providers/openai-codex-responses.d.ts.map +1 -1
  124. package/packages/pi-ai/dist/providers/openai-codex-responses.js +82 -1
  125. package/packages/pi-ai/dist/providers/openai-codex-responses.js.map +1 -1
  126. package/packages/pi-ai/dist/providers/openai-codex-responses.test.d.ts +2 -0
  127. package/packages/pi-ai/dist/providers/openai-codex-responses.test.d.ts.map +1 -0
  128. package/packages/pi-ai/dist/providers/openai-codex-responses.test.js +52 -0
  129. package/packages/pi-ai/dist/providers/openai-codex-responses.test.js.map +1 -0
  130. package/packages/pi-ai/dist/providers/simple-options.d.ts +2 -4
  131. package/packages/pi-ai/dist/providers/simple-options.d.ts.map +1 -1
  132. package/packages/pi-ai/dist/providers/simple-options.js +5 -6
  133. package/packages/pi-ai/dist/providers/simple-options.js.map +1 -1
  134. package/packages/pi-ai/dist/providers/simple-options.test.d.ts +2 -0
  135. package/packages/pi-ai/dist/providers/simple-options.test.d.ts.map +1 -0
  136. package/packages/pi-ai/dist/providers/simple-options.test.js +50 -0
  137. package/packages/pi-ai/dist/providers/simple-options.test.js.map +1 -0
  138. package/packages/pi-ai/src/providers/openai-codex-responses.test.ts +63 -0
  139. package/packages/pi-ai/src/providers/openai-codex-responses.ts +91 -1
  140. package/packages/pi-ai/src/providers/simple-options.test.ts +60 -0
  141. package/packages/pi-ai/src/providers/simple-options.ts +5 -6
  142. package/packages/pi-ai/tsconfig.tsbuildinfo +1 -1
  143. package/packages/pi-coding-agent/dist/core/agent-session-thinking-level.test.d.ts +2 -0
  144. package/packages/pi-coding-agent/dist/core/agent-session-thinking-level.test.d.ts.map +1 -0
  145. package/packages/pi-coding-agent/dist/core/agent-session-thinking-level.test.js +66 -0
  146. package/packages/pi-coding-agent/dist/core/agent-session-thinking-level.test.js.map +1 -0
  147. package/packages/pi-coding-agent/dist/core/agent-session.js +1 -1
  148. package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
  149. package/packages/pi-coding-agent/src/core/agent-session-thinking-level.test.ts +79 -0
  150. package/packages/pi-coding-agent/src/core/agent-session.ts +1 -1
  151. package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
  152. package/src/resources/GSD-WORKFLOW.md +10 -1
  153. package/src/resources/extensions/claude-code-cli/partial-builder.ts +2 -1
  154. package/src/resources/extensions/claude-code-cli/tests/partial-builder.test.ts +19 -2
  155. package/src/resources/extensions/cmux/index.ts +6 -0
  156. package/src/resources/extensions/gsd/auto/contracts.ts +14 -6
  157. package/src/resources/extensions/gsd/auto/infra-errors.ts +9 -3
  158. package/src/resources/extensions/gsd/auto/loop.ts +8 -5
  159. package/src/resources/extensions/gsd/auto/orchestrator.ts +11 -0
  160. package/src/resources/extensions/gsd/auto/phases.ts +7 -1
  161. package/src/resources/extensions/gsd/auto/workflow-memory-pressure.ts +13 -0
  162. package/src/resources/extensions/gsd/auto-dispatch.ts +14 -6
  163. package/src/resources/extensions/gsd/auto-model-selection.ts +2 -1
  164. package/src/resources/extensions/gsd/auto-post-unit.ts +266 -139
  165. package/src/resources/extensions/gsd/auto-prompts.ts +2 -2
  166. package/src/resources/extensions/gsd/auto-recovery.ts +29 -0
  167. package/src/resources/extensions/gsd/auto-start.ts +92 -9
  168. package/src/resources/extensions/gsd/auto-verification.ts +36 -34
  169. package/src/resources/extensions/gsd/auto-worktree.ts +119 -1
  170. package/src/resources/extensions/gsd/auto.ts +32 -3
  171. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +6 -1
  172. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +9 -8
  173. package/src/resources/extensions/gsd/bootstrap/subagent-input.ts +19 -7
  174. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +19 -3
  175. package/src/resources/extensions/gsd/clean-root-preflight.ts +174 -8
  176. package/src/resources/extensions/gsd/commands/catalog.ts +4 -1
  177. package/src/resources/extensions/gsd/commands/handlers/core.ts +40 -0
  178. package/src/resources/extensions/gsd/commands-bootstrap.ts +10 -0
  179. package/src/resources/extensions/gsd/crash-recovery.ts +30 -4
  180. package/src/resources/extensions/gsd/db/unit-dispatches.ts +4 -3
  181. package/src/resources/extensions/gsd/dispatch-guard.ts +2 -2
  182. package/src/resources/extensions/gsd/doctor-runtime-checks.ts +25 -13
  183. package/src/resources/extensions/gsd/doctor.ts +2 -27
  184. package/src/resources/extensions/gsd/export-html.ts +27 -427
  185. package/src/resources/extensions/gsd/git-service.ts +45 -1
  186. package/src/resources/extensions/gsd/gsd-db.ts +3 -0
  187. package/src/resources/extensions/gsd/guided-flow.ts +6 -0
  188. package/src/resources/extensions/gsd/migrate/parsers.ts +11 -0
  189. package/src/resources/extensions/gsd/milestone-actions.ts +10 -4
  190. package/src/resources/extensions/gsd/native-git-bridge.ts +54 -12
  191. package/src/resources/extensions/gsd/post-execution-checks.ts +87 -2
  192. package/src/resources/extensions/gsd/pre-execution-checks.ts +32 -1
  193. package/src/resources/extensions/gsd/prompt-loader.ts +1 -1
  194. package/src/resources/extensions/gsd/prompts/plan-slice.md +3 -3
  195. package/src/resources/extensions/gsd/prompts/refine-slice.md +1 -1
  196. package/src/resources/extensions/gsd/state-reconciliation/drift/merge-state.ts +8 -1
  197. package/src/resources/extensions/gsd/status-guards.ts +5 -0
  198. package/src/resources/extensions/gsd/templates/plan.md +8 -5
  199. package/src/resources/extensions/gsd/templates/task-plan.md +4 -2
  200. package/src/resources/extensions/gsd/tests/auto-deterministic-error-classification-4973.test.ts +116 -0
  201. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +54 -0
  202. package/src/resources/extensions/gsd/tests/auto-orchestrator.test.ts +80 -1
  203. package/src/resources/extensions/gsd/tests/auto-paused-ui-cleanup.test.ts +6 -6
  204. package/src/resources/extensions/gsd/tests/auto-post-unit-step-message.test.ts +12 -1
  205. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +15 -1
  206. package/src/resources/extensions/gsd/tests/auto-start-orphan-bootstrap.test.ts +1 -0
  207. package/src/resources/extensions/gsd/tests/auto-worktree-registry.test.ts +69 -1
  208. package/src/resources/extensions/gsd/tests/brief-command.test.ts +89 -0
  209. package/src/resources/extensions/gsd/tests/clean-root-preflight.test.ts +107 -2
  210. package/src/resources/extensions/gsd/tests/closeout-git-deferral.test.ts +16 -0
  211. package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +4 -1
  212. package/src/resources/extensions/gsd/tests/complete-task.test.ts +3 -1
  213. package/src/resources/extensions/gsd/tests/crash-recovery-via-db.test.ts +43 -2
  214. package/src/resources/extensions/gsd/tests/deep-project-auto-loop.test.ts +59 -2
  215. package/src/resources/extensions/gsd/tests/dispatch-complete-milestone-guard.test.ts +39 -0
  216. package/src/resources/extensions/gsd/tests/dispatch-guard.test.ts +27 -0
  217. package/src/resources/extensions/gsd/tests/evidence-cross-ref.test.ts +38 -0
  218. package/src/resources/extensions/gsd/tests/export-html-enhancements.test.ts +8 -0
  219. package/src/resources/extensions/gsd/tests/guided-flow.test.ts +21 -0
  220. package/src/resources/extensions/gsd/tests/hook-model-resolution.test.ts +5 -0
  221. package/src/resources/extensions/gsd/tests/infra-error.test.ts +2 -2
  222. package/src/resources/extensions/gsd/tests/infra-errors-cooldown.test.ts +9 -0
  223. package/src/resources/extensions/gsd/tests/integration/doctor-runtime.test.ts +20 -0
  224. package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +103 -1
  225. package/src/resources/extensions/gsd/tests/integration/state-machine-runtime-failures.test.ts +6 -1
  226. package/src/resources/extensions/gsd/tests/migrate-validator-parsers.test.ts +24 -1
  227. package/src/resources/extensions/gsd/tests/native-git-bridge-exec-fallback.test.ts +63 -2
  228. package/src/resources/extensions/gsd/tests/orphaned-worktree-audit.test.ts +121 -1
  229. package/src/resources/extensions/gsd/tests/park-db-sync.test.ts +55 -1
  230. package/src/resources/extensions/gsd/tests/plan-milestone.test.ts +26 -0
  231. package/src/resources/extensions/gsd/tests/plan-slice.test.ts +225 -1
  232. package/src/resources/extensions/gsd/tests/plan-task.test.ts +17 -0
  233. package/src/resources/extensions/gsd/tests/post-exec-retry-bypass.test.ts +79 -1
  234. package/src/resources/extensions/gsd/tests/post-execution-checks.test.ts +86 -0
  235. package/src/resources/extensions/gsd/tests/post-unit-git-failure.test.ts +1 -1
  236. package/src/resources/extensions/gsd/tests/pre-execution-checks.test.ts +53 -0
  237. package/src/resources/extensions/gsd/tests/prompt-loader.test.ts +23 -0
  238. package/src/resources/extensions/gsd/tests/remediation-completion-guard.test.ts +46 -2
  239. package/src/resources/extensions/gsd/tests/session-switch-abort-misclassification.test.ts +10 -0
  240. package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +31 -1
  241. package/src/resources/extensions/gsd/tests/stuck-state-via-db.test.ts +64 -1
  242. package/src/resources/extensions/gsd/tests/summary-render-parity.test.ts +7 -3
  243. package/src/resources/extensions/gsd/tests/unit-context-manifest.test.ts +86 -7
  244. package/src/resources/extensions/gsd/tests/verification-gate.test.ts +110 -1
  245. package/src/resources/extensions/gsd/tests/verification-verdict.test.ts +78 -0
  246. package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +1 -1
  247. package/src/resources/extensions/gsd/tests/workflow-memory-pressure.test.ts +21 -1
  248. package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +1 -1
  249. package/src/resources/extensions/gsd/tests/worktree-git-pathspec.test.ts +39 -0
  250. package/src/resources/extensions/gsd/tests/worktree-journal-events.test.ts +64 -12
  251. package/src/resources/extensions/gsd/tests/write-gate-planning-unit.test.ts +54 -0
  252. package/src/resources/extensions/gsd/tools/complete-milestone.ts +8 -10
  253. package/src/resources/extensions/gsd/tools/complete-slice.ts +6 -8
  254. package/src/resources/extensions/gsd/tools/plan-milestone.ts +5 -1
  255. package/src/resources/extensions/gsd/tools/plan-slice.ts +98 -12
  256. package/src/resources/extensions/gsd/types.ts +1 -1
  257. package/src/resources/extensions/gsd/unit-context-manifest.ts +47 -11
  258. package/src/resources/extensions/gsd/validation.ts +23 -1
  259. package/src/resources/extensions/gsd/verification-gate.ts +78 -6
  260. package/src/resources/extensions/gsd/verification-verdict.ts +47 -0
  261. package/src/resources/extensions/gsd/workflow-projections.ts +6 -8
  262. package/src/resources/extensions/gsd/worktree-lifecycle.ts +41 -8
  263. package/src/resources/extensions/shared/html-shell.ts +412 -0
  264. package/src/resources/extensions/subagent/index.ts +567 -103
  265. package/src/resources/extensions/subagent/launch.ts +131 -0
  266. package/src/resources/extensions/subagent/run-store.ts +218 -0
  267. package/src/resources/extensions/subagent/tests/launch.test.ts +115 -0
  268. package/src/resources/extensions/subagent/tests/run-store.test.ts +111 -0
  269. package/src/resources/extensions/visual-brief/artifact-policy.ts +41 -0
  270. package/src/resources/extensions/visual-brief/extension-manifest.json +8 -0
  271. package/src/resources/extensions/visual-brief/index.ts +8 -0
  272. package/src/resources/extensions/visual-brief/page-contract.ts +136 -0
  273. package/src/resources/extensions/visual-brief/prompts.ts +183 -0
  274. package/src/resources/extensions/visual-brief/tests/visual-brief.test.ts +212 -0
  275. package/dist/web/standalone/.next/server/chunks/5822.js +0 -2
  276. package/dist/web/standalone/.next/static/chunks/2556.0527fea66e123b7f.js +0 -1
  277. package/dist/web/standalone/.next/static/chunks/app/layout-a16c7a7ecdf0c2cf.js +0 -1
  278. package/dist/web/standalone/.next/static/css/54ec2745c1da488b.css +0 -1
  279. package/dist/web/standalone/.next/static/css/de70bee13400563f.css +0 -1
  280. package/dist/web/standalone/.next/static/media/4cf2300e9c8272f7-s.p.woff2 +0 -0
  281. package/dist/web/standalone/.next/static/media/747892c23ea88013-s.woff2 +0 -0
  282. package/dist/web/standalone/.next/static/media/8d697b304b401681-s.woff2 +0 -0
  283. package/dist/web/standalone/.next/static/media/93f479601ee12b01-s.p.woff2 +0 -0
  284. package/dist/web/standalone/.next/static/media/9610d9e46709d722-s.woff2 +0 -0
  285. package/dist/web/standalone/.next/static/media/ba015fad6dcf6784-s.woff2 +0 -0
  286. /package/dist/web/standalone/.next/static/{Wop3A7KRGyR06H3rla_1- → q0WYuDVbHeFFYbdd-fei2}/_buildManifest.js +0 -0
  287. /package/dist/web/standalone/.next/static/{Wop3A7KRGyR06H3rla_1- → q0WYuDVbHeFFYbdd-fei2}/_ssgManifest.js +0 -0
@@ -1,6 +1,6 @@
1
1
  import test from "node:test";
2
2
  import assert from "node:assert/strict";
3
- import { existsSync, mkdirSync, readFileSync, realpathSync, rmSync, writeFileSync } from "node:fs";
3
+ import { chmodSync, existsSync, mkdirSync, readFileSync, realpathSync, rmSync, writeFileSync } from "node:fs";
4
4
  import { execFileSync } from "node:child_process";
5
5
  import { tmpdir } from "node:os";
6
6
  import { join } from "node:path";
@@ -10,7 +10,7 @@ import { runDispatch, runPreDispatch } from "../auto/phases.ts";
10
10
  import { AutoSession } from "../auto/session.ts";
11
11
  import { resolveUnitSupervisionTimeouts } from "../auto-timers.ts";
12
12
  import { bootstrapAutoSession } from "../auto-start.ts";
13
- import { postUnitPreVerification } from "../auto-post-unit.ts";
13
+ import { postUnitPostVerification, postUnitPreVerification } from "../auto-post-unit.ts";
14
14
  import { resolveDispatch, setResearchProjectPromptBuilderForTest } from "../auto-dispatch.ts";
15
15
  import { resolveExpectedArtifactPath, verifyExpectedArtifact, writeBlockerPlaceholder } from "../auto-recovery.ts";
16
16
  import { finalizeProjectResearchTimeout } from "../project-research-policy.ts";
@@ -272,6 +272,7 @@ test("deep project setup: bootstrap can start auto-mode without an active milest
272
272
  {
273
273
  shouldUseWorktreeIsolation: () => false,
274
274
  registerSigtermHandler: () => {},
275
+ registerAutoWorkerForSession: () => {},
275
276
  lockBase: () => base,
276
277
  buildLifecycle: () => ({
277
278
  adoptSessionRoot: (sessionBase: string, originalBase?: string) => {
@@ -386,6 +387,7 @@ test("deep project setup: bootstrap continues queued M002 without milestone cont
386
387
  {
387
388
  shouldUseWorktreeIsolation: () => false,
388
389
  registerSigtermHandler: () => {},
390
+ registerAutoWorkerForSession: () => {},
389
391
  lockBase: () => base,
390
392
  buildLifecycle: () => ({
391
393
  adoptSessionRoot: (sessionBase: string, originalBase?: string) => {
@@ -1587,6 +1589,61 @@ test("deep project setup: discuss-milestone question failure pauses instead of a
1587
1589
  }
1588
1590
  });
1589
1591
 
1592
+ test("verified task git closeout failure retries and continues auto-mode", async () => {
1593
+ const base = makeBase();
1594
+ try {
1595
+ execFileSync("git", ["init"], { cwd: base, stdio: "ignore" });
1596
+ execFileSync("git", ["config", "user.email", "test@example.com"], { cwd: base, stdio: "ignore" });
1597
+ execFileSync("git", ["config", "user.name", "Test User"], { cwd: base, stdio: "ignore" });
1598
+ const hookPath = join(base, ".git", "hooks", "pre-commit");
1599
+ writeFileSync(
1600
+ hookPath,
1601
+ [
1602
+ "#!/bin/sh",
1603
+ "count_file=.git/pre-commit-count",
1604
+ "count=0",
1605
+ "if [ -f \"$count_file\" ]; then count=$(cat \"$count_file\"); fi",
1606
+ "count=$((count + 1))",
1607
+ "printf \"%s\" \"$count\" > \"$count_file\"",
1608
+ "echo blocked by test hook >&2",
1609
+ "exit 1",
1610
+ ].join("\n"),
1611
+ );
1612
+ chmodSync(hookPath, 0o755);
1613
+ writeFileSync(join(base, "work.txt"), "changed\n");
1614
+
1615
+ const s = new AutoSession();
1616
+ s.active = true;
1617
+ s.basePath = base;
1618
+ s.originalBasePath = base;
1619
+ s.currentUnit = { type: "execute-task", id: "M001/S01/T01", startedAt: Date.now() };
1620
+
1621
+ let pauseCalled = false;
1622
+ const notifications: Array<{ message: string; severity?: string }> = [];
1623
+ const result = await postUnitPostVerification({
1624
+ s,
1625
+ ctx: { ui: { notify: (message: string, severity?: string) => notifications.push({ message, severity }) } } as any,
1626
+ pi: {} as any,
1627
+ buildSnapshotOpts: () => ({}) as any,
1628
+ lockBase: () => base,
1629
+ stopAuto: async () => {},
1630
+ pauseAuto: async () => { pauseCalled = true; },
1631
+ updateProgressWidget: () => {},
1632
+ });
1633
+
1634
+ assert.equal(result, "continue");
1635
+ assert.equal(pauseCalled, false);
1636
+ assert.equal(s.lastGitActionStatus, "failed");
1637
+ assert.equal(readFileSync(join(base, ".git", "pre-commit-count"), "utf-8"), "3");
1638
+ assert.ok(
1639
+ notifications.some((entry) => entry.severity === "warning" && entry.message.includes("Git commit failed")),
1640
+ "verified task git closeout failure should warn instead of stopping auto-mode",
1641
+ );
1642
+ } finally {
1643
+ rmSync(base, { recursive: true, force: true });
1644
+ }
1645
+ });
1646
+
1590
1647
  test("deep project setup: approval wait wins over deterministic write-gate placeholder", async () => {
1591
1648
  const base = makeBase();
1592
1649
  try {
@@ -118,3 +118,42 @@ describe("complete phase dispatch guard (#5683)", () => {
118
118
  assert.equal(result?.reason, "All milestones complete.");
119
119
  });
120
120
  });
121
+
122
+ describe("complete milestone context recovery guard (#5831)", () => {
123
+ let base = "";
124
+ const executionEntryRule = DISPATCH_RULES.find(
125
+ (candidate) => candidate.name === "execution-entry phase (no context) → discuss-milestone",
126
+ );
127
+ const prePlanningRule = DISPATCH_RULES.find(
128
+ (candidate) => candidate.name === "pre-planning (no context) → discuss-milestone",
129
+ );
130
+ assert.ok(executionEntryRule, "execution-entry missing-context rule should exist");
131
+ assert.ok(prePlanningRule, "pre-planning missing-context rule should exist");
132
+
133
+ afterEach(() => {
134
+ if (base) rmSync(base, { recursive: true, force: true });
135
+ base = "";
136
+ });
137
+
138
+ test("does not discuss a complete execution-entry milestone with no CONTEXT file", async () => {
139
+ base = makeBase();
140
+ const ctx = buildDispatchCtx(base);
141
+ ctx.state.registry = [{ id: "M001", title: "Milestone One", status: "complete" }];
142
+ ctx.state.phase = "completing-milestone";
143
+
144
+ const result = await executionEntryRule.match(ctx);
145
+
146
+ assert.equal(result, null);
147
+ });
148
+
149
+ test("does not discuss a complete pre-planning milestone with no CONTEXT file", async () => {
150
+ base = makeBase();
151
+ const ctx = buildDispatchCtx(base);
152
+ ctx.state.registry = [{ id: "M001", title: "Milestone One", status: "complete" }];
153
+ ctx.state.phase = "pre-planning";
154
+
155
+ const result = await prePlanningRule.match(ctx);
156
+
157
+ assert.equal(result, null);
158
+ });
159
+ });
@@ -47,6 +47,33 @@ test("dispatch guard blocks when prior milestone has incomplete slices", (t) =>
47
47
  );
48
48
  });
49
49
 
50
+ test("dispatch guard skips prior DB parked or deferred milestones without marker files", (t) => {
51
+ const repo = setupRepo();
52
+ t.after(() => teardownRepo(repo));
53
+
54
+ mkdirSync(join(repo, ".gsd", "milestones", "M001"), { recursive: true });
55
+ mkdirSync(join(repo, ".gsd", "milestones", "M002"), { recursive: true });
56
+ mkdirSync(join(repo, ".gsd", "milestones", "M003"), { recursive: true });
57
+
58
+ insertMilestone({ id: "M001", title: "Parked", status: "parked" });
59
+ insertSlice({ id: "S01", milestoneId: "M001", title: "Incomplete", status: "pending", depends: [], sequence: 1 });
60
+
61
+ insertMilestone({ id: "M002", title: "Deferred", status: "deferred" });
62
+ insertSlice({ id: "S01", milestoneId: "M002", title: "Incomplete", status: "pending", depends: [], sequence: 1 });
63
+
64
+ insertMilestone({ id: "M003", title: "Current", status: "active" });
65
+ insertSlice({ id: "S01", milestoneId: "M003", title: "First", status: "pending", depends: [], sequence: 1 });
66
+
67
+ writeFileSync(join(repo, ".gsd", "milestones", "M001", "M001-ROADMAP.md"), "# M001\n");
68
+ writeFileSync(join(repo, ".gsd", "milestones", "M002", "M002-ROADMAP.md"), "# M002\n");
69
+ writeFileSync(join(repo, ".gsd", "milestones", "M003", "M003-ROADMAP.md"), "# M003\n");
70
+
71
+ assert.equal(
72
+ getPriorSliceCompletionBlocker(repo, "main", "plan-slice", "M003/S01"),
73
+ null,
74
+ );
75
+ });
76
+
50
77
  test("dispatch guard blocks later slice in same milestone when earlier incomplete", (t) => {
51
78
  const repo = setupRepo();
52
79
  t.after(() => teardownRepo(repo));
@@ -0,0 +1,38 @@
1
+ // Project/App: GSD-2
2
+ // File Purpose: Tests for verification evidence cross-reference mismatch policy.
3
+
4
+ import test from "node:test";
5
+ import assert from "node:assert/strict";
6
+
7
+ import { crossReferenceEvidence } from "../safety/evidence-cross-ref.ts";
8
+ import type { EvidenceEntry } from "../safety/evidence-collector.ts";
9
+
10
+ test("claims of passing verification become errors when recorded bash evidence failed", () => {
11
+ const mismatches = crossReferenceEvidence(
12
+ [{ command: "npm test", exitCode: 0, verdict: "passed" }],
13
+ [
14
+ {
15
+ kind: "bash",
16
+ toolCallId: "call-1",
17
+ command: "npm test",
18
+ exitCode: 1,
19
+ outputSnippet: "failed",
20
+ timestamp: Date.now(),
21
+ },
22
+ ] as EvidenceEntry[],
23
+ );
24
+
25
+ assert.equal(mismatches.length, 1);
26
+ assert.equal(mismatches[0].severity, "error");
27
+ assert.match(mismatches[0].reason, /Claimed exitCode=0/);
28
+ });
29
+
30
+ test("missing recorded bash evidence remains a warning", () => {
31
+ const mismatches = crossReferenceEvidence(
32
+ [{ command: "npm test", exitCode: 0, verdict: "passed" }],
33
+ [],
34
+ );
35
+
36
+ assert.equal(mismatches.length, 1);
37
+ assert.equal(mismatches[0].severity, "warning");
38
+ });
@@ -141,6 +141,14 @@ test("Feature 1: executive summary paragraph is rendered", () => {
141
141
  assert.ok(html.includes("$2.50 spent"), "should contain cost");
142
142
  });
143
143
 
144
+ test("report uses the shared GSD HTML shell", () => {
145
+ const html = generateHtmlReport(mockData(), mockOpts());
146
+ assert.ok(html.includes('<span class="logo">GSD</span>'), "should render shared shell logo");
147
+ assert.ok(html.includes('<span class="kind-chip">Report</span>'), "should render report kind chip");
148
+ assert.ok(html.includes('<nav class="toc" aria-label="Report sections">'), "should render shared shell TOC");
149
+ assert.ok(html.includes('<main>'), "should render content inside shared shell main");
150
+ });
151
+
144
152
  test("Feature 1: executive summary includes budget context when set", () => {
145
153
  const data = mockData({ health: { ...mockData().health, budgetCeiling: 10.00 } });
146
154
  const html = generateHtmlReport(data, mockOpts());
@@ -0,0 +1,21 @@
1
+ import test from "node:test";
2
+ import assert from "node:assert/strict";
3
+ import { readFileSync } from "node:fs";
4
+ import { dirname, join } from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+
7
+ const __dirname = dirname(fileURLToPath(import.meta.url));
8
+
9
+ test("guided milestone discussion callsites pass workingDirectory to loadPrompt", () => {
10
+ const source = readFileSync(join(__dirname, "..", "guided-flow.ts"), "utf-8");
11
+ const calls = [...source.matchAll(/loadPrompt\("guided-discuss-milestone",\s*\{([\s\S]*?)\}\)/g)];
12
+
13
+ assert.equal(calls.length, 6, "all guided-flow guided-discuss-milestone callsites should be covered");
14
+ for (const call of calls) {
15
+ assert.match(
16
+ call[1] ?? "",
17
+ /\bworkingDirectory:\s*basePath\b/,
18
+ "guided-discuss-milestone prompts need workingDirectory so template validation does not crash",
19
+ );
20
+ }
21
+ });
@@ -92,6 +92,11 @@ test("resolveModelId: returns undefined for unknown model", () => {
92
92
  assert.equal(match, undefined);
93
93
  });
94
94
 
95
+ test("resolveModelId: returns undefined for missing model ID", () => {
96
+ const match = resolveModelId(undefined, AVAILABLE_MODELS, "anthropic");
97
+ assert.equal(match, undefined);
98
+ });
99
+
95
100
  test("resolveModelId: returns undefined for unknown provider/model combo", () => {
96
101
  const match = resolveModelId("fakeprovider/fake-model", AVAILABLE_MODELS, undefined);
97
102
  assert.equal(match, undefined);
@@ -9,11 +9,11 @@ import { isInfrastructureError, INFRA_ERROR_CODES } from "../auto/infra-errors.j
9
9
  test("INFRA_ERROR_CODES contains the expected codes", () => {
10
10
  for (const code of [
11
11
  "ENOSPC", "ENOMEM", "EROFS", "EDQUOT", "EMFILE", "ENFILE",
12
- "EAGAIN", "ECONNREFUSED", "ENOTFOUND", "ENETUNREACH",
12
+ "EAGAIN", "ENOBUFS", "ECONNREFUSED", "ENOTFOUND", "ENETUNREACH",
13
13
  ]) {
14
14
  assert.ok(INFRA_ERROR_CODES.has(code), `missing ${code}`);
15
15
  }
16
- assert.equal(INFRA_ERROR_CODES.size, 10, "unexpected extra codes");
16
+ assert.equal(INFRA_ERROR_CODES.size, 11, "unexpected extra codes");
17
17
  });
18
18
 
19
19
  // ── isInfrastructureError: code property detection ───────────────────────────
@@ -5,6 +5,8 @@ import test, { describe } from "node:test";
5
5
  import assert from "node:assert/strict";
6
6
 
7
7
  import {
8
+ INFRA_ERROR_CODES,
9
+ isInfrastructureError,
8
10
  isTransientCooldownError,
9
11
  getCooldownRetryAfterMs,
10
12
  MAX_COOLDOWN_RETRIES,
@@ -13,6 +15,13 @@ import {
13
15
 
14
16
  // ─── Constants ────────────────────────────────────────────────────────────────
15
17
 
18
+ describe("infra error classification", () => {
19
+ test("ENOBUFS is treated as infrastructure exhaustion", () => {
20
+ assert.equal(INFRA_ERROR_CODES.has("ENOBUFS"), true);
21
+ assert.equal(isInfrastructureError(Object.assign(new Error("spawnSync git ENOBUFS"), { code: "ENOBUFS" })), "ENOBUFS");
22
+ });
23
+ });
24
+
16
25
  describe("infra-errors cooldown constants", () => {
17
26
  test("COOLDOWN_FALLBACK_WAIT_MS is a positive number greater than the 30s rate-limit backoff", () => {
18
27
  assert.ok(typeof COOLDOWN_FALLBACK_WAIT_MS === "number");
@@ -446,6 +446,26 @@ node_modules/
446
446
  const strandedIssues = detect.issues.filter(i => i.code === "stranded_lock_directory");
447
447
  assert.deepStrictEqual(strandedIssues.length, 0, "live lock holder: stranded_lock_directory NOT detected");
448
448
  });
449
+
450
+ test('stranded_lock_directory still reports when worker lookup fails', async () => {
451
+ const dir = createMinimalProject();
452
+ cleanups.push(dir);
453
+
454
+ const lockDir = join(dir, ".gsd.lock");
455
+ mkdirSync(lockDir, { recursive: true });
456
+ const { openDatabase, _getAdapter, closeDatabase } = await import("../../gsd-db.ts");
457
+ openDatabase(join(dir, ".gsd", "gsd.db"));
458
+ const db = _getAdapter()!;
459
+ db.exec("DROP TABLE workers");
460
+
461
+ try {
462
+ const detect = await runGSDDoctor(dir);
463
+ const strandedIssues = detect.issues.filter(i => i.code === "stranded_lock_directory");
464
+ assert.ok(strandedIssues.length > 0, "reports stranded lock directory even when active worker lookup fails");
465
+ } finally {
466
+ closeDatabase();
467
+ }
468
+ });
449
469
  } else {
450
470
  }
451
471
 
@@ -5,7 +5,7 @@ import assert from 'node:assert/strict';
5
5
  import { mkdtempSync, mkdirSync, writeFileSync, rmSync, existsSync, symlinkSync, readFileSync } from "node:fs";
6
6
  import { join, dirname } from "node:path";
7
7
  import { tmpdir } from "node:os";
8
- import { execSync } from "node:child_process";
8
+ import { execFileSync, execSync } from "node:child_process";
9
9
 
10
10
  import {
11
11
  inferCommitType,
@@ -371,6 +371,18 @@ describe('git-service', async () => {
371
371
  return dir;
372
372
  }
373
373
 
374
+ function gitRun(args: string[], cwd: string): string {
375
+ return execFileSync("git", args, {
376
+ cwd,
377
+ stdio: ["ignore", "pipe", "pipe"],
378
+ encoding: "utf-8",
379
+ env: {
380
+ ...process.env,
381
+ GIT_ALLOW_PROTOCOL: "file",
382
+ },
383
+ }).trim();
384
+ }
385
+
374
386
  // ─── GitServiceImpl: smart staging ─────────────────────────────────────
375
387
 
376
388
  test('GitServiceImpl: smart staging', () => {
@@ -413,6 +425,96 @@ describe('git-service', async () => {
413
425
  rmSync(repo, { recursive: true, force: true });
414
426
  });
415
427
 
428
+ test('GitServiceImpl: task autoCommit skips keyFiles inside submodules', () => {
429
+ const repo = initTempRepo();
430
+ const subSrc = mkdtempSync(join(tmpdir(), "gsd-git-submodule-src-"));
431
+
432
+ try {
433
+ gitRun(["init", "-b", "main"], subSrc);
434
+ gitRun(["config", "user.name", "Pi Test"], subSrc);
435
+ gitRun(["config", "user.email", "pi@example.com"], subSrc);
436
+ createFile(subSrc, "tracked.txt", "initial\n");
437
+ gitRun(["add", "-A"], subSrc);
438
+ gitRun(["commit", "-m", "init submodule"], subSrc);
439
+
440
+ gitRun(["-c", "protocol.file.allow=always", "submodule", "add", `file://${subSrc}`, "sub"], repo);
441
+ gitRun(["commit", "-m", "add submodule"], repo);
442
+
443
+ createFile(repo, "sub/copied.txt", "copied from source\n");
444
+ createFile(repo, "src/feature.ts", "export const feature = true;\n");
445
+ createFile(repo, "src/unrelated.ts", "export const unrelated = true;\n");
446
+
447
+ const svc = new GitServiceImpl(repo);
448
+ const taskContext: TaskCommitContext = {
449
+ taskId: "S01/T01",
450
+ taskDisplayId: "T01",
451
+ taskTitle: "fix submodule staging",
452
+ milestoneId: "M001",
453
+ milestoneTitle: "Submodule auto commit",
454
+ sliceId: "S01",
455
+ sliceTitle: "Commit scoped files",
456
+ oneLiner: "Fixed auto commit when key files include submodule paths",
457
+ keyFiles: ["sub/copied.txt", "src/feature.ts"],
458
+ };
459
+
460
+ const result = svc.autoCommit("execute-task", "M001/S01/T01", [], taskContext);
461
+
462
+ assert.ok(result !== null, "autoCommit should commit non-submodule changes");
463
+ const committed = gitRun(["show", "--name-only", "--format=", "HEAD"], repo);
464
+ assert.ok(committed.includes("src/feature.ts"), "non-submodule keyFile is committed");
465
+ assert.ok(!committed.includes("sub/copied.txt"), "submodule inner keyFile is not pathspec-staged");
466
+ assert.ok(!committed.includes("src/unrelated.ts"), "scoped staging does not fall back to smartStage");
467
+ } finally {
468
+ rmSync(repo, { recursive: true, force: true });
469
+ rmSync(subSrc, { recursive: true, force: true });
470
+ }
471
+ });
472
+
473
+ test('GitServiceImpl: all keyFiles inside submodules falls back to smartStage', () => {
474
+ const repo = initTempRepo();
475
+ const subSrc = mkdtempSync(join(tmpdir(), "gsd-git-all-submodule-src-"));
476
+
477
+ try {
478
+ gitRun(["init", "-b", "main"], subSrc);
479
+ gitRun(["config", "user.name", "Pi Test"], subSrc);
480
+ gitRun(["config", "user.email", "pi@example.com"], subSrc);
481
+ createFile(subSrc, "tracked.txt", "initial\n");
482
+ gitRun(["add", "-A"], subSrc);
483
+ gitRun(["commit", "-m", "init submodule"], subSrc);
484
+
485
+ gitRun(["-c", "protocol.file.allow=always", "submodule", "add", `file://${subSrc}`, "sub"], repo);
486
+ gitRun(["commit", "-m", "add submodule"], repo);
487
+
488
+ createFile(repo, "sub/file1.txt", "inside submodule\n");
489
+ createFile(repo, "sub/file2.txt", "also inside\n");
490
+ createFile(repo, "src/real.ts", "export const real = true;\n");
491
+
492
+ const svc = new GitServiceImpl(repo);
493
+ const taskContext: TaskCommitContext = {
494
+ taskId: "S01/T02",
495
+ taskDisplayId: "T02",
496
+ taskTitle: "all keyFiles inside submodule",
497
+ milestoneId: "M001",
498
+ milestoneTitle: "Submodule auto commit",
499
+ sliceId: "S01",
500
+ sliceTitle: "Commit scoped files",
501
+ oneLiner: "Fell back when all key files are inside submodules",
502
+ keyFiles: ["sub", "sub/file1.txt", "sub/file2.txt"],
503
+ };
504
+
505
+ const result = svc.autoCommit("execute-task", "M001/S01/T02", [], taskContext);
506
+
507
+ assert.ok(result !== null, "autoCommit falls back to smartStage when all keyFiles are filtered");
508
+ const committed = gitRun(["show", "--name-only", "--format=", "HEAD"], repo);
509
+ assert.ok(!committed.includes("sub/file1.txt"), "submodule keyFile is not committed");
510
+ assert.ok(!committed.includes("sub/file2.txt"), "submodule keyFile is not committed");
511
+ assert.ok(committed.includes("src/real.ts"), "smartStage fallback commits other dirty files");
512
+ } finally {
513
+ rmSync(repo, { recursive: true, force: true });
514
+ rmSync(subSrc, { recursive: true, force: true });
515
+ }
516
+ });
517
+
416
518
  // ─── GitServiceImpl: smart staging excludes tracked runtime files ──────
417
519
 
418
520
  test('GitServiceImpl: smart staging excludes tracked runtime files', () => {
@@ -195,6 +195,11 @@ describe("infrastructure error detection", () => {
195
195
  assert.equal(isInfrastructureError(err), "EAGAIN");
196
196
  });
197
197
 
198
+ test("ENOBUFS (no buffer space available) is detected", () => {
199
+ const err = Object.assign(new Error("spawnSync git ENOBUFS"), { code: "ENOBUFS" });
200
+ assert.equal(isInfrastructureError(err), "ENOBUFS");
201
+ });
202
+
198
203
  test("SQLite WAL corruption is detected via message scan", () => {
199
204
  const err = new Error("database disk image is malformed");
200
205
  assert.equal(isInfrastructureError(err), "SQLITE_CORRUPT");
@@ -223,7 +228,7 @@ describe("infrastructure error detection", () => {
223
228
  test("all INFRA_ERROR_CODES are covered", () => {
224
229
  const expectedCodes = [
225
230
  "ENOSPC", "ENOMEM", "EROFS", "EDQUOT", "EMFILE",
226
- "ENFILE", "EAGAIN", "ECONNREFUSED", "ENOTFOUND", "ENETUNREACH",
231
+ "ENFILE", "EAGAIN", "ENOBUFS", "ECONNREFUSED", "ENOTFOUND", "ENETUNREACH",
227
232
  ];
228
233
  for (const code of expectedCodes) {
229
234
  assert.ok(INFRA_ERROR_CODES.has(code), `${code} should be in INFRA_ERROR_CODES`);
@@ -41,6 +41,15 @@ const SAMPLE_ROADMAP = `# Project Roadmap
41
41
  - [ ] 31 — Notifications
42
42
  `;
43
43
 
44
+ const SAMPLE_VERSION_PREFIX_ROADMAP = `# Project Roadmap
45
+
46
+ ## Phases
47
+
48
+ - ✅ **v1.0 MVP** — Phases 1-6 (shipped 2026-02-24)
49
+ - ✅ **v1.1 Onboarding** — Phases 7-9 (shipped 2026-03-01)
50
+ - 🚧 **v1.8 Production** — Phases 44-53
51
+ `;
52
+
44
53
  const SAMPLE_PROJECT = `# My Project
45
54
 
46
55
  A sample project for testing the migration parser.
@@ -246,6 +255,21 @@ test('parseOldRoadmap: flat format', () => {
246
255
  assert.deepStrictEqual(roadmap.phases[1].done, false, 'flat roadmap: second phase not done');
247
256
  });
248
257
 
258
+ test('parseOldRoadmap: emoji version-prefix phase ranges', () => {
259
+ const roadmap = parseOldRoadmap(SAMPLE_VERSION_PREFIX_ROADMAP);
260
+ assert.deepStrictEqual(roadmap.milestones.length, 0, 'version roadmap: no milestone sections');
261
+ assert.deepStrictEqual(roadmap.phases.length, 3, 'version roadmap: 3 phase ranges');
262
+ assert.deepStrictEqual(roadmap.phases[0].number, 1, 'version roadmap: first range starts at phase 1');
263
+ assert.deepStrictEqual(roadmap.phases[0].title, 'MVP', 'version roadmap: first title');
264
+ assert.deepStrictEqual(roadmap.phases[0].done, true, 'version roadmap: first range done');
265
+ assert.deepStrictEqual(roadmap.phases[1].number, 7, 'version roadmap: second range starts at phase 7');
266
+ assert.deepStrictEqual(roadmap.phases[1].title, 'Onboarding', 'version roadmap: second title');
267
+ assert.deepStrictEqual(roadmap.phases[1].done, true, 'version roadmap: second range done');
268
+ assert.deepStrictEqual(roadmap.phases[2].number, 44, 'version roadmap: third range starts at phase 44');
269
+ assert.deepStrictEqual(roadmap.phases[2].title, 'Production', 'version roadmap: third title');
270
+ assert.deepStrictEqual(roadmap.phases[2].done, false, 'version roadmap: third range in progress');
271
+ });
272
+
249
273
  test('parseOldRoadmap: milestone-sectioned with <details>', () => {
250
274
  const roadmap = parseOldRoadmap(SAMPLE_MILESTONE_SECTIONED_ROADMAP);
251
275
  assert.ok(roadmap.milestones.length >= 2, 'ms roadmap: has milestone sections');
@@ -387,4 +411,3 @@ test('parseOldProject', () => {
387
411
  const project = parseOldProject(SAMPLE_PROJECT);
388
412
  assert.deepStrictEqual(project, SAMPLE_PROJECT, 'project: returns raw content');
389
413
  });
390
-
@@ -13,17 +13,19 @@
13
13
  import { describe, test, beforeEach, afterEach } from "node:test";
14
14
  import assert from "node:assert/strict";
15
15
  import { chmodSync, existsSync, mkdtempSync, writeFileSync, readFileSync, rmSync } from "node:fs";
16
- import { join } from "node:path";
16
+ import { delimiter, join } from "node:path";
17
17
  import { tmpdir } from "node:os";
18
18
  import { execFileSync } from "node:child_process";
19
19
  import {
20
20
  assertWorktreeMaterialized,
21
21
  nativeBranchDelete,
22
22
  nativeCommit,
23
+ nativeGetCurrentBranch,
23
24
  nativeIsRepo,
24
25
  nativeResetHard,
25
26
  nativeWorktreeAdd,
26
27
  } from "../native-git-bridge.js";
28
+ import { GIT_NO_PROMPT_ENV } from "../git-constants.js";
27
29
 
28
30
  // Note: prior static-analysis tests that scanned native-git-bridge.ts for
29
31
  // the raw shell-spawn pattern were removed under #4827 — the integration
@@ -78,6 +80,55 @@ describe("native-git-bridge #4180: fallback runtime behaviour", () => {
78
80
  assert.equal(subject, "test: regression commit #4180");
79
81
  });
80
82
 
83
+ test("nativeCommit retries once after transient ENOBUFS from git", (t) => {
84
+ const bin = mkdtempSync(join(tmpdir(), "ngb-enobufs-bin-"));
85
+ t.after(() => rmSync(bin, { recursive: true, force: true }));
86
+
87
+ const realGit = execFileSync("git", ["--exec-path"], { encoding: "utf-8" }).trim();
88
+ const attempts = join(bin, "attempts.txt");
89
+ const fakeGit = join(bin, "fake-git.cjs");
90
+ writeFileSync(fakeGit, `
91
+ const { appendFileSync, readFileSync } = require("node:fs");
92
+ const { spawnSync } = require("node:child_process");
93
+ const attempts = ${JSON.stringify(attempts)};
94
+ const realGit = ${JSON.stringify(join(realGit, process.platform === "win32" ? "git.exe" : "git"))};
95
+ appendFileSync(attempts, "1");
96
+ if (process.argv[2] === "commit" && readFileSync(attempts, "utf-8").length === 1) {
97
+ console.error("spawnSync git ENOBUFS");
98
+ process.exit(1);
99
+ }
100
+ const result = spawnSync(realGit, process.argv.slice(2), { stdio: "inherit" });
101
+ process.exit(result.status ?? 1);
102
+ `, "utf-8");
103
+
104
+ if (process.platform === "win32") {
105
+ writeFileSync(join(bin, "git.cmd"), `@echo off\r\nnode "${fakeGit}" %*\r\n`, "utf-8");
106
+ } else {
107
+ const shim = join(bin, "git");
108
+ writeFileSync(shim, `#!/bin/sh\nexec node "${fakeGit}" "$@"\n`, "utf-8");
109
+ chmodSync(shim, 0o755);
110
+ }
111
+
112
+ writeFileSync(join(repo, "file.txt"), "retry commit\n");
113
+ git(["add", "."], repo);
114
+
115
+ const originalPath = process.env.PATH ?? "";
116
+ const gitEnv = GIT_NO_PROMPT_ENV as NodeJS.ProcessEnv;
117
+ const originalGitEnvPath = gitEnv.PATH;
118
+ try {
119
+ process.env.PATH = `${bin}${delimiter}${originalPath}`;
120
+ gitEnv.PATH = process.env.PATH;
121
+ const result = nativeCommit(repo, "test: retry ENOBUFS commit");
122
+ assert.ok(result !== null, "commit should succeed after retry");
123
+ } finally {
124
+ process.env.PATH = originalPath;
125
+ gitEnv.PATH = originalGitEnvPath;
126
+ }
127
+
128
+ assert.equal(readFileSync(attempts, "utf-8").length, 2);
129
+ assert.equal(git(["log", "-1", "--format=%s"], repo), "test: retry ENOBUFS commit");
130
+ });
131
+
81
132
  test("nativeCommit runs commit hooks", () => {
82
133
  const hookPath = join(repo, ".git", "hooks", "commit-msg");
83
134
  const marker = join(repo, "hook-ran.txt");
@@ -119,7 +170,17 @@ describe("native-git-bridge #4180: fallback runtime behaviour", () => {
119
170
  test("nativeBranchDelete throws when git cannot delete the branch", () => {
120
171
  assert.throws(
121
172
  () => nativeBranchDelete(repo, "does-not-exist"),
122
- /GSD_GIT_ERROR|git branch -D does-not-exist failed/,
173
+ /git branch -D does-not-exist failed[\s\S]*does-not-exist/,
174
+ );
175
+ });
176
+
177
+ test("nativeGetCurrentBranch preserves git stderr in fallback errors", (t) => {
178
+ const dir = mkdtempSync(join(tmpdir(), "ngb-stderr-notrepo-"));
179
+ t.after(() => rmSync(dir, { recursive: true, force: true }));
180
+
181
+ assert.throws(
182
+ () => nativeGetCurrentBranch(dir),
183
+ /git branch --show-current failed[\s\S]*(not a git repository|not a git repo|fatal:)/,
123
184
  );
124
185
  });
125
186