gsd-pi 2.77.0-dev.eaa4973bc → 2.78.0-dev.aeeb2ca00

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 (545) hide show
  1. package/README.md +53 -17
  2. package/dist/claude-cli-check.js +46 -10
  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 +72 -16
  13. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +481 -17
  14. package/dist/resources/extensions/github-sync/templates.js +103 -0
  15. package/dist/resources/extensions/google-search/index.js +3 -2
  16. package/dist/resources/extensions/gsd/auto/loop.js +124 -2
  17. package/dist/resources/extensions/gsd/auto/phases.js +57 -39
  18. package/dist/resources/extensions/gsd/auto/session.js +6 -2
  19. package/dist/resources/extensions/gsd/auto-dispatch.js +142 -29
  20. package/dist/resources/extensions/gsd/auto-model-selection.js +124 -4
  21. package/dist/resources/extensions/gsd/auto-post-unit.js +150 -64
  22. package/dist/resources/extensions/gsd/auto-prompts.js +372 -104
  23. package/dist/resources/extensions/gsd/auto-recovery.js +197 -48
  24. package/dist/resources/extensions/gsd/auto-start.js +107 -29
  25. package/dist/resources/extensions/gsd/auto-tool-tracking.js +47 -7
  26. package/dist/resources/extensions/gsd/auto-worktree.js +122 -26
  27. package/dist/resources/extensions/gsd/auto.js +76 -21
  28. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +19 -1
  29. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +209 -0
  30. package/dist/resources/extensions/gsd/bootstrap/provider-error-resume.js +3 -6
  31. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +7 -3
  32. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +127 -9
  33. package/dist/resources/extensions/gsd/component-loader.js +447 -0
  34. package/dist/resources/extensions/gsd/component-types.js +69 -0
  35. package/dist/resources/extensions/gsd/context-store.js +23 -7
  36. package/dist/resources/extensions/gsd/detection.js +49 -1
  37. package/dist/resources/extensions/gsd/dispatch-guard.js +2 -17
  38. package/dist/resources/extensions/gsd/docs/preferences-reference.md +1 -1
  39. package/dist/resources/extensions/gsd/forensics.js +106 -0
  40. package/dist/resources/extensions/gsd/gate-registry.js +2 -2
  41. package/dist/resources/extensions/gsd/git-constants.js +28 -1
  42. package/dist/resources/extensions/gsd/git-self-heal.js +27 -0
  43. package/dist/resources/extensions/gsd/git-service.js +126 -2
  44. package/dist/resources/extensions/gsd/gsd-db.js +6 -3
  45. package/dist/resources/extensions/gsd/guided-flow.js +39 -13
  46. package/dist/resources/extensions/gsd/memory-extractor.js +7 -1
  47. package/dist/resources/extensions/gsd/milestone-scope-classifier.js +299 -0
  48. package/dist/resources/extensions/gsd/milestone-summary-classifier.js +37 -0
  49. package/dist/resources/extensions/gsd/model-cost-table.js +3 -0
  50. package/dist/resources/extensions/gsd/model-router.js +6 -0
  51. package/dist/resources/extensions/gsd/native-git-bridge.js +34 -4
  52. package/dist/resources/extensions/gsd/preferences-validation.js +23 -0
  53. package/dist/resources/extensions/gsd/prompt-cache-optimizer.js +4 -0
  54. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +6 -2
  55. package/dist/resources/extensions/gsd/prompts/discuss-headless.md +23 -4
  56. package/dist/resources/extensions/gsd/prompts/doctor-heal.md +5 -4
  57. package/dist/resources/extensions/gsd/prompts/plan-slice.md +15 -2
  58. package/dist/resources/extensions/gsd/safety/git-checkpoint.js +11 -0
  59. package/dist/resources/extensions/gsd/service-tier.js +5 -2
  60. package/dist/resources/extensions/gsd/session-lock.js +19 -10
  61. package/dist/resources/extensions/gsd/skill-manifest.js +168 -0
  62. package/dist/resources/extensions/gsd/slice-cadence.js +238 -0
  63. package/dist/resources/extensions/gsd/slice-parallel-orchestrator.js +278 -8
  64. package/dist/resources/extensions/gsd/state-transition-matrix.js +118 -0
  65. package/dist/resources/extensions/gsd/state.js +69 -58
  66. package/dist/resources/extensions/gsd/sync-lock.js +98 -42
  67. package/dist/resources/extensions/gsd/tools/validate-milestone.js +7 -2
  68. package/dist/resources/extensions/gsd/unit-context-composer.js +147 -0
  69. package/dist/resources/extensions/gsd/unit-context-manifest.js +370 -0
  70. package/dist/resources/extensions/gsd/uok/dispatch-envelope.js +33 -0
  71. package/dist/resources/extensions/gsd/uok/execution-graph.js +10 -0
  72. package/dist/resources/extensions/gsd/uok/gate-runner.js +53 -5
  73. package/dist/resources/extensions/gsd/uok/gitops.js +2 -1
  74. package/dist/resources/extensions/gsd/uok/loop-adapter.js +37 -10
  75. package/dist/resources/extensions/gsd/uok/parity-report.js +58 -0
  76. package/dist/resources/extensions/gsd/uok/plan-v2.js +10 -4
  77. package/dist/resources/extensions/gsd/uok/writer.js +82 -0
  78. package/dist/resources/extensions/gsd/workflow-mcp.js +6 -0
  79. package/dist/resources/extensions/gsd/worktree-manager.js +85 -8
  80. package/dist/resources/extensions/gsd/worktree-resolver.js +86 -7
  81. package/dist/resources/extensions/gsd/worktree-telemetry.js +198 -0
  82. package/dist/resources/extensions/mcp-client/index.js +3 -1
  83. package/dist/resources/extensions/ollama/index.js +5 -1
  84. package/dist/resources/extensions/remote-questions/manager.js +11 -5
  85. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  86. package/dist/web/standalone/.next/BUILD_ID +1 -1
  87. package/dist/web/standalone/.next/app-path-routes-manifest.json +11 -11
  88. package/dist/web/standalone/.next/build-manifest.json +2 -2
  89. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  90. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  91. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  92. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  93. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  94. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  95. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  96. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  97. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  98. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  99. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  100. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  101. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  102. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  103. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  104. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  105. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  106. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  107. package/dist/web/standalone/.next/server/app/index.html +1 -1
  108. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  109. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  110. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  111. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  112. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  113. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  114. package/dist/web/standalone/.next/server/app-paths-manifest.json +11 -11
  115. package/dist/web/standalone/.next/server/chunks/1926.js +1 -1
  116. package/dist/web/standalone/.next/server/chunks/6897.js +1 -1
  117. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  118. package/dist/web/standalone/.next/server/middleware-manifest.json +5 -5
  119. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  120. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  121. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  122. package/package.json +2 -3
  123. package/packages/daemon/package.json +2 -2
  124. package/packages/daemon/src/logger.ts +4 -3
  125. package/packages/mcp-server/dist/server.d.ts +24 -0
  126. package/packages/mcp-server/dist/server.d.ts.map +1 -1
  127. package/packages/mcp-server/dist/server.js +88 -87
  128. package/packages/mcp-server/dist/server.js.map +1 -1
  129. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  130. package/packages/mcp-server/dist/workflow-tools.js +15 -6
  131. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  132. package/packages/mcp-server/package.json +2 -2
  133. package/packages/mcp-server/src/mcp-server.test.ts +25 -3
  134. package/packages/mcp-server/src/readers/graph.test.ts +87 -15
  135. package/packages/mcp-server/src/secure-env-collect.test.ts +232 -237
  136. package/packages/mcp-server/src/server.ts +131 -105
  137. package/packages/mcp-server/src/workflow-tools.test.ts +85 -0
  138. package/packages/mcp-server/src/workflow-tools.ts +19 -6
  139. package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
  140. package/packages/native/package.json +2 -2
  141. package/packages/native/src/__tests__/_test-coverage-guard.test.mjs +98 -0
  142. package/packages/native/src/__tests__/module-compat.test.mjs +59 -27
  143. package/packages/native/src/__tests__/ps.test.mjs +14 -8
  144. package/packages/native/src/__tests__/stream-process.test.mjs +23 -2
  145. package/packages/native/src/__tests__/truncate.test.mjs +17 -2
  146. package/packages/pi-agent-core/package.json +1 -1
  147. package/packages/pi-agent-core/src/agent-loop.test.ts +5 -15
  148. package/packages/pi-agent-core/src/agent.test.ts +96 -102
  149. package/packages/pi-agent-core/tsconfig.tsbuildinfo +1 -1
  150. package/packages/pi-ai/dist/models/capability-patches.d.ts.map +1 -1
  151. package/packages/pi-ai/dist/models/capability-patches.js +9 -2
  152. package/packages/pi-ai/dist/models/capability-patches.js.map +1 -1
  153. package/packages/pi-ai/dist/models/generated/index.d.ts +34 -0
  154. package/packages/pi-ai/dist/models/generated/index.d.ts.map +1 -1
  155. package/packages/pi-ai/dist/models/generated/openai-codex.d.ts +17 -0
  156. package/packages/pi-ai/dist/models/generated/openai-codex.d.ts.map +1 -1
  157. package/packages/pi-ai/dist/models/generated/openai-codex.js +17 -0
  158. package/packages/pi-ai/dist/models/generated/openai-codex.js.map +1 -1
  159. package/packages/pi-ai/dist/models/generated/openai.d.ts +17 -0
  160. package/packages/pi-ai/dist/models/generated/openai.d.ts.map +1 -1
  161. package/packages/pi-ai/dist/models/generated/openai.js +17 -0
  162. package/packages/pi-ai/dist/models/generated/openai.js.map +1 -1
  163. package/packages/pi-ai/dist/models.generated.test.js +43 -70
  164. package/packages/pi-ai/dist/models.generated.test.js.map +1 -1
  165. package/packages/pi-ai/dist/models.test.js +36 -11
  166. package/packages/pi-ai/dist/models.test.js.map +1 -1
  167. package/packages/pi-ai/package.json +1 -1
  168. package/packages/pi-ai/scripts/generate-models.ts +44 -0
  169. package/packages/pi-ai/src/models/capability-patches.ts +10 -2
  170. package/packages/pi-ai/src/models/generated/openai-codex.ts +17 -0
  171. package/packages/pi-ai/src/models/generated/openai.ts +17 -0
  172. package/packages/pi-ai/src/models.generated.test.ts +46 -73
  173. package/packages/pi-ai/src/models.test.ts +48 -11
  174. package/packages/pi-ai/tsconfig.tsbuildinfo +1 -1
  175. package/packages/pi-coding-agent/dist/core/agent-session-abort-order.test.js +96 -32
  176. package/packages/pi-coding-agent/dist/core/agent-session-abort-order.test.js.map +1 -1
  177. package/packages/pi-coding-agent/dist/core/agent-session-model-switch.test.js +75 -12
  178. package/packages/pi-coding-agent/dist/core/agent-session-model-switch.test.js.map +1 -1
  179. package/packages/pi-coding-agent/dist/core/agent-session-tool-refresh.test.js +99 -31
  180. package/packages/pi-coding-agent/dist/core/agent-session-tool-refresh.test.js.map +1 -1
  181. package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts +5 -0
  182. package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
  183. package/packages/pi-coding-agent/dist/core/extensions/loader.js +61 -0
  184. package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
  185. package/packages/pi-coding-agent/dist/core/lsp/lsp-integration.test.js +30 -4
  186. package/packages/pi-coding-agent/dist/core/lsp/lsp-integration.test.js.map +1 -1
  187. package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js +17 -0
  188. package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js.map +1 -1
  189. package/packages/pi-coding-agent/dist/core/resource-loader-cache-reset.test.js +76 -18
  190. package/packages/pi-coding-agent/dist/core/resource-loader-cache-reset.test.js.map +1 -1
  191. package/packages/pi-coding-agent/dist/core/retry-handler.d.ts.map +1 -1
  192. package/packages/pi-coding-agent/dist/core/retry-handler.js +2 -6
  193. package/packages/pi-coding-agent/dist/core/retry-handler.js.map +1 -1
  194. package/packages/pi-coding-agent/dist/core/retry-handler.test.js +5 -1
  195. package/packages/pi-coding-agent/dist/core/retry-handler.test.js.map +1 -1
  196. package/packages/pi-coding-agent/dist/core/retryable-error-regex.d.ts +18 -0
  197. package/packages/pi-coding-agent/dist/core/retryable-error-regex.d.ts.map +1 -0
  198. package/packages/pi-coding-agent/dist/core/retryable-error-regex.js +18 -0
  199. package/packages/pi-coding-agent/dist/core/retryable-error-regex.js.map +1 -0
  200. package/packages/pi-coding-agent/dist/core/system-prompt.d.ts +20 -0
  201. package/packages/pi-coding-agent/dist/core/system-prompt.d.ts.map +1 -1
  202. package/packages/pi-coding-agent/dist/core/system-prompt.js +16 -2
  203. package/packages/pi-coding-agent/dist/core/system-prompt.js.map +1 -1
  204. package/packages/pi-coding-agent/dist/index.d.ts +1 -0
  205. package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
  206. package/packages/pi-coding-agent/dist/index.js +1 -0
  207. package/packages/pi-coding-agent/dist/index.js.map +1 -1
  208. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js +36 -5
  209. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js.map +1 -1
  210. package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.test.js +20 -13
  211. package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.test.js.map +1 -1
  212. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  213. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +30 -12
  214. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
  215. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.d.ts.map +1 -1
  216. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js +18 -3
  217. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js.map +1 -1
  218. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js +125 -0
  219. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js.map +1 -1
  220. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts +2 -0
  221. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts.map +1 -1
  222. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.js.map +1 -1
  223. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +4 -0
  224. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  225. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +105 -13
  226. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  227. package/packages/pi-coding-agent/dist/tests/system-prompt-skill-filter.test.d.ts +2 -0
  228. package/packages/pi-coding-agent/dist/tests/system-prompt-skill-filter.test.d.ts.map +1 -0
  229. package/packages/pi-coding-agent/dist/tests/system-prompt-skill-filter.test.js +130 -0
  230. package/packages/pi-coding-agent/dist/tests/system-prompt-skill-filter.test.js.map +1 -0
  231. package/packages/pi-coding-agent/package.json +1 -1
  232. package/packages/pi-coding-agent/src/core/agent-session-abort-order.test.ts +113 -37
  233. package/packages/pi-coding-agent/src/core/agent-session-model-switch.test.ts +89 -17
  234. package/packages/pi-coding-agent/src/core/agent-session-tool-refresh.test.ts +112 -43
  235. package/packages/pi-coding-agent/src/core/extensions/loader.ts +58 -0
  236. package/packages/pi-coding-agent/src/core/lsp/lsp-integration.test.ts +35 -4
  237. package/packages/pi-coding-agent/src/core/model-registry-auth-mode.test.ts +20 -0
  238. package/packages/pi-coding-agent/src/core/resource-loader-cache-reset.test.ts +93 -28
  239. package/packages/pi-coding-agent/src/core/retry-handler.test.ts +5 -1
  240. package/packages/pi-coding-agent/src/core/retry-handler.ts +2 -8
  241. package/packages/pi-coding-agent/src/core/retryable-error-regex.ts +18 -0
  242. package/packages/pi-coding-agent/src/core/system-prompt.ts +35 -1
  243. package/packages/pi-coding-agent/src/index.ts +1 -0
  244. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/tool-execution.test.ts +49 -3
  245. package/packages/pi-coding-agent/src/modes/interactive/components/dynamic-border.test.ts +26 -20
  246. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +48 -9
  247. package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.test.ts +146 -1
  248. package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.ts +20 -3
  249. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode-state.ts +2 -0
  250. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +119 -13
  251. package/packages/pi-coding-agent/src/tests/system-prompt-skill-filter.test.ts +157 -0
  252. package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
  253. package/packages/pi-tui/dist/__tests__/autocomplete.test.js +18 -8
  254. package/packages/pi-tui/dist/__tests__/autocomplete.test.js.map +1 -1
  255. package/packages/pi-tui/dist/__tests__/overlay-layout.test.js +128 -17
  256. package/packages/pi-tui/dist/__tests__/overlay-layout.test.js.map +1 -1
  257. package/packages/pi-tui/dist/__tests__/stdin-buffer.test.js +37 -11
  258. package/packages/pi-tui/dist/__tests__/stdin-buffer.test.js.map +1 -1
  259. package/packages/pi-tui/dist/__tests__/tui.test.js +18 -30
  260. package/packages/pi-tui/dist/__tests__/tui.test.js.map +1 -1
  261. package/packages/pi-tui/dist/components/__tests__/input.test.js +10 -3
  262. package/packages/pi-tui/dist/components/__tests__/input.test.js.map +1 -1
  263. package/packages/pi-tui/dist/components/__tests__/loader.test.js +53 -9
  264. package/packages/pi-tui/dist/components/__tests__/loader.test.js.map +1 -1
  265. package/packages/pi-tui/dist/components/__tests__/markdown-maxlines.test.js +6 -2
  266. package/packages/pi-tui/dist/components/__tests__/markdown-maxlines.test.js.map +1 -1
  267. package/packages/pi-tui/dist/components/editor.d.ts +14 -0
  268. package/packages/pi-tui/dist/components/editor.d.ts.map +1 -1
  269. package/packages/pi-tui/dist/components/editor.js +19 -0
  270. package/packages/pi-tui/dist/components/editor.js.map +1 -1
  271. package/packages/pi-tui/dist/components/image.test.js +6 -5
  272. package/packages/pi-tui/dist/components/image.test.js.map +1 -1
  273. package/packages/pi-tui/dist/editor-component.d.ts +2 -0
  274. package/packages/pi-tui/dist/editor-component.d.ts.map +1 -1
  275. package/packages/pi-tui/dist/editor-component.js.map +1 -1
  276. package/packages/pi-tui/package.json +1 -1
  277. package/packages/pi-tui/src/__tests__/autocomplete.test.ts +24 -8
  278. package/packages/pi-tui/src/__tests__/overlay-layout.test.ts +140 -17
  279. package/packages/pi-tui/src/__tests__/stdin-buffer.test.ts +42 -11
  280. package/packages/pi-tui/src/__tests__/tui.test.ts +18 -37
  281. package/packages/pi-tui/src/components/__tests__/input.test.ts +19 -3
  282. package/packages/pi-tui/src/components/__tests__/loader.test.ts +112 -35
  283. package/packages/pi-tui/src/components/__tests__/markdown-maxlines.test.ts +9 -2
  284. package/packages/pi-tui/src/components/editor.ts +22 -0
  285. package/packages/pi-tui/src/components/image.test.ts +10 -5
  286. package/packages/pi-tui/src/editor-component.ts +3 -0
  287. package/packages/pi-tui/tsconfig.tsbuildinfo +1 -1
  288. package/packages/rpc-client/dist/rpc-client.test.js +101 -51
  289. package/packages/rpc-client/dist/rpc-client.test.js.map +1 -1
  290. package/packages/rpc-client/package.json +1 -1
  291. package/packages/rpc-client/src/rpc-client.test.ts +109 -52
  292. package/packages/rpc-client/tsconfig.tsbuildinfo +1 -1
  293. package/pkg/package.json +1 -1
  294. package/scripts/install.js +15 -1
  295. package/src/resources/extensions/browser-tools/capture.ts +12 -0
  296. package/src/resources/extensions/browser-tools/tests/browser-tools-integration.test.mjs +8 -59
  297. package/src/resources/extensions/browser-tools/tests/browser-tools-unit.test.cjs +36 -24
  298. package/src/resources/extensions/browser-tools/tests/capture-sharp-optional.test.cjs +69 -71
  299. package/src/resources/extensions/browser-tools/tools/forms.ts +5 -1
  300. package/src/resources/extensions/browser-tools/tools/intent.ts +5 -1
  301. package/src/resources/extensions/claude-code-cli/readiness.ts +75 -16
  302. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +518 -19
  303. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +919 -75
  304. package/src/resources/extensions/github-sync/templates.ts +151 -0
  305. package/src/resources/extensions/github-sync/tests/cli.test.ts +76 -7
  306. package/src/resources/extensions/github-sync/tests/templates.test.ts +92 -1
  307. package/src/resources/extensions/google-search/index.ts +3 -2
  308. package/src/resources/extensions/gsd/auto/loop.ts +142 -2
  309. package/src/resources/extensions/gsd/auto/phases.ts +62 -38
  310. package/src/resources/extensions/gsd/auto/session.ts +7 -2
  311. package/src/resources/extensions/gsd/auto-dispatch.ts +156 -29
  312. package/src/resources/extensions/gsd/auto-model-selection.ts +131 -4
  313. package/src/resources/extensions/gsd/auto-post-unit.ts +163 -73
  314. package/src/resources/extensions/gsd/auto-prompts.ts +385 -93
  315. package/src/resources/extensions/gsd/auto-recovery.ts +230 -51
  316. package/src/resources/extensions/gsd/auto-start.ts +127 -9
  317. package/src/resources/extensions/gsd/auto-tool-tracking.ts +51 -7
  318. package/src/resources/extensions/gsd/auto-worktree.ts +130 -26
  319. package/src/resources/extensions/gsd/auto.ts +90 -23
  320. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +20 -1
  321. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +221 -0
  322. package/src/resources/extensions/gsd/bootstrap/provider-error-resume.ts +3 -7
  323. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +7 -3
  324. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +158 -9
  325. package/src/resources/extensions/gsd/component-loader.ts +598 -0
  326. package/src/resources/extensions/gsd/component-types.ts +362 -0
  327. package/src/resources/extensions/gsd/context-store.ts +25 -8
  328. package/src/resources/extensions/gsd/detection.ts +58 -1
  329. package/src/resources/extensions/gsd/dispatch-guard.ts +2 -20
  330. package/src/resources/extensions/gsd/docs/preferences-reference.md +1 -1
  331. package/src/resources/extensions/gsd/forensics.ts +118 -1
  332. package/src/resources/extensions/gsd/gate-registry.ts +2 -2
  333. package/src/resources/extensions/gsd/git-constants.ts +30 -1
  334. package/src/resources/extensions/gsd/git-self-heal.ts +31 -0
  335. package/src/resources/extensions/gsd/git-service.ts +149 -2
  336. package/src/resources/extensions/gsd/gsd-db.ts +6 -3
  337. package/src/resources/extensions/gsd/guided-flow.ts +57 -14
  338. package/src/resources/extensions/gsd/journal.ts +11 -1
  339. package/src/resources/extensions/gsd/memory-extractor.ts +11 -3
  340. package/src/resources/extensions/gsd/milestone-scope-classifier.ts +366 -0
  341. package/src/resources/extensions/gsd/milestone-summary-classifier.ts +42 -0
  342. package/src/resources/extensions/gsd/model-cost-table.ts +3 -0
  343. package/src/resources/extensions/gsd/model-router.ts +6 -0
  344. package/src/resources/extensions/gsd/native-git-bridge.ts +34 -4
  345. package/src/resources/extensions/gsd/preferences-validation.ts +21 -0
  346. package/src/resources/extensions/gsd/prompt-cache-optimizer.ts +4 -0
  347. package/src/resources/extensions/gsd/prompts/complete-milestone.md +6 -2
  348. package/src/resources/extensions/gsd/prompts/discuss-headless.md +23 -4
  349. package/src/resources/extensions/gsd/prompts/doctor-heal.md +5 -4
  350. package/src/resources/extensions/gsd/prompts/plan-slice.md +15 -2
  351. package/src/resources/extensions/gsd/safety/git-checkpoint.ts +15 -0
  352. package/src/resources/extensions/gsd/service-tier.ts +5 -2
  353. package/src/resources/extensions/gsd/session-lock.ts +20 -10
  354. package/src/resources/extensions/gsd/skill-manifest.ts +175 -0
  355. package/src/resources/extensions/gsd/slice-cadence.ts +299 -0
  356. package/src/resources/extensions/gsd/slice-parallel-orchestrator.ts +309 -8
  357. package/src/resources/extensions/gsd/state-transition-matrix.ts +152 -0
  358. package/src/resources/extensions/gsd/state.ts +76 -66
  359. package/src/resources/extensions/gsd/sync-lock.ts +97 -39
  360. package/src/resources/extensions/gsd/tests/artifact-retry-cap.test.ts +270 -0
  361. package/src/resources/extensions/gsd/tests/artifacts-table-preserved-on-cache-invalidate.test.ts +2 -1
  362. package/src/resources/extensions/gsd/tests/auto-deterministic-error-classification-4973.test.ts +341 -0
  363. package/src/resources/extensions/gsd/tests/auto-discuss-milestone-deadlock-4973.test.ts +264 -0
  364. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +133 -292
  365. package/src/resources/extensions/gsd/tests/auto-model-selection-tool-poisoning.test.ts +742 -0
  366. package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +78 -0
  367. package/src/resources/extensions/gsd/tests/auto-phases-lifecycle.test.ts +61 -0
  368. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +93 -0
  369. package/src/resources/extensions/gsd/tests/auto-remediate-slice-status.test.ts +4 -1
  370. package/src/resources/extensions/gsd/tests/auto-retry-mcp-churn-fixes.test.ts +8 -194
  371. package/src/resources/extensions/gsd/tests/auto-start-clean-runtime-db-gated.test.ts +3 -2
  372. package/src/resources/extensions/gsd/tests/auto-start-cold-db-bootstrap.test.ts +2 -2
  373. package/src/resources/extensions/gsd/tests/auto-start-needs-discussion.test.ts +15 -58
  374. package/src/resources/extensions/gsd/tests/auto-start-worktree-db-path.test.ts +2 -2
  375. package/src/resources/extensions/gsd/tests/auto-thinking-restore.test.ts +3 -2
  376. package/src/resources/extensions/gsd/tests/auto-warning-noise-regression.test.ts +3 -2
  377. package/src/resources/extensions/gsd/tests/bootstrap-derive-state-db-open.test.ts +2 -1
  378. package/src/resources/extensions/gsd/tests/cache-staleness-regression.test.ts +17 -21
  379. package/src/resources/extensions/gsd/tests/canonical-milestone-root.test.ts +108 -0
  380. package/src/resources/extensions/gsd/tests/complete-milestone-excerpt.test.ts +263 -0
  381. package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +25 -0
  382. package/src/resources/extensions/gsd/tests/complete-slice-composer.test.ts +192 -0
  383. package/src/resources/extensions/gsd/tests/complete-slice-verification-gate.test.ts +2 -1
  384. package/src/resources/extensions/gsd/tests/complete-task.test.ts +16 -8
  385. package/src/resources/extensions/gsd/tests/component-loader.test.ts +589 -0
  386. package/src/resources/extensions/gsd/tests/component-types.test.ts +127 -0
  387. package/src/resources/extensions/gsd/tests/context-store.test.ts +79 -0
  388. package/src/resources/extensions/gsd/tests/copy-planning-artifacts-samepath.test.ts +2 -1
  389. package/src/resources/extensions/gsd/tests/crash-recovery.test.ts +50 -1
  390. package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +159 -0
  391. package/src/resources/extensions/gsd/tests/db-access-guardrails.test.ts +1 -0
  392. package/src/resources/extensions/gsd/tests/derive-state-crossval.test.ts +3 -3
  393. package/src/resources/extensions/gsd/tests/derive-state-db-disk-reconcile.test.ts +40 -0
  394. package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +91 -3
  395. package/src/resources/extensions/gsd/tests/derive-state.test.ts +4 -4
  396. package/src/resources/extensions/gsd/tests/discuss-slice-structured-questions.test.ts +2 -1
  397. package/src/resources/extensions/gsd/tests/discuss-tool-scope-leak.test.ts +2 -1
  398. package/src/resources/extensions/gsd/tests/dispatch-complete-milestone-guard.test.ts +5 -0
  399. package/src/resources/extensions/gsd/tests/dispatch-guard.test.ts +25 -0
  400. package/src/resources/extensions/gsd/tests/dispatch-missing-task-plans.test.ts +14 -0
  401. package/src/resources/extensions/gsd/tests/dispatcher-stuck-planning.test.ts +3 -2
  402. package/src/resources/extensions/gsd/tests/double-merge-guard.test.ts +4 -3
  403. package/src/resources/extensions/gsd/tests/empty-content-abort-loop.test.ts +4 -3
  404. package/src/resources/extensions/gsd/tests/execution-entry-missing-context-4671.test.ts +173 -0
  405. package/src/resources/extensions/gsd/tests/extension-bootstrap-isolation.test.ts +139 -129
  406. package/src/resources/extensions/gsd/tests/finalize-timeout-guard.test.ts +8 -104
  407. package/src/resources/extensions/gsd/tests/gate-state-canonicalization.test.ts +102 -0
  408. package/src/resources/extensions/gsd/tests/gate-storage.test.ts +1 -1
  409. package/src/resources/extensions/gsd/tests/google-search-stub.test.ts +14 -4
  410. package/src/resources/extensions/gsd/tests/headless-milestone-parity.test.ts +117 -0
  411. package/src/resources/extensions/gsd/tests/hook-key-parsing.test.ts +4 -55
  412. package/src/resources/extensions/gsd/tests/integration/all-milestones-complete-merge.test.ts +7 -56
  413. package/src/resources/extensions/gsd/tests/integration/auto-recovery.test.ts +20 -0
  414. package/src/resources/extensions/gsd/tests/integration/doctor-proactive.test.ts +18 -2
  415. package/src/resources/extensions/gsd/tests/integration/queue-completed-milestone-perf.test.ts +10 -4
  416. package/src/resources/extensions/gsd/tests/integration/state-machine-edge-cases.test.ts +144 -7
  417. package/src/resources/extensions/gsd/tests/integration/state-machine-live-validation.test.ts +4 -0
  418. package/src/resources/extensions/gsd/tests/integration/state-machine-runtime-failures.test.ts +2 -16
  419. package/src/resources/extensions/gsd/tests/interactive-routing-bypass.test.ts +9 -3
  420. package/src/resources/extensions/gsd/tests/interrupted-session-ui.test.ts +6 -9
  421. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +64 -0
  422. package/src/resources/extensions/gsd/tests/knowledge.test.ts +93 -1
  423. package/src/resources/extensions/gsd/tests/mcp-client-security.test.ts +8 -37
  424. package/src/resources/extensions/gsd/tests/memory-extractor.test.ts +5 -15
  425. package/src/resources/extensions/gsd/tests/merge-conflict-stops-loop.test.ts +227 -55
  426. package/src/resources/extensions/gsd/tests/milestone-scope-classifier.test.ts +187 -0
  427. package/src/resources/extensions/gsd/tests/milestone-summary-classifier.test.ts +30 -0
  428. package/src/resources/extensions/gsd/tests/model-cost-table.test.ts +9 -1
  429. package/src/resources/extensions/gsd/tests/model-router.test.ts +1 -1
  430. package/src/resources/extensions/gsd/tests/native-git-bridge-exec-fallback.test.ts +6 -48
  431. package/src/resources/extensions/gsd/tests/notification-widget.test.ts +6 -3
  432. package/src/resources/extensions/gsd/tests/orphaned-worktree-audit.test.ts +59 -2
  433. package/src/resources/extensions/gsd/tests/parallel-research-dispatch.test.ts +273 -130
  434. package/src/resources/extensions/gsd/tests/pipeline-variant-dispatch.test.ts +301 -0
  435. package/src/resources/extensions/gsd/tests/pre-execution-pause-wiring.test.ts +32 -1
  436. package/src/resources/extensions/gsd/tests/preferences-worktree-sync.test.ts +2 -1
  437. package/src/resources/extensions/gsd/tests/prompt-cache-optimizer.test.ts +12 -0
  438. package/src/resources/extensions/gsd/tests/prompt-step-ordering.test.ts +15 -4
  439. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +23 -24
  440. package/src/resources/extensions/gsd/tests/queue-auto-guard.test.ts +32 -0
  441. package/src/resources/extensions/gsd/tests/queue-draft-detection.test.ts +3 -2
  442. package/src/resources/extensions/gsd/tests/queued-discuss-fast-path.test.ts +4 -5
  443. package/src/resources/extensions/gsd/tests/ready-phrase-no-files-4573.test.ts +75 -2
  444. package/src/resources/extensions/gsd/tests/reassess-default-optin.test.ts +132 -0
  445. package/src/resources/extensions/gsd/tests/recovery-attempts-reset.test.ts +8 -40
  446. package/src/resources/extensions/gsd/tests/regex-hardening.test.ts +136 -256
  447. package/src/resources/extensions/gsd/tests/research-milestone-composer.test.ts +114 -0
  448. package/src/resources/extensions/gsd/tests/restore-tools-after-discuss.test.ts +6 -3
  449. package/src/resources/extensions/gsd/tests/run-uat-composer.test.ts +148 -0
  450. package/src/resources/extensions/gsd/tests/service-tier.test.ts +4 -0
  451. package/src/resources/extensions/gsd/tests/session-lock-regression.test.ts +29 -0
  452. package/src/resources/extensions/gsd/tests/sidecar-queue.test.ts +3 -2
  453. package/src/resources/extensions/gsd/tests/silent-catch-diagnostics.test.ts +55 -95
  454. package/src/resources/extensions/gsd/tests/single-writer-v3-tool-surface.test.ts +158 -0
  455. package/src/resources/extensions/gsd/tests/skill-activation.test.ts +120 -1
  456. package/src/resources/extensions/gsd/tests/skill-manifest.test.ts +112 -0
  457. package/src/resources/extensions/gsd/tests/slice-cadence.test.ts +242 -0
  458. package/src/resources/extensions/gsd/tests/slice-context-injection.test.ts +3 -2
  459. package/src/resources/extensions/gsd/tests/slice-parallel-orchestrator.test.ts +164 -1
  460. package/src/resources/extensions/gsd/tests/smart-entry-draft.test.ts +2 -1
  461. package/src/resources/extensions/gsd/tests/stale-dirlistcache-4648.test.ts +112 -0
  462. package/src/resources/extensions/gsd/tests/state-machine-full-walkthrough.test.ts +29 -5
  463. package/src/resources/extensions/gsd/tests/state-transition-matrix.test.ts +44 -0
  464. package/src/resources/extensions/gsd/tests/stop-auto-race-null-unit.test.ts +3 -3
  465. package/src/resources/extensions/gsd/tests/structured-data-formatter.test.ts +11 -92
  466. package/src/resources/extensions/gsd/tests/subagent-model-dispatch.test.ts +7 -6
  467. package/src/resources/extensions/gsd/tests/survivor-branch-complete.test.ts +102 -101
  468. package/src/resources/extensions/gsd/tests/sync-lock.test.ts +31 -0
  469. package/src/resources/extensions/gsd/tests/sync-worktree-skip-current.test.ts +4 -3
  470. package/src/resources/extensions/gsd/tests/test-helpers.test.ts +98 -0
  471. package/src/resources/extensions/gsd/tests/test-helpers.ts +153 -0
  472. package/src/resources/extensions/gsd/tests/token-profile.test.ts +8 -1
  473. package/src/resources/extensions/gsd/tests/tool-invocation-error-loop-break.test.ts +61 -1
  474. package/src/resources/extensions/gsd/tests/tool-naming.test.ts +8 -1
  475. package/src/resources/extensions/gsd/tests/unit-context-composer.test.ts +355 -0
  476. package/src/resources/extensions/gsd/tests/unit-context-manifest.test.ts +258 -0
  477. package/src/resources/extensions/gsd/tests/uok-contracts.test.ts +51 -0
  478. package/src/resources/extensions/gsd/tests/uok-execution-graph.test.ts +16 -0
  479. package/src/resources/extensions/gsd/tests/uok-gate-runner.test.ts +75 -0
  480. package/src/resources/extensions/gsd/tests/uok-gitops-wiring.test.ts +49 -26
  481. package/src/resources/extensions/gsd/tests/uok-loop-adapter-writer.test.ts +65 -0
  482. package/src/resources/extensions/gsd/tests/uok-parity-report.test.ts +42 -0
  483. package/src/resources/extensions/gsd/tests/uok-plan-v2-wiring.test.ts +19 -2
  484. package/src/resources/extensions/gsd/tests/uok-writer.test.ts +75 -0
  485. package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +12 -0
  486. package/src/resources/extensions/gsd/tests/verify-artifact-tightened.test.ts +144 -80
  487. package/src/resources/extensions/gsd/tests/visualizer-critical-path.test.ts +20 -54
  488. package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +342 -277
  489. package/src/resources/extensions/gsd/tests/worker-model-override.test.ts +37 -29
  490. package/src/resources/extensions/gsd/tests/worktree-db.test.ts +226 -266
  491. package/src/resources/extensions/gsd/tests/worktree-health-monorepo.test.ts +103 -67
  492. package/src/resources/extensions/gsd/tests/worktree-nested-git-safety.test.ts +92 -90
  493. package/src/resources/extensions/gsd/tests/worktree-submodule-safety.test.ts +238 -59
  494. package/src/resources/extensions/gsd/tests/worktree-sync-overwrite-loop.test.ts +113 -161
  495. package/src/resources/extensions/gsd/tests/worktree-telemetry.test.ts +210 -0
  496. package/src/resources/extensions/gsd/tests/write-gate-planning-unit.test.ts +262 -0
  497. package/src/resources/extensions/gsd/tests/write-gate-predicates.test.ts +186 -0
  498. package/src/resources/extensions/gsd/tests/write-gate.test.ts +7 -5
  499. package/src/resources/extensions/gsd/tests/zombie-gsd-state.test.ts +80 -96
  500. package/src/resources/extensions/gsd/tools/validate-milestone.ts +8 -2
  501. package/src/resources/extensions/gsd/types.ts +3 -3
  502. package/src/resources/extensions/gsd/unit-context-composer.ts +218 -0
  503. package/src/resources/extensions/gsd/unit-context-manifest.ts +574 -0
  504. package/src/resources/extensions/gsd/uok/contracts.ts +65 -0
  505. package/src/resources/extensions/gsd/uok/dispatch-envelope.ts +56 -0
  506. package/src/resources/extensions/gsd/uok/execution-graph.ts +22 -0
  507. package/src/resources/extensions/gsd/uok/gate-runner.ts +65 -5
  508. package/src/resources/extensions/gsd/uok/gitops.ts +6 -1
  509. package/src/resources/extensions/gsd/uok/loop-adapter.ts +45 -10
  510. package/src/resources/extensions/gsd/uok/parity-report.ts +84 -0
  511. package/src/resources/extensions/gsd/uok/plan-v2.ts +13 -5
  512. package/src/resources/extensions/gsd/uok/writer.ts +113 -0
  513. package/src/resources/extensions/gsd/workflow-mcp.ts +6 -0
  514. package/src/resources/extensions/gsd/worktree-manager.ts +108 -7
  515. package/src/resources/extensions/gsd/worktree-resolver.ts +96 -9
  516. package/src/resources/extensions/gsd/worktree-telemetry.ts +322 -0
  517. package/src/resources/extensions/mcp-client/index.ts +3 -1
  518. package/src/resources/extensions/mcp-client/tests/server-name-spaces.test.ts +70 -36
  519. package/src/resources/extensions/ollama/index.ts +5 -1
  520. package/src/resources/extensions/ollama/ollama-auth-mode.test.ts +123 -15
  521. package/src/resources/extensions/ollama/ollama-status-indicator.test.ts +206 -19
  522. package/src/resources/extensions/remote-questions/manager.ts +36 -4
  523. package/src/resources/extensions/remote-questions/tests/command-polling.test.ts +200 -190
  524. package/src/resources/extensions/shared/tests/interview-preview.test.ts +11 -3
  525. package/src/resources/extensions/voice/tests/linux-ready.test.ts +129 -113
  526. package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.d.ts +0 -2
  527. package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.d.ts.map +0 -1
  528. package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.js +0 -289
  529. package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.js.map +0 -1
  530. package/packages/pi-ai/src/utils/oauth/oauth-providers.test.ts +0 -363
  531. package/src/resources/extensions/gsd/tests/auto-start-model-capture.test.ts +0 -143
  532. package/src/resources/extensions/gsd/tests/complete-milestone-false-merge.test.ts +0 -157
  533. package/src/resources/extensions/gsd/tests/dashboard-model-label-ordering.test.ts +0 -107
  534. package/src/resources/extensions/gsd/tests/find-missing-summaries-closed.test.ts +0 -48
  535. package/src/resources/extensions/gsd/tests/forensics-context-persist.test.ts +0 -159
  536. package/src/resources/extensions/gsd/tests/forensics-db-completion.test.ts +0 -96
  537. package/src/resources/extensions/gsd/tests/forensics-dedup.test.ts +0 -79
  538. package/src/resources/extensions/gsd/tests/forensics-hook-key-parse.test.ts +0 -74
  539. package/src/resources/extensions/gsd/tests/forensics-journal.test.ts +0 -162
  540. package/src/resources/extensions/gsd/tests/gitignore-bg-shell.test.ts +0 -38
  541. package/src/resources/extensions/gsd/tests/gsd-no-project-error.test.ts +0 -73
  542. package/src/resources/extensions/gsd/tests/idle-watchdog-stall-override.test.ts +0 -125
  543. package/src/resources/extensions/gsd/tests/import-done-milestones.test.ts +0 -42
  544. /package/dist/web/standalone/.next/static/{5wbu35_C2_MQ3Jj1lEVDx → cAJH99yNS1UPbeSEiNRrV}/_buildManifest.js +0 -0
  545. /package/dist/web/standalone/.next/static/{5wbu35_C2_MQ3Jj1lEVDx → cAJH99yNS1UPbeSEiNRrV}/_ssgManifest.js +0 -0
