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
@@ -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
  const autoSrc = readFileSync(join(import.meta.dirname, "..", "auto.ts"), "utf-8");
7
8
  const phasesSrc = readFileSync(join(import.meta.dirname, "..", "auto", "phases.ts"), "utf-8");
@@ -20,7 +21,7 @@ test("stopAuto restores original thinking level", () => {
20
21
  test("runUnitPhase threads captured thinking level into selectAndApplyModel", () => {
21
22
  const callIdx = phasesSrc.indexOf("deps.selectAndApplyModel(");
22
23
  assert.ok(callIdx > -1, "phases.ts should call selectAndApplyModel");
23
- const callBlock = phasesSrc.slice(callIdx, callIdx + 600);
24
+ const callBlock = extractSourceRegion(phasesSrc, "deps.selectAndApplyModel(");
24
25
  assert.ok(
25
26
  callBlock.includes("s.autoModeStartThinkingLevel"),
26
27
  "runUnitPhase should pass autoModeStartThinkingLevel to selectAndApplyModel",
@@ -30,7 +31,7 @@ test("runUnitPhase threads captured thinking level into selectAndApplyModel", ()
30
31
  test("hook model override preserves captured thinking level", () => {
31
32
  const hookIdx = phasesSrc.indexOf("const hookModelOverride = sidecarItem?.model ?? iterData.hookModelOverride;");
32
33
  assert.ok(hookIdx > -1, "phases.ts should include hook model override handling");
33
- const hookBlock = phasesSrc.slice(hookIdx, hookIdx + 600);
34
+ const hookBlock = extractSourceRegion(phasesSrc, "const hookModelOverride = sidecarItem?.model ?? iterData.hookModelOverride;");
34
35
  assert.ok(
35
36
  hookBlock.includes("pi.setThinkingLevel(s.autoModeStartThinkingLevel)"),
36
37
  "hook model override should re-apply captured thinking level after setModel",
@@ -27,6 +27,7 @@ import { readFileSync } from "node:fs";
27
27
  import { join } from "node:path";
28
28
 
29
29
  import { buildFlatRateContext } from "../auto-model-selection.ts";
30
+ import { extractSourceRegion } from "./test-helpers.ts";
30
31
 
31
32
  // ─── Bug 2: this-binding regression ─────────────────────────────────────
32
33
 
@@ -69,7 +70,7 @@ test("isSamePath short-circuits ENOENT before logging a warning", () => {
69
70
  assert.ok(fnIdx !== -1, "isSamePath function exists");
70
71
 
71
72
  // Grab the function body (enough to cover the catch block).
72
- const fnBody = src.slice(fnIdx, fnIdx + 600);
73
+ const fnBody = extractSourceRegion(src, "function isSamePath", { fromIdx: fnIdx });
73
74
 
74
75
  const catchIdx = fnBody.indexOf("catch");
75
76
  assert.ok(catchIdx !== -1, "isSamePath has a catch block");
@@ -103,7 +104,7 @@ test("checkAutoStartAfterDiscuss guards DISCUSSION-MANIFEST.json unlink with exi
103
104
 
104
105
  // Everything from the comment to a short distance below should contain
105
106
  // the existsSync guard before the unlinkSync call.
106
- const block = src.slice(cleanupIdx, cleanupIdx + 400);
107
+ const block = extractSourceRegion(src, "remove discussion manifest after auto-start", { fromIdx: cleanupIdx });
107
108
 
108
109
  const existsIdx = block.indexOf("existsSync(manifestPath)");
109
110
  const unlinkIdx = block.indexOf("unlinkSync(manifestPath)");
@@ -2,6 +2,7 @@ import { describe, 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
  const systemContextSrc = readFileSync(
7
8
  join(import.meta.dirname, "..", "bootstrap", "system-context.ts"),
@@ -29,7 +30,7 @@ describe("bootstrap deriveState DB guards (#3844)", () => {
29
30
  test("register-hooks opens DB before deriveState in session_before_compact", () => {
30
31
  const compactIdx = registerHooksSrc.indexOf('pi.on("session_before_compact"');
31
32
  assert.ok(compactIdx > -1, "register-hooks should define session_before_compact");
32
- const compactSection = registerHooksSrc.slice(compactIdx, compactIdx + 1600);
33
+ const compactSection = extractSourceRegion(registerHooksSrc, 'pi.on("session_before_compact"');
33
34
  const ensureIdx = compactSection.indexOf("ensureDbOpen()");
34
35
  const deriveIdx = compactSection.indexOf("deriveState(basePath)");
35
36
  assert.ok(ensureIdx > -1, "session_before_compact should call ensureDbOpen()");
@@ -69,11 +69,11 @@ describe("cache-staleness-regression", () => {
69
69
  ].join('\n');
70
70
  writeMilestoneFile(base, 'M001', 'ROADMAP', roadmap);
71
71
 
72
- // Step 3: WITHOUT invalidation, the old state might be cached
73
- // The state cache has a 100ms TTL, so wait just past it
74
- await new Promise(r => setTimeout(r, 150));
75
-
76
- // Step 4: Invalidate and re-derive — should see the new roadmap
72
+ // Step 3: Explicit invalidation — this is the #1240 fix path. We
73
+ // do NOT rely on the 100ms TTL here; the production code calls
74
+ // invalidateAllCaches() / invalidateStateCache() immediately after
75
+ // writing planning files, so the next deriveState() must see the
76
+ // new roadmap without any wall-clock wait.
77
77
  invalidateAllCaches();
78
78
  invalidateStateCache();
79
79
  const state2 = await deriveState(base);
@@ -100,10 +100,8 @@ describe("cache-staleness-regression", () => {
100
100
  // Simulate: discussion completes, CONTEXT.md is written
101
101
  writeMilestoneFile(base, 'M001', 'CONTEXT', '# M001: Test\n\nFull context after discussion.\n');
102
102
 
103
- // Wait past TTL
104
- await new Promise(r => setTimeout(r, 150));
105
-
106
- // Without invalidation, we'd still see 'needs-discussion'
103
+ // Explicit invalidation is the production fix path for #1249 —
104
+ // no wall-clock wait needed.
107
105
  invalidateAllCaches();
108
106
  invalidateStateCache();
109
107
  const state2 = await deriveState(base);
@@ -116,7 +114,7 @@ describe("cache-staleness-regression", () => {
116
114
  }
117
115
  });
118
116
 
119
- test("state cache TTL: fresh reads after 100ms", async () => {
117
+ test("state cache TTL: within window returns cached; past window re-derives", async () => {
120
118
  const base = createBase();
121
119
  try {
122
120
  writeMilestoneFile(base, 'M001', 'CONTEXT', '# M001\n\nDesc.\n');
@@ -126,7 +124,7 @@ describe("cache-staleness-regression", () => {
126
124
  const state1 = await deriveState(base);
127
125
  assert.strictEqual(state1.phase, 'pre-planning', 'initial: pre-planning');
128
126
 
129
- // Write roadmap immediately
127
+ // Write roadmap immediately — no invalidation
130
128
  writeMilestoneFile(base, 'M001', 'ROADMAP', [
131
129
  '# M001: Test',
132
130
  '',
@@ -136,18 +134,19 @@ describe("cache-staleness-regression", () => {
136
134
  '',
137
135
  ].join('\n'));
138
136
 
139
- // Immediately after writing (within 100ms TTL), the cache might be stale
137
+ // Within the TTL window, deriveState() must return the cached
138
+ // pre-planning state — this is the "cached" half of the TTL
139
+ // contract and the reason invalidateStateCache() exists.
140
140
  const state2 = await deriveState(base);
141
- // This MAY still show pre-planning if within TTL that's expected behavior
141
+ assert.strictEqual(state2.phase, 'pre-planning', 'within TTL: cached pre-planning is returned');
142
142
 
143
- // Wait past TTL
143
+ // Past the TTL + explicit parse-cache flush, the fresh derive must
144
+ // see the new roadmap. invalidateAllCaches() is required because
145
+ // the file-parse cache is independent of the state TTL.
144
146
  await new Promise(r => setTimeout(r, 150));
145
-
146
- // ALSO invalidate parse cache (not just state cache)
147
147
  invalidateAllCaches();
148
- invalidateStateCache();
149
148
  const state3 = await deriveState(base);
150
- assert.strictEqual(state3.phase, 'planning', 'after TTL expiry + invalidation → planning');
149
+ assert.strictEqual(state3.phase, 'planning', 'past TTL: re-derive sees new roadmap');
151
150
  } finally {
152
151
  cleanup(base);
153
152
  }
@@ -194,7 +193,6 @@ describe("cache-staleness-regression", () => {
194
193
  '- [ ] **T02: Second Task** `est:1h`',
195
194
  ].join('\n'));
196
195
 
197
- await new Promise(r => setTimeout(r, 150));
198
196
  invalidateAllCaches();
199
197
  invalidateStateCache();
200
198
  const state2 = await deriveState(base);
@@ -242,7 +240,6 @@ describe("cache-staleness-regression", () => {
242
240
  '- [x] **T01: Task** `est:1h`',
243
241
  ].join('\n'));
244
242
 
245
- await new Promise(r => setTimeout(r, 150));
246
243
  invalidateAllCaches();
247
244
  invalidateStateCache();
248
245
  const state2 = await deriveState(base);
@@ -282,7 +279,6 @@ describe("cache-staleness-regression", () => {
282
279
  '',
283
280
  ].join('\n'));
284
281
 
285
- await new Promise(r => setTimeout(r, 150));
286
282
  invalidateAllCaches();
287
283
  invalidateStateCache();
288
284
  const state2 = await deriveState(base);
@@ -0,0 +1,108 @@
1
+ /**
2
+ * Tests for resolveCanonicalMilestoneRoot — the worktree-aware reader
3
+ * that fixes #4761 (worktree work stranded when auto-loop exits without
4
+ * milestone completion).
5
+ *
6
+ * Contract: given (basePath, milestoneId), return the worktree path if a
7
+ * live git worktree exists for that milestone at .gsd/worktrees/<MID>/;
8
+ * otherwise return basePath unchanged. A live worktree has a .git file
9
+ * (not directory) — a bare directory without .git is a stale leftover.
10
+ */
11
+
12
+ import test from "node:test";
13
+ import assert from "node:assert/strict";
14
+ import { mkdirSync, writeFileSync, rmSync } from "node:fs";
15
+ import { join } from "node:path";
16
+ import { tmpdir } from "node:os";
17
+ import { randomUUID } from "node:crypto";
18
+
19
+ import { resolveCanonicalMilestoneRoot } from "../worktree-manager.ts";
20
+
21
+ function makeTmpBase(): string {
22
+ const base = join(tmpdir(), `gsd-canon-test-${randomUUID()}`);
23
+ mkdirSync(join(base, ".gsd", "milestones"), { recursive: true });
24
+ return base;
25
+ }
26
+
27
+ function cleanup(base: string): void {
28
+ try { rmSync(base, { recursive: true, force: true }); } catch { /* */ }
29
+ }
30
+
31
+ /**
32
+ * Create a worktree directory shape that looks live: the .gsd/worktrees/<MID>/
33
+ * directory with a .git file containing a gitdir: pointer. We don't need a
34
+ * real git worktree — the resolver only checks for the .git file's presence.
35
+ */
36
+ function makeLiveWorktree(base: string, mid: string): string {
37
+ const wtPath = join(base, ".gsd", "worktrees", mid);
38
+ mkdirSync(wtPath, { recursive: true });
39
+ writeFileSync(
40
+ join(wtPath, ".git"),
41
+ `gitdir: ${join(base, ".git", "worktrees", mid)}\n`,
42
+ );
43
+ return wtPath;
44
+ }
45
+
46
+ function makeStaleWorktree(base: string, mid: string): string {
47
+ const wtPath = join(base, ".gsd", "worktrees", mid);
48
+ mkdirSync(wtPath, { recursive: true });
49
+ // No .git file — this is the stale-leftover shape createWorktree() sees
50
+ // and cleans up.
51
+ return wtPath;
52
+ }
53
+
54
+ test("returns worktree path when a live worktree exists for the milestone", () => {
55
+ const base = makeTmpBase();
56
+ try {
57
+ const wtPath = makeLiveWorktree(base, "M001");
58
+ const result = resolveCanonicalMilestoneRoot(base, "M001");
59
+ assert.equal(result, wtPath);
60
+ } finally {
61
+ cleanup(base);
62
+ }
63
+ });
64
+
65
+ test("returns basePath when no worktree directory exists", () => {
66
+ const base = makeTmpBase();
67
+ try {
68
+ const result = resolveCanonicalMilestoneRoot(base, "M001");
69
+ assert.equal(result, base);
70
+ } finally {
71
+ cleanup(base);
72
+ }
73
+ });
74
+
75
+ test("returns basePath when worktree directory exists but has no .git file (stale)", () => {
76
+ const base = makeTmpBase();
77
+ try {
78
+ makeStaleWorktree(base, "M001");
79
+ const result = resolveCanonicalMilestoneRoot(base, "M001");
80
+ assert.equal(result, base);
81
+ } finally {
82
+ cleanup(base);
83
+ }
84
+ });
85
+
86
+ test("returns basePath for invalid milestoneId (path separators)", () => {
87
+ const base = makeTmpBase();
88
+ try {
89
+ // Even if a worktree coincidentally exists, the guard should reject.
90
+ assert.equal(resolveCanonicalMilestoneRoot(base, "../evil"), base);
91
+ assert.equal(resolveCanonicalMilestoneRoot(base, "M001/subdir"), base);
92
+ assert.equal(resolveCanonicalMilestoneRoot(base, "M001\\subdir"), base);
93
+ assert.equal(resolveCanonicalMilestoneRoot(base, ""), base);
94
+ } finally {
95
+ cleanup(base);
96
+ }
97
+ });
98
+
99
+ test("only returns the worktree for the requested milestone, not siblings", () => {
100
+ const base = makeTmpBase();
101
+ try {
102
+ makeLiveWorktree(base, "M001");
103
+ const result = resolveCanonicalMilestoneRoot(base, "M002");
104
+ assert.equal(result, base, "M002 has no worktree → basePath");
105
+ } finally {
106
+ cleanup(base);
107
+ }
108
+ });
@@ -0,0 +1,263 @@
1
+ // GSD-2 — #4780: slice-summary excerpts replace full inlining in
2
+ // buildCompleteMilestonePrompt. Verify (a) the excerpt helper emits
3
+ // frontmatter fields + section heads + on-demand path, (b) the closer
4
+ // prompt lists all slice SUMMARY paths under "On-demand Slice Summaries",
5
+ // (c) regression on prompt size.
6
+
7
+ import test from "node:test";
8
+ import assert from "node:assert/strict";
9
+ import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from "node:fs";
10
+ import { join } from "node:path";
11
+ import { tmpdir } from "node:os";
12
+
13
+ import { buildSliceSummaryExcerpt, buildCompleteMilestonePrompt } from "../auto-prompts.ts";
14
+ import { invalidateAllCaches } from "../cache.ts";
15
+
16
+ // ─── Fixture helpers ──────────────────────────────────────────────────────
17
+
18
+ function createBase(): string {
19
+ const base = mkdtempSync(join(tmpdir(), "gsd-cm-excerpt-"));
20
+ mkdirSync(join(base, ".gsd", "milestones", "M001", "slices", "S01", "tasks"), { recursive: true });
21
+ mkdirSync(join(base, ".gsd", "milestones", "M001", "slices", "S02", "tasks"), { recursive: true });
22
+ return base;
23
+ }
24
+
25
+ function cleanup(base: string): void {
26
+ rmSync(base, { recursive: true, force: true });
27
+ }
28
+
29
+ function writeRoadmap(base: string, content: string): void {
30
+ writeFileSync(join(base, ".gsd", "milestones", "M001", "M001-ROADMAP.md"), content);
31
+ }
32
+
33
+ function writeSummary(base: string, sid: string, content: string): void {
34
+ writeFileSync(
35
+ join(base, ".gsd", "milestones", "M001", "slices", sid, `${sid}-SUMMARY.md`),
36
+ content,
37
+ );
38
+ }
39
+
40
+ // A summary with enough body narrative that full inlining would balloon the
41
+ // prompt. The excerpt should keep frontmatter + sections but drop the
42
+ // "What Happened" narrative.
43
+ function makeFatSummary(sid: string): string {
44
+ const narrativePara =
45
+ "The team discovered several subtle integration issues, traced them to the cache layer, and produced a patch set that threads the cache key through every call site. ".repeat(20);
46
+ return [
47
+ "---",
48
+ `id: ${sid}`,
49
+ "parent: M001",
50
+ "milestone: M001",
51
+ "provides:",
52
+ " - compact slice-summary excerpts",
53
+ " - on-demand read path registry",
54
+ "affects:",
55
+ " - complete-milestone prompt builder",
56
+ "key_decisions:",
57
+ " - use parseSummary for frontmatter extraction",
58
+ " - fall back to full inline when frontmatter fails",
59
+ "patterns_established:",
60
+ " - excerpt-first inlining for closer units",
61
+ "key_files:",
62
+ " - src/resources/extensions/gsd/auto-prompts.ts",
63
+ "duration: 1h",
64
+ "verification_result: passed",
65
+ "completed_at: 2026-04-24",
66
+ "blocker_discovered: false",
67
+ "---",
68
+ "",
69
+ `# ${sid}: Slice summary`,
70
+ "**Short one-liner for the slice**",
71
+ "",
72
+ "## What Happened",
73
+ "",
74
+ narrativePara,
75
+ "",
76
+ "## Deviations",
77
+ "",
78
+ "Extended the excerpt helper scope at review time.",
79
+ "",
80
+ "## Known Limitations",
81
+ "",
82
+ "Does not yet cover validate-milestone — follow-up.",
83
+ "",
84
+ "## Follow-ups",
85
+ "",
86
+ "- Wire the same excerpt into buildValidateMilestonePrompt",
87
+ ].join("\n");
88
+ }
89
+
90
+ function makeRoadmap(): string {
91
+ return [
92
+ "# M001 Roadmap",
93
+ "## Slices",
94
+ "- [x] **S01: Excerpt helper** `risk:medium` `depends:[]`",
95
+ "- [x] **S02: Closer wiring** `risk:low` `depends:[S01]`",
96
+ ].join("\n");
97
+ }
98
+
99
+ // ─── buildSliceSummaryExcerpt unit tests ──────────────────────────────────
100
+
101
+ test("#4780 excerpt: emits compact block with frontmatter fields + section heads", async (t) => {
102
+ const base = createBase();
103
+ t.after(() => cleanup(base));
104
+ invalidateAllCaches();
105
+
106
+ const absPath = join(base, ".gsd", "milestones", "M001", "slices", "S01", "S01-SUMMARY.md");
107
+ const relPath = ".gsd/milestones/M001/slices/S01/S01-SUMMARY.md";
108
+ writeSummary(base, "S01", makeFatSummary("S01"));
109
+
110
+ const out = await buildSliceSummaryExcerpt(absPath, relPath, "S01");
111
+
112
+ // Compact header with source path for on-demand Read
113
+ assert.match(out, /### S01 Summary \(excerpt\)/);
114
+ assert.match(out, /Source: `\.gsd\/milestones\/M001\/slices\/S01\/S01-SUMMARY\.md`/);
115
+
116
+ // Frontmatter fields surfaced
117
+ assert.match(out, /\*\*Title:\*\* S01: Slice summary/);
118
+ assert.match(out, /\*\*One-liner:\*\*/);
119
+ assert.match(out, /\*\*Verification:\*\* `passed`/);
120
+ assert.match(out, /\*\*Blockers:\*\* none/);
121
+ assert.match(out, /\*\*Provides:\*\* compact slice-summary excerpts;/);
122
+ assert.match(out, /\*\*Key decisions:\*\* use parseSummary/);
123
+ assert.match(out, /\*\*Patterns established:\*\* excerpt-first inlining/);
124
+
125
+ // Section heads included (body-section markdown), not whole sections inlined
126
+ assert.match(out, /#### Deviations/);
127
+ assert.match(out, /#### Known limitations/);
128
+ assert.match(out, /#### Follow-ups/);
129
+
130
+ // On-demand instruction present
131
+ assert.match(out, /On-demand.*read.*for the full "What Happened"/);
132
+
133
+ // Bulk narrative is NOT inlined — excerpt is meaningfully shorter than full
134
+ // A 20x-repeated paragraph produces ~2.5KB; excerpt should come in well under.
135
+ const fullSize = makeFatSummary("S01").length;
136
+ assert.ok(
137
+ out.length < fullSize * 0.6,
138
+ `excerpt length ${out.length} should be < 60% of full summary length ${fullSize}`,
139
+ );
140
+ });
141
+
142
+ test("#4780 excerpt: blocker_discovered=true surfaces prominent marker", async (t) => {
143
+ const base = createBase();
144
+ t.after(() => cleanup(base));
145
+ invalidateAllCaches();
146
+
147
+ const content = [
148
+ "---",
149
+ "id: S01",
150
+ "parent: M001",
151
+ "milestone: M001",
152
+ "blocker_discovered: true",
153
+ "---",
154
+ "# S01",
155
+ "**One-liner**",
156
+ "",
157
+ "## What Happened",
158
+ "content",
159
+ ].join("\n");
160
+ const absPath = join(base, ".gsd", "milestones", "M001", "slices", "S01", "S01-SUMMARY.md");
161
+ writeSummary(base, "S01", content);
162
+
163
+ const out = await buildSliceSummaryExcerpt(absPath, "rel", "S01");
164
+ assert.match(out, /Blockers:\*\* ⚠️ blocker recorded/);
165
+ });
166
+
167
+ test("#4780 excerpt: fall back to full inline when frontmatter is unrecognizable", async (t) => {
168
+ const base = createBase();
169
+ t.after(() => cleanup(base));
170
+ invalidateAllCaches();
171
+
172
+ // No frontmatter, no id — parser returns empty id, triggering fallback
173
+ const garbage = "# S99\n\nJust a wall of text with no frontmatter at all.\n";
174
+ const absPath = join(base, ".gsd", "milestones", "M001", "slices", "S01", "S99-SUMMARY.md");
175
+ writeFileSync(absPath, garbage);
176
+
177
+ const out = await buildSliceSummaryExcerpt(absPath, "rel/path.md", "S99");
178
+ // Full content preserved (no excerpt wrapper), no data-loss
179
+ assert.match(out, /Just a wall of text/);
180
+ assert.match(out, /### S99 Summary/);
181
+ });
182
+
183
+ test("#4780 excerpt: missing file reports not-found fallback", async (t) => {
184
+ const base = createBase();
185
+ t.after(() => cleanup(base));
186
+
187
+ const out = await buildSliceSummaryExcerpt(null, "rel/missing.md", "S42");
188
+ assert.match(out, /### S42 Summary \(excerpt\)/);
189
+ assert.match(out, /not found — file does not exist yet/);
190
+ });
191
+
192
+ test("#4780 excerpt: section bodies are capped (coderabbit review)", async (t) => {
193
+ const base = createBase();
194
+ t.after(() => cleanup(base));
195
+ invalidateAllCaches();
196
+
197
+ // Long Follow-ups section (~4.8KB) would balloon the excerpt without
198
+ // the cap — regression coverage for the coderabbit finding on #4908.
199
+ const longFollowUps = "A verbose follow-up bullet that keeps restating the same point. ".repeat(60);
200
+ const content = [
201
+ "---",
202
+ "id: S01",
203
+ "parent: M001",
204
+ "milestone: M001",
205
+ "---",
206
+ "# S01: Test",
207
+ "**One-liner**",
208
+ "",
209
+ "## Follow-ups",
210
+ longFollowUps,
211
+ ].join("\n");
212
+ const absPath = join(base, ".gsd", "milestones", "M001", "slices", "S01", "S01-SUMMARY.md");
213
+ writeSummary(base, "S01", content);
214
+
215
+ const out = await buildSliceSummaryExcerpt(absPath, "rel/path.md", "S01");
216
+
217
+ assert.match(out, /\(truncated — see full `rel\/path\.md`\)/);
218
+ assert.ok(
219
+ out.length < 2000,
220
+ `excerpt length ${out.length} should be well under 2KB when one section hits the cap`,
221
+ );
222
+ });
223
+
224
+ // ─── buildCompleteMilestonePrompt integration test ─────────────────────────
225
+
226
+ test("#4780 closer prompt: uses excerpts + lists on-demand slice SUMMARY paths", async (t) => {
227
+ const base = createBase();
228
+ t.after(() => cleanup(base));
229
+ invalidateAllCaches();
230
+
231
+ writeRoadmap(base, makeRoadmap());
232
+ writeSummary(base, "S01", makeFatSummary("S01"));
233
+ writeSummary(base, "S02", makeFatSummary("S02"));
234
+
235
+ const prompt = await buildCompleteMilestonePrompt("M001", "Test Milestone", base);
236
+
237
+ // Excerpt markers present for each slice
238
+ assert.match(prompt, /### S01 Summary \(excerpt\)/);
239
+ assert.match(prompt, /### S02 Summary \(excerpt\)/);
240
+
241
+ // On-demand path section exists with both slice paths
242
+ assert.match(prompt, /### On-demand Slice Summaries/);
243
+ assert.match(prompt, /S01-SUMMARY\.md/);
244
+ assert.match(prompt, /S02-SUMMARY\.md/);
245
+
246
+ // Fat narrative (the 20x-repeated paragraph) is NOT inlined
247
+ assert.ok(
248
+ !prompt.includes("threads the cache key through every call site."),
249
+ "closer prompt must not inline full 'What Happened' narrative after #4780",
250
+ );
251
+
252
+ // Prompt size is bounded — the two fat summaries' narratives alone would
253
+ // have exceeded ~4KB each. Post-fix closer prompt should be meaningfully
254
+ // smaller than their combined raw size.
255
+ const rawSize = makeFatSummary("S01").length + makeFatSummary("S02").length;
256
+ // Prompt includes roadmap, templates, and other inlines, so it may still
257
+ // be sizable — the guard is specifically that the fat narrative is gone.
258
+ // Use a soft bound: prompt - overhead should be less than 2x one summary.
259
+ assert.ok(
260
+ prompt.length < rawSize + 20_000,
261
+ `closer prompt length ${prompt.length} should be < raw summary size ${rawSize} + 20KB headroom`,
262
+ );
263
+ });
@@ -153,6 +153,31 @@ describe("complete-milestone", () => {
153
153
  );
154
154
  });
155
155
 
156
+ test("prompt does not hard-fail main self-diff as missing implementation (#4699)", () => {
157
+ const prompt = loadPromptFromWorktree("complete-milestone", {
158
+ workingDirectory: "/tmp/test-project",
159
+ milestoneId: "M001",
160
+ milestoneTitle: "Main Retry Test",
161
+ roadmapPath: ".gsd/milestones/M001/M001-ROADMAP.md",
162
+ inlinedContext: "context",
163
+ });
164
+
165
+ assert.ok(
166
+ !prompt.includes("git diff --stat HEAD $(git merge-base HEAD main) -- ':!.gsd/'"),
167
+ "prompt must not require the known self-diff command from #4699",
168
+ );
169
+ assert.match(
170
+ prompt,
171
+ /self-diff/i,
172
+ "prompt should explicitly guard retries where HEAD and the integration branch are the same commit",
173
+ );
174
+ assert.match(
175
+ prompt,
176
+ /GSD-(?:Task|Unit)/,
177
+ "prompt should direct main-branch retries toward milestone-scoped GSD commit evidence",
178
+ );
179
+ });
180
+
156
181
  test("handleCompleteMilestone rejects when verificationPassed is false", async () => {
157
182
  const { handleCompleteMilestone } = await import("../tools/complete-milestone.ts");
158
183
  const base = createFixtureBase();