@@ -627,4 +627,83 @@ Integration tests mock external services.
627
627
 
628
628
  assert.strictEqual(result, '', 'empty content returns empty string');
629
629
  });
630
+
631
+ // ── Regression: issue #4719 — single-H2 with many H3 entries ──────────────
632
+ // A KNOWLEDGE.md structured as one top-level H2 with many H3 entries must
633
+ // filter at H3 granularity; otherwise one keyword match against the H2
634
+ // header or first paragraph returns the entire file.
635
+ test("single H2 with many H3 entries filters at H3 level (issue #4719)", async () => {
636
+ const singleH2Knowledge = `# Project Knowledge
637
+
638
+ ## Patterns
639
+
640
+ ### Database: prepared statements
641
+ Always use prepared statements with SQLite.
642
+
643
+ ### API: versioned paths
644
+ Use /v1/resource style versioning.
645
+
646
+ ### Testing: node:test
647
+ Prefer node:test over external frameworks.
648
+
649
+ ### Deployment: blue-green
650
+ Blue-green deployment for zero-downtime releases.
651
+ `;
652
+
653
+ const result = await queryKnowledge(singleH2Knowledge, ['database']);
654
+
655
+ // Should include only the matching H3 entry, not the whole file
656
+ assert.match(result, /Database: prepared statements/, 'includes matching H3 entry');
657
+ assert.ok(
658
+ !result.includes('API: versioned paths'),
659
+ 'does not include non-matching H3 entry',
660
+ );
661
+ assert.ok(
662
+ !result.includes('Testing: node:test'),
663
+ 'does not include non-matching H3 entry',
664
+ );
665
+ assert.ok(
666
+ !result.includes('Deployment: blue-green'),
667
+ 'does not include non-matching H3 entry',
668
+ );
669
+ // The returned payload must be dramatically smaller than the full content
670
+ assert.ok(
671
+ result.length < singleH2Knowledge.length / 2,
672
+ `scoped result (${result.length} chars) should be <50% of full content (${singleH2Knowledge.length} chars)`,
673
+ );
674
+ });
675
+
676
+ test("single H2 with H3 entries returns empty when no H3 matches (issue #4719)", async () => {
677
+ const singleH2Knowledge = `# Project Knowledge
678
+
679
+ ## Patterns
680
+
681
+ ### Database: prepared statements
682
+ Always use prepared statements with SQLite.
683
+
684
+ ### API: versioned paths
685
+ Use /v1/resource style versioning.
686
+ `;
687
+
688
+ const result = await queryKnowledge(singleH2Knowledge, ['nonexistent']);
689
+
690
+ assert.strictEqual(result, '', 'no H3 match returns empty string');
691
+ });
692
+
693
+ test("falls back to H2 when no H3 headings exist at all", async () => {
694
+ // Backwards-compat: files with only H2 topic headers must still filter.
695
+ const h2OnlyKnowledge = `# Project Knowledge
696
+
697
+ ## Database Patterns
698
+ Use prepared statements.
699
+
700
+ ## API Design
701
+ REST with OpenAPI.
702
+ `;
703
+
704
+ const result = await queryKnowledge(h2OnlyKnowledge, ['database']);
705
+
706
+ assert.match(result, /Database Patterns/, 'H2-only file falls back to H2 filtering');
707
+ assert.ok(!result.includes('API Design'), 'non-matching H2 section excluded');
708
+ });
630
709
  });
@@ -2,6 +2,7 @@ import test from "node:test";
2
2
  import assert from "node:assert/strict";
3
3
  import { readFileSync } from "node:fs";
4
4
  import { join } from "node:path";
5
+ import { extractSourceRegion } from "./test-helpers.ts";
5
6
 
6
7
  test("copyPlanningArtifacts skips when source and destination .gsd resolve to the same path", () => {
7
8
  const srcPath = join(import.meta.dirname, "..", "auto-worktree.ts");
@@ -10,7 +11,7 @@ test("copyPlanningArtifacts skips when source and destination .gsd resolve to th
10
11
  const fnIdx = src.indexOf("function copyPlanningArtifacts");
11
12
  assert.ok(fnIdx !== -1, "copyPlanningArtifacts function exists");
12
13
 
13
- const fnBody = src.slice(fnIdx, fnIdx + 2400);
14
+ const fnBody = extractSourceRegion(src, "function copyPlanningArtifacts");
14
15
 
15
16
  const guardIdx = fnBody.indexOf("if (isSamePath(srcGsd, dstGsd)) return;");
16
17
  const copyIdx = fnBody.indexOf("safeCopyRecursive(join(srcGsd, \"milestones\")");
@@ -1,6 +1,6 @@
1
1
  import test from "node:test";
2
2
  import assert from "node:assert/strict";
3
- import { mkdirSync, rmSync, writeFileSync } from "node:fs";
3
+ import { existsSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
4
4
  import { join } from "node:path";
5
5
  import { tmpdir } from "node:os";
6
6
  import { randomUUID } from "node:crypto";
@@ -21,6 +21,7 @@ import {
21
21
  } from "../interrupted-session.ts";
22
22
  import { gsdRoot } from "../paths.ts";
23
23
  import type { GSDState } from "../types.ts";
24
+ import { _synthesizePausedSessionRecoveryForTest } from "../auto.ts";
24
25
 
25
26
  function makeTmpBase(): string {
26
27
  const base = join(tmpdir(), `gsd-test-${randomUUID()}`);
@@ -167,6 +168,54 @@ test("readPausedSessionMetadata reads paused-session metadata when present", ()
167
168
  }
168
169
  });
169
170
 
171
+ test("paused session recovery consumes JSONL without deleting the evidence file", (t) => {
172
+ const base = makeTmpBase();
173
+ t.after(() => cleanup(base));
174
+
175
+ const sessionFile = join(base, "paused-session.jsonl");
176
+ writeFileSync(
177
+ sessionFile,
178
+ [
179
+ JSON.stringify({ type: "session", id: "session-1" }),
180
+ JSON.stringify({
181
+ type: "message",
182
+ message: {
183
+ role: "assistant",
184
+ content: [
185
+ {
186
+ type: "toolCall",
187
+ name: "bash",
188
+ id: "tool-1",
189
+ arguments: { command: "echo paused" },
190
+ },
191
+ ],
192
+ },
193
+ }),
194
+ JSON.stringify({
195
+ type: "message",
196
+ message: {
197
+ role: "toolResult",
198
+ toolCallId: "tool-1",
199
+ toolName: "bash",
200
+ isError: false,
201
+ content: "paused\n",
202
+ },
203
+ }),
204
+ ].join("\n"),
205
+ "utf-8",
206
+ );
207
+
208
+ const recovery = _synthesizePausedSessionRecoveryForTest(
209
+ base,
210
+ "execute-task",
211
+ "M001/S01/T01",
212
+ sessionFile,
213
+ );
214
+
215
+ assert.equal(recovery?.trace.toolCallCount, 1);
216
+ assert.equal(existsSync(sessionFile), true, "paused JSONL must remain available after synthesis");
217
+ });
218
+
170
219
  test("readPausedSessionMetadata preserves unitType and unitId through round-trip", () => {
171
220
  const base = makeTmpBase();
172
221
  try {
@@ -481,6 +481,165 @@ describe("Custom engine loop integration", () => {
481
481
  );
482
482
  });
483
483
 
484
+ it("stops custom workflow after repeated verification retries", async () => {
485
+ _resetPendingResolve();
486
+
487
+ const runDir = makeTmpDir();
488
+ const graph = makeGraph([makeStep({ id: "retry-step" })], "retry-exhaustion");
489
+ writeGraph(runDir, graph);
490
+ writeFileSync(join(runDir, "DEFINITION.yaml"), stringify({
491
+ version: 1,
492
+ name: "retry-exhaustion",
493
+ steps: [{
494
+ id: "retry-step",
495
+ name: "retry-step",
496
+ prompt: "Do retry-step",
497
+ produces: "retry-step/output.md",
498
+ verify: { policy: "shell-command", command: "exit 1" },
499
+ }],
500
+ }));
501
+
502
+ const ctx = makeMockCtx();
503
+ const pi = makeMockPi();
504
+ const s = makeLoopSession({
505
+ activeEngineId: "custom",
506
+ activeRunDir: runDir,
507
+ basePath: runDir,
508
+ });
509
+ const deps = makeMockDeps({
510
+ stopAuto: async (_ctx, _pi, reason) => {
511
+ deps.callLog.push(`stopAuto:${reason ?? "no-reason"}`);
512
+ s.active = false;
513
+ },
514
+ });
515
+
516
+ const resolver = setInterval(() => {
517
+ resolveAgentEnd({ messages: [{ role: "assistant" }] });
518
+ }, 25);
519
+ let timeout: NodeJS.Timeout | undefined;
520
+ try {
521
+ await Promise.race([
522
+ autoLoop(ctx, pi, s, deps),
523
+ new Promise((_, reject) =>
524
+ timeout = setTimeout(() => {
525
+ s.active = false;
526
+ resolveAgentEnd({ messages: [{ role: "assistant" }] });
527
+ reject(new Error(
528
+ `autoLoop did not stop after verification retry exhaustion; calls=${pi.calls.length}; log=${deps.callLog.join(",")}`,
529
+ ));
530
+ }, 3_000),
531
+ ),
532
+ ]);
533
+ } finally {
534
+ clearInterval(resolver);
535
+ if (timeout) clearTimeout(timeout);
536
+ }
537
+
538
+ assert.equal(pi.calls.length, 4, "verification retry should be capped after four dispatched attempts");
539
+ const stopEntry = deps.callLog.find((e: string) => e.startsWith("stopAuto:"));
540
+ assert.match(stopEntry ?? "", /requested retry 4 times without passing/);
541
+ const finalGraph = readGraph(runDir);
542
+ assert.equal(finalGraph.steps[0]?.status, "active", "failed verification must not reconcile the step complete");
543
+ });
544
+
545
+ it("persists custom verification retry budget across a session restart", async () => {
546
+ _resetPendingResolve();
547
+
548
+ const runDir = makeTmpDir();
549
+ const graph = makeGraph([makeStep({ id: "retry-step" })], "retry-restart");
550
+ writeGraph(runDir, graph);
551
+ writeFileSync(join(runDir, "DEFINITION.yaml"), stringify({
552
+ version: 1,
553
+ name: "retry-restart",
554
+ steps: [{
555
+ id: "retry-step",
556
+ name: "retry-step",
557
+ prompt: "Do retry-step",
558
+ produces: "retry-step/output.md",
559
+ verify: { policy: "shell-command", command: "exit 1" },
560
+ }],
561
+ }));
562
+
563
+ const ctx1 = makeMockCtx();
564
+ const pi1 = makeMockPi();
565
+ const s1 = makeLoopSession({
566
+ activeEngineId: "custom",
567
+ activeRunDir: runDir,
568
+ basePath: runDir,
569
+ });
570
+ const deps1 = makeMockDeps();
571
+ const resolver1 = setInterval(() => {
572
+ resolveAgentEnd({ messages: [{ role: "assistant" }] });
573
+ if (pi1.calls.length >= 2) {
574
+ s1.active = false;
575
+ }
576
+ }, 25);
577
+ let timeout1: NodeJS.Timeout | undefined;
578
+ try {
579
+ await Promise.race([
580
+ autoLoop(ctx1, pi1, s1, deps1),
581
+ new Promise((_, reject) =>
582
+ timeout1 = setTimeout(() => {
583
+ s1.active = false;
584
+ resolveAgentEnd({ messages: [{ role: "assistant" }] });
585
+ reject(new Error(
586
+ `first autoLoop did not pause after two retry attempts; calls=${pi1.calls.length}; log=${deps1.callLog.join(",")}`,
587
+ ));
588
+ }, 3_000),
589
+ ),
590
+ ]);
591
+ } finally {
592
+ clearInterval(resolver1);
593
+ if (timeout1) clearTimeout(timeout1);
594
+ }
595
+ assert.equal(pi1.calls.length, 2, "first session should consume two retry attempts");
596
+ assert.equal(
597
+ deps1.callLog.some((e: string) => e.startsWith("stopAuto:")),
598
+ false,
599
+ "first session should stop because the session deactivated, not because retry budget exhausted",
600
+ );
601
+
602
+ _resetPendingResolve();
603
+ const ctx2 = makeMockCtx();
604
+ const pi2 = makeMockPi();
605
+ const s2 = makeLoopSession({
606
+ activeEngineId: "custom",
607
+ activeRunDir: runDir,
608
+ basePath: runDir,
609
+ });
610
+ const deps2 = makeMockDeps({
611
+ stopAuto: async (_ctx, _pi, reason) => {
612
+ deps2.callLog.push(`stopAuto:${reason ?? "no-reason"}`);
613
+ s2.active = false;
614
+ },
615
+ });
616
+ const resolver2 = setInterval(() => {
617
+ resolveAgentEnd({ messages: [{ role: "assistant" }] });
618
+ }, 25);
619
+ let timeout2: NodeJS.Timeout | undefined;
620
+ try {
621
+ await Promise.race([
622
+ autoLoop(ctx2, pi2, s2, deps2),
623
+ new Promise((_, reject) =>
624
+ timeout2 = setTimeout(() => {
625
+ s2.active = false;
626
+ resolveAgentEnd({ messages: [{ role: "assistant" }] });
627
+ reject(new Error(
628
+ `second autoLoop did not stop after persisted retry exhaustion; calls=${pi2.calls.length}; log=${deps2.callLog.join(",")}`,
629
+ ));
630
+ }, 3_000),
631
+ ),
632
+ ]);
633
+ } finally {
634
+ clearInterval(resolver2);
635
+ if (timeout2) clearTimeout(timeout2);
636
+ }
637
+
638
+ assert.equal(pi2.calls.length, 2, "second session should exhaust after attempts 3 and 4");
639
+ const stopEntry = deps2.callLog.find((e: string) => e.startsWith("stopAuto:"));
640
+ assert.match(stopEntry ?? "", /requested retry 4 times without passing/);
641
+ });
642
+
484
643
  it("GRAPH.yaml step stays pending when session deactivates before reconcile", async () => {
485
644
  _resetPendingResolve();
486
645
 
@@ -50,6 +50,7 @@ test("doctor-heal.md contains DB access guardrail naming gsd_milestone_status",
50
50
  const prompt = readPrompt("doctor-heal");
51
51
  assert.match(prompt, /gsd_milestone_status/, "doctor-heal.md must name gsd_milestone_status as the DB inspection tool");
52
52
  assert.match(prompt, /Do NOT query.*\.gsd\/gsd\.db/i, "doctor-heal.md must prohibit direct DB queries");
53
+ assert.doesNotMatch(prompt, /\{\{milestoneId\}\}/, "doctor-heal.md must not declare unprovided milestoneId template variables");
53
54
  });
54
55
 
55
56
  test("forensics.md contains DB inspection guardrail", () => {
@@ -351,9 +351,9 @@ skills_used: []
351
351
  const dbState = await deriveStateFromDb(base);
352
352
 
353
353
  assertStatesEqual(dbState, fileState, 'E-blocked');
354
- // With partial-dep fallback, circular deps no longer block fallback picks first eligible slice
355
- assert.deepStrictEqual(dbState.phase, 'planning', 'E-blocked: phase is planning (fallback picks a slice)');
356
- assert.ok(dbState.activeSlice !== null, 'E-blocked: activeSlice is set via fallback');
354
+ assert.deepStrictEqual(dbState.phase, 'blocked', 'E-blocked: phase is blocked when no slice deps are satisfied');
355
+ assert.deepStrictEqual(dbState.activeSlice, null, 'E-blocked: no activeSlice is selected through unmet deps');
356
+ assert.ok(dbState.blockers.some(b => b.includes('No slice eligible')), 'E-blocked: blocker explains no eligible slice');
357
357
 
358
358
  closeDatabase();
359
359
  } finally {
@@ -112,6 +112,46 @@ async function main(): Promise<void> {
112
112
  cleanup(base);
113
113
  }
114
114
 
115
+ console.log("\n=== #4974: summary-only disk milestones keep parsed title ===");
116
+
117
+ {
118
+ const summaryOnlyBase = createFixtureBase();
119
+ const summaryOnlyDbPath = join(summaryOnlyBase, ".gsd", "gsd.db");
120
+
121
+ try {
122
+ openDatabase(summaryOnlyDbPath);
123
+ insertMilestone({ id: "M001", title: "M001: Existing DB Milestone", status: "complete", depends_on: [] });
124
+
125
+ writeFile(
126
+ summaryOnlyBase,
127
+ "milestones/M002/SUMMARY.md",
128
+ `---\nid: M002\nstatus: complete\n---\n\n# M002: Summary-Only Milestone\n\nComplete.`,
129
+ );
130
+
131
+ invalidateStateCache();
132
+ const state = await deriveStateFromDb(summaryOnlyBase);
133
+ const m002Entry = state.registry.find((m) => m.id === "M002");
134
+
135
+ assertTrue(
136
+ m002Entry !== undefined,
137
+ "M002 summary-only disk milestone should appear in state.registry (#4974)",
138
+ );
139
+ assertEq(
140
+ m002Entry?.title,
141
+ "Summary-Only Milestone",
142
+ "M002 summary-only disk milestone should use parsed SUMMARY title (#4974)",
143
+ );
144
+ assertEq(
145
+ m002Entry?.status,
146
+ "complete",
147
+ "M002 summary-only disk milestone should reconcile as complete (#4974)",
148
+ );
149
+ } finally {
150
+ closeDatabase();
151
+ cleanup(summaryOnlyBase);
152
+ }
153
+ }
154
+
115
155
  report();
116
156
  }
117
157
 
@@ -14,6 +14,7 @@ import {
14
14
  getAllMilestones,
15
15
  insertSlice,
16
16
  insertTask,
17
+ getSliceTasks,
17
18
  updateTaskStatus,
18
19
  } from '../gsd-db.ts';
19
20
  // ─── Fixture Helpers ───────────────────────────────────────────────────────
@@ -270,6 +271,93 @@ describe('derive-state-db', async () => {
270
271
  }
271
272
  });
272
273
 
274
+ test('derive-state-db: partial task rows reconcile missing plan tasks before summarizing', async (t) => {
275
+ const base = createFixtureBase();
276
+ t.after(() => {
277
+ closeDatabase();
278
+ cleanup(base);
279
+ });
280
+
281
+ const partialTaskPlan = `# S01: First Slice
282
+
283
+ **Goal:** Test partial task DB reconciliation.
284
+ **Demo:** Tests pass.
285
+
286
+ ## Tasks
287
+
288
+ - [x] **T01: Existing Complete** \`est:10m\`
289
+ Already complete in DB.
290
+
291
+ - [ ] **T02: Missing Pending** \`est:10m\`
292
+ Missing from DB but present in the plan.
293
+ `;
294
+ writeFile(base, 'milestones/M001/M001-ROADMAP.md', ROADMAP_CONTENT);
295
+ writeFile(base, 'milestones/M001/slices/S01/S01-PLAN.md', partialTaskPlan);
296
+ writeFile(base, 'milestones/M001/slices/S01/tasks/.gitkeep', '');
297
+ writeFile(base, 'milestones/M001/slices/S01/tasks/T01-PLAN.md', '# T01 Plan');
298
+ writeFile(base, 'milestones/M001/slices/S01/tasks/T02-PLAN.md', '# T02 Plan');
299
+
300
+ openDatabase(':memory:');
301
+ insertMilestone({ id: 'M001', title: 'Test Milestone', status: 'active' });
302
+ insertSlice({ id: 'S01', milestoneId: 'M001', title: 'First Slice', status: 'active', risk: 'low', depends: [] });
303
+ insertTask({ id: 'T01', sliceId: 'S01', milestoneId: 'M001', title: 'Existing Complete', status: 'complete' });
304
+
305
+ invalidateStateCache();
306
+ const state = await deriveState(base);
307
+
308
+ const dbTasks = getSliceTasks('M001', 'S01');
309
+ assert.deepStrictEqual(dbTasks.length, 2, 'partial-task-db: missing T02 imported from plan');
310
+ assert.deepStrictEqual(dbTasks.find(t => t.id === 'T01')?.status, 'complete', 'partial-task-db: existing complete T01 preserved');
311
+ assert.deepStrictEqual(dbTasks.find(t => t.id === 'T02')?.status, 'pending', 'partial-task-db: missing T02 inserted pending');
312
+ assert.deepStrictEqual(state.phase, 'executing', 'partial-task-db: phase remains executing, not summarizing');
313
+ assert.deepStrictEqual(state.activeTask?.id, 'T02', 'partial-task-db: activeTask is missing plan task T02');
314
+ assert.deepStrictEqual(state.progress?.tasks, { done: 1, total: 2 }, 'partial-task-db: task progress includes reconciled plan task');
315
+ });
316
+
317
+ test('derive-state-db: empty DB disk import preserves milestone completion and dependencies', async (t) => {
318
+ const base = createFixtureBase();
319
+ t.after(() => {
320
+ closeDatabase();
321
+ cleanup(base);
322
+ });
323
+
324
+ writeFile(base, 'milestones/M001/M001-ROADMAP.md', `# M001: Foundation
325
+
326
+ **Vision:** Foundation.
327
+
328
+ ## Slices
329
+
330
+ - [x] **S01: Done** \`risk:low\` \`depends:[]\`
331
+ > After this: Done.
332
+ `);
333
+ writeFile(base, 'milestones/M001/M001-VALIDATION.md', '---\nverdict: pass\nremediation_round: 0\n---\n\nPassed.');
334
+ writeFile(base, 'milestones/M001/M001-SUMMARY.md', '# M001 Summary\n\nDone.');
335
+ writeFile(base, 'milestones/M002/M002-CONTEXT.md', '---\ndepends_on:\n - M001\n---\n\n# M002: Dependent\n');
336
+ writeFile(base, 'milestones/M002/M002-ROADMAP.md', `# M002: Dependent
337
+
338
+ **Vision:** Active work.
339
+
340
+ ## Slices
341
+
342
+ - [ ] **S01: Work** \`risk:low\` \`depends:[]\`
343
+ > After this: Done.
344
+ `);
345
+ writeFile(base, 'milestones/M003/M003-CONTEXT.md', '---\ndepends_on:\n - M002\n---\n\n# M003: Blocked\n');
346
+
347
+ openDatabase(':memory:');
348
+
349
+ invalidateStateCache();
350
+ const state = await deriveState(base);
351
+
352
+ const milestones = getAllMilestones();
353
+ assert.deepStrictEqual(milestones.find(m => m.id === 'M001')?.status, 'complete', 'disk-import: terminal summary imports M001 as complete');
354
+ assert.deepStrictEqual(milestones.find(m => m.id === 'M002')?.depends_on, ['M001'], 'disk-import: M002 depends_on imported from CONTEXT');
355
+ assert.deepStrictEqual(milestones.find(m => m.id === 'M003')?.depends_on, ['M002'], 'disk-import: M003 depends_on imported from CONTEXT');
356
+ assert.deepStrictEqual(state.activeMilestone?.id, 'M002', 'disk-import: M002 is active because M001 completion satisfies dependency');
357
+ assert.deepStrictEqual(state.registry.find(e => e.id === 'M001')?.status, 'complete', 'disk-import: M001 registry status is complete');
358
+ assert.deepStrictEqual(state.registry.find(e => e.id === 'M003')?.status, 'pending', 'disk-import: M003 stays pending on unmet M002 dependency');
359
+ });
360
+
273
361
  // ─── Test 5: Requirements counting from disk (DB no longer used for content) ─
274
362
  test('derive-state-db: requirements from disk content', async () => {
275
363
  const base = createFixtureBase();
@@ -616,10 +704,10 @@ describe('derive-state-db', async () => {
616
704
  invalidateStateCache();
617
705
  const dbState = await deriveStateFromDb(base);
618
706
 
619
- // With partial-dep fallback, circular deps no longer block fallback picks first eligible slice
620
- assert.deepStrictEqual(dbState.phase, 'planning', 'blocked-db: phase is planning (fallback picks a slice)');
707
+ assert.deepStrictEqual(dbState.phase, 'blocked', 'blocked-db: phase is blocked when no slice deps are satisfied');
621
708
  assert.deepStrictEqual(dbState.phase, fileState.phase, 'blocked-db: phase matches filesystem');
622
- assert.ok(dbState.activeSlice !== null, 'blocked-db: activeSlice is set via fallback');
709
+ assert.deepStrictEqual(dbState.activeSlice, null, 'blocked-db: no activeSlice is selected through unmet deps');
710
+ assert.ok(dbState.blockers.some(b => b.includes('No slice eligible')), 'blocked-db: blocker explains no eligible slice');
623
711
 
624
712
  closeDatabase();
625
713
  } finally {
@@ -431,7 +431,7 @@ Continue from step 2.
431
431
  cleanup(base1);
432
432
  }
433
433
 
434
- // Case B: S01 depends on nonexistent S99 truly blocked
434
+ // Case B: S01 depends on nonexistent S99 -> no slice is eligible
435
435
  const base2 = createFixtureBase();
436
436
  try {
437
437
  writeRoadmap(base2, 'M001', `# M001: Test Milestone
@@ -446,9 +446,9 @@ Continue from step 2.
446
446
 
447
447
  const state2 = await deriveState(base2);
448
448
 
449
- // With partial-dep fallback, S01 is picked despite unmet dep on S99
450
- assert.deepStrictEqual(state2.phase, 'planning', 'blocked-B: phase is planning (fallback picks S01)');
451
- assert.deepStrictEqual(state2.activeSlice?.id, 'S01', 'blocked-B: activeSlice is S01 via fallback');
449
+ assert.deepStrictEqual(state2.phase, 'blocked', 'blocked-B: phase is blocked when dependency is unsatisfied');
450
+ assert.deepStrictEqual(state2.activeSlice, null, 'blocked-B: no activeSlice selected through unmet deps');
451
+ assert.ok(state2.blockers.some(b => b.includes('No slice eligible')), 'blocked-B: blocker explains no eligible slice');
452
452
  } finally {
453
453
  cleanup(base2);
454
454
  }
@@ -11,6 +11,7 @@ import { describe, it } from 'node:test'
11
11
  import assert from 'node:assert/strict'
12
12
  import { readFileSync } from 'node:fs'
13
13
  import { resolve } from 'node:path'
14
+ import { extractSourceRegion } from "./test-helpers.ts";
14
15
 
15
16
  const template = readFileSync(
16
17
  resolve(process.cwd(), 'src', 'resources', 'extensions', 'gsd', 'prompts', 'guided-discuss-slice.md'),
@@ -37,7 +38,7 @@ describe('discuss-slice structuredQuestionsAvailable template variable', () => {
37
38
  const falseIdx = template.indexOf('`{{structuredQuestionsAvailable}}` is `false`')
38
39
  assert.ok(falseIdx !== -1)
39
40
 
40
- const afterFalse = template.slice(falseIdx, falseIdx + 300)
41
+ const afterFalse = extractSourceRegion(template, '`{{structuredQuestionsAvailable}}` is `false`')
41
42
  assert.ok(
42
43
  afterFalse.includes('plain text'),
43
44
  'when structuredQuestionsAvailable is false, questions should be in plain text',
@@ -22,6 +22,7 @@ import { join, dirname } from "node:path";
22
22
  import { fileURLToPath } from "node:url";
23
23
 
24
24
  import { DISCUSS_TOOLS_ALLOWLIST } from "../constants.ts";
25
+ import { extractSourceRegion } from "./test-helpers.ts";
25
26
 
26
27
  const __dirname = dirname(fileURLToPath(import.meta.url));
27
28
  const guidedFlowSource = readFileSync(join(__dirname, "..", "guided-flow.ts"), "utf-8");
@@ -58,7 +59,7 @@ describe("#3616 — discuss tool scoping must not leak across sessions", () => {
58
59
  );
59
60
  const newSessionStart = agentSessionSource.indexOf("async newSession(options?:");
60
61
  assert.ok(newSessionStart >= 0, "should find newSession");
61
- const body = agentSessionSource.slice(newSessionStart, newSessionStart + 3000);
62
+ const body = extractSourceRegion(agentSessionSource, "async newSession(options?:");
62
63
 
63
64
  // Both branches (cwd-changed and cwd-unchanged) must include extension tools
64
65
  assert.ok(
@@ -55,6 +55,11 @@ describe("completing-milestone dispatch guard (#4324)", () => {
55
55
 
56
56
  const classifyCall = source.indexOf("classifyMilestoneSummaryContent", summaryGuard);
57
57
  assert.ok(classifyCall > -1, "SUMMARY mismatch handling should classify summary content");
58
+ const dbGateBeforeClassify = source.indexOf("existingSummary && isDbAvailable()", summaryGuard);
59
+ assert.ok(
60
+ dbGateBeforeClassify === -1 || dbGateBeforeClassify > classifyCall,
61
+ "SUMMARY classification must not be gated on DB availability",
62
+ );
58
63
 
59
64
  const reconcileCall = source.indexOf('updateMilestoneStatus(mid, "complete"', summaryGuard);
60
65
  assert.ok(reconcileCall > -1, "successful SUMMARY should reconcile DB to complete");
@@ -255,6 +255,31 @@ test("dispatch guard skips completed milestone with SUMMARY even if it has unche
255
255
  );
256
256
  });
257
257
 
258
+ test("dispatch guard does not skip failed milestone SUMMARY without blocker prose", (t) => {
259
+ const repo = setupRepo();
260
+ t.after(() => teardownRepo(repo));
261
+
262
+ mkdirSync(join(repo, ".gsd", "milestones", "M001"), { recursive: true });
263
+ mkdirSync(join(repo, ".gsd", "milestones", "M002"), { recursive: true });
264
+
265
+ insertMilestone({ id: "M001", title: "Previous" });
266
+ insertSlice({ id: "S01", milestoneId: "M001", title: "Core", status: "complete", depends: [], sequence: 1 });
267
+ insertSlice({ id: "S02", milestoneId: "M001", title: "Unfinished", status: "pending", depends: ["S01"], sequence: 2 });
268
+
269
+ insertMilestone({ id: "M002", title: "Current" });
270
+ insertSlice({ id: "S01", milestoneId: "M002", title: "Start", status: "pending", depends: [], sequence: 1 });
271
+
272
+ writeFileSync(join(repo, ".gsd", "milestones", "M001", "M001-ROADMAP.md"), "# M001\n");
273
+ writeFileSync(join(repo, ".gsd", "milestones", "M001", "M001-SUMMARY.md"),
274
+ "---\nstatus: failed\n---\n# M001 Summary\nRecovery stopped.\n");
275
+ writeFileSync(join(repo, ".gsd", "milestones", "M002", "M002-ROADMAP.md"), "# M002\n");
276
+
277
+ assert.equal(
278
+ getPriorSliceCompletionBlocker(repo, "main", "plan-slice", "M002/S01"),
279
+ "Cannot dispatch plan-slice M002/S01: earlier slice M001/S02 is not complete.",
280
+ );
281
+ });
282
+
258
283
  test("dispatch guard works without git repo", (t) => {
259
284
  const repo = setupRepo();
260
285
  t.after(() => teardownRepo(repo));
@@ -57,6 +57,17 @@ function scaffoldSlicePlan(basePath: string, mid: string, sid: string): void {
57
57
  ].join("\n"));
58
58
  }
59
59
 
60
+ function scaffoldMilestoneContext(basePath: string, mid: string): void {
61
+ const dir = join(basePath, ".gsd", "milestones", mid);
62
+ mkdirSync(dir, { recursive: true });
63
+ writeFileSync(join(dir, `${mid}-CONTEXT.md`), [
64
+ `# ${mid}: Test Milestone`,
65
+ "",
66
+ "Context for dispatch recovery tests.",
67
+ "",
68
+ ].join("\n"));
69
+ }
70
+
60
71
  function scaffoldTaskPlan(basePath: string, mid: string, sid: string, tid: string): void {
61
72
  const dir = join(basePath, ".gsd", "milestones", mid, "slices", sid, "tasks");
62
73
  mkdirSync(dir, { recursive: true });
@@ -76,6 +87,7 @@ test("dispatch: missing task plan triggers plan-slice (not stop) — issue #909"
76
87
  t.after(() => rmSync(tmp, { recursive: true, force: true }));
77
88
 
78
89
  // Slice plan exists with tasks, but tasks/ directory is empty
90
+ scaffoldMilestoneContext(tmp, "M002");
79
91
  scaffoldSlicePlan(tmp, "M002", "S03");
80
92
 
81
93
  const ctx = makeContext(tmp);
@@ -92,6 +104,7 @@ test("dispatch: present task plan proceeds to execute-task normally", async (t)
92
104
  const tmp = mkdtempSync(join(tmpdir(), "gsd-909-ok-"));
93
105
  t.after(() => rmSync(tmp, { recursive: true, force: true }));
94
106
 
107
+ scaffoldMilestoneContext(tmp, "M002");
95
108
  scaffoldSlicePlan(tmp, "M002", "S03");
96
109
  scaffoldTaskPlan(tmp, "M002", "S03", "T01");
97
110
 
@@ -111,6 +124,7 @@ test("dispatch: plan-slice recovery loop — second call after plan-slice still
111
124
  const tmp = mkdtempSync(join(tmpdir(), "gsd-909-loop-"));
112
125
  t.after(() => rmSync(tmp, { recursive: true, force: true }));
113
126
 
127
+ scaffoldMilestoneContext(tmp, "M002");
114
128
  scaffoldSlicePlan(tmp, "M002", "S03");
115
129
 
116
130
  const ctx = makeContext(tmp);