gsd-pi 2.80.0-dev.c5f2443b3 → 2.80.0-dev.cf9433f56

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 (443) hide show
  1. package/README.md +4 -2
  2. package/dist/resources/.managed-resources-content-hash +1 -1
  3. package/dist/resources/GSD-WORKFLOW.md +2 -2
  4. package/dist/resources/extensions/github-sync/templates.js +39 -8
  5. package/dist/resources/extensions/gsd/auto/loop.js +48 -10
  6. package/dist/resources/extensions/gsd/auto/phases.js +66 -45
  7. package/dist/resources/extensions/gsd/auto/resolve.js +17 -0
  8. package/dist/resources/extensions/gsd/auto/run-unit.js +32 -16
  9. package/dist/resources/extensions/gsd/auto-dashboard.js +51 -15
  10. package/dist/resources/extensions/gsd/auto-dispatch.js +10 -0
  11. package/dist/resources/extensions/gsd/auto-post-unit.js +10 -10
  12. package/dist/resources/extensions/gsd/auto-prompts.js +124 -2
  13. package/dist/resources/extensions/gsd/auto-recovery.js +197 -9
  14. package/dist/resources/extensions/gsd/auto-start.js +2 -3
  15. package/dist/resources/extensions/gsd/auto-supervisor.js +8 -1
  16. package/dist/resources/extensions/gsd/auto-timeout-recovery.js +2 -2
  17. package/dist/resources/extensions/gsd/auto.js +77 -5
  18. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +36 -3
  19. package/dist/resources/extensions/gsd/bootstrap/exec-tools.js +27 -20
  20. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +32 -1
  21. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +129 -1
  22. package/dist/resources/extensions/gsd/clean-root-preflight.js +42 -4
  23. package/dist/resources/extensions/gsd/commands/dispatcher.js +5 -0
  24. package/dist/resources/extensions/gsd/commands-extract-learnings.js +17 -12
  25. package/dist/resources/extensions/gsd/context-budget.js +37 -2
  26. package/dist/resources/extensions/gsd/crash-recovery.js +56 -10
  27. package/dist/resources/extensions/gsd/custom-workflow-engine.js +22 -2
  28. package/dist/resources/extensions/gsd/db/unit-dispatches.js +39 -0
  29. package/dist/resources/extensions/gsd/db-base-schema.js +18 -2
  30. package/dist/resources/extensions/gsd/db-migration-steps.js +22 -0
  31. package/dist/resources/extensions/gsd/detection.js +106 -0
  32. package/dist/resources/extensions/gsd/git-service.js +36 -4
  33. package/dist/resources/extensions/gsd/graph.js +9 -3
  34. package/dist/resources/extensions/gsd/gsd-db.js +146 -13
  35. package/dist/resources/extensions/gsd/guided-flow.js +82 -16
  36. package/dist/resources/extensions/gsd/memory-store.js +69 -12
  37. package/dist/resources/extensions/gsd/migrate/command.js +40 -1
  38. package/dist/resources/extensions/gsd/migration-auto-check.js +87 -0
  39. package/dist/resources/extensions/gsd/planning-path-scope.js +26 -0
  40. package/dist/resources/extensions/gsd/pr-evidence.js +57 -16
  41. package/dist/resources/extensions/gsd/pre-execution-checks.js +7 -0
  42. package/dist/resources/extensions/gsd/prompt-loader.js +28 -2
  43. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +19 -19
  44. package/dist/resources/extensions/gsd/prompts/parallel-research-slices.md +1 -1
  45. package/dist/resources/extensions/gsd/prompts/plan-milestone.md +3 -1
  46. package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  47. package/dist/resources/extensions/gsd/prompts/quick-task.md +1 -5
  48. package/dist/resources/extensions/gsd/prompts/validate-milestone.md +2 -2
  49. package/dist/resources/extensions/gsd/quick.js +34 -2
  50. package/dist/resources/extensions/gsd/safety/evidence-collector.js +10 -2
  51. package/dist/resources/extensions/gsd/tools/context-mode-tool-result.js +15 -0
  52. package/dist/resources/extensions/gsd/tools/exec-search-tool.js +5 -0
  53. package/dist/resources/extensions/gsd/tools/exec-tool.js +3 -15
  54. package/dist/resources/extensions/gsd/tools/memory-tools.js +1 -0
  55. package/dist/resources/extensions/gsd/tools/plan-slice.js +9 -0
  56. package/dist/resources/extensions/gsd/tools/plan-task.js +9 -0
  57. package/dist/resources/extensions/gsd/tools/resume-tool.js +5 -0
  58. package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +1 -1
  59. package/dist/resources/extensions/gsd/unit-context-composer.js +12 -3
  60. package/dist/resources/extensions/gsd/unit-runtime.js +22 -0
  61. package/dist/resources/extensions/gsd/working-output-messages.js +64 -0
  62. package/dist/resources/extensions/gsd/worktree-manager.js +16 -14
  63. package/dist/resources/extensions/gsd/worktree-resolver.js +33 -17
  64. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  65. package/dist/web/standalone/.next/BUILD_ID +1 -1
  66. package/dist/web/standalone/.next/app-path-routes-manifest.json +10 -10
  67. package/dist/web/standalone/.next/build-manifest.json +3 -3
  68. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  69. package/dist/web/standalone/.next/react-loadable-manifest.json +1 -1
  70. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  71. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  72. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  73. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  74. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  75. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  76. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  77. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  78. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  79. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  80. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  81. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  82. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  83. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  84. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  85. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  86. package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
  87. package/dist/web/standalone/.next/server/app/index.html +1 -1
  88. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  89. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  90. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  91. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  92. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  93. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  94. package/dist/web/standalone/.next/server/app-paths-manifest.json +10 -10
  95. package/dist/web/standalone/.next/server/chunks/6897.js +3 -3
  96. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  97. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  98. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  99. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  100. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  101. package/dist/web/standalone/.next/static/chunks/{8336.6f6f30e410419aff.js → 8336.631939fb583761fa.js} +1 -1
  102. package/dist/web/standalone/.next/static/chunks/{webpack-d82dbee6356c1733.js → webpack-0481f1221120a7c6.js} +1 -1
  103. package/package.json +12 -8
  104. package/packages/contracts/package.json +1 -1
  105. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  106. package/packages/mcp-server/dist/workflow-tools.js +22 -17
  107. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  108. package/packages/mcp-server/src/workflow-tools.test.ts +75 -2
  109. package/packages/mcp-server/src/workflow-tools.ts +30 -16
  110. package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
  111. package/packages/native/tsconfig.tsbuildinfo +1 -1
  112. package/packages/pi-ai/dist/models/fake-model.d.ts +12 -0
  113. package/packages/pi-ai/dist/models/fake-model.d.ts.map +1 -0
  114. package/packages/pi-ai/dist/models/fake-model.js +27 -0
  115. package/packages/pi-ai/dist/models/fake-model.js.map +1 -0
  116. package/packages/pi-ai/dist/models/index.d.ts.map +1 -1
  117. package/packages/pi-ai/dist/models/index.js +8 -0
  118. package/packages/pi-ai/dist/models/index.js.map +1 -1
  119. package/packages/pi-ai/dist/providers/fake.d.ts +42 -0
  120. package/packages/pi-ai/dist/providers/fake.d.ts.map +1 -0
  121. package/packages/pi-ai/dist/providers/fake.js +319 -0
  122. package/packages/pi-ai/dist/providers/fake.js.map +1 -0
  123. package/packages/pi-ai/dist/providers/register-builtins.d.ts.map +1 -1
  124. package/packages/pi-ai/dist/providers/register-builtins.js +24 -0
  125. package/packages/pi-ai/dist/providers/register-builtins.js.map +1 -1
  126. package/packages/pi-ai/src/models/fake-model.ts +30 -0
  127. package/packages/pi-ai/src/models/index.ts +9 -0
  128. package/packages/pi-ai/src/providers/fake.ts +376 -0
  129. package/packages/pi-ai/src/providers/register-builtins.ts +23 -0
  130. package/packages/pi-ai/tsconfig.tsbuildinfo +1 -1
  131. package/packages/pi-coding-agent/dist/core/agent-session-abort-order.test.js +32 -0
  132. package/packages/pi-coding-agent/dist/core/agent-session-abort-order.test.js.map +1 -1
  133. package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
  134. package/packages/pi-coding-agent/dist/core/agent-session.js +8 -0
  135. package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
  136. package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js +76 -0
  137. package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js.map +1 -1
  138. package/packages/pi-coding-agent/dist/core/compaction/compaction.d.ts +11 -0
  139. package/packages/pi-coding-agent/dist/core/compaction/compaction.d.ts.map +1 -1
  140. package/packages/pi-coding-agent/dist/core/compaction/compaction.js +9 -0
  141. package/packages/pi-coding-agent/dist/core/compaction/compaction.js.map +1 -1
  142. package/packages/pi-coding-agent/dist/core/compaction-threshold.test.d.ts +2 -0
  143. package/packages/pi-coding-agent/dist/core/compaction-threshold.test.d.ts.map +1 -0
  144. package/packages/pi-coding-agent/dist/core/compaction-threshold.test.js +103 -0
  145. package/packages/pi-coding-agent/dist/core/compaction-threshold.test.js.map +1 -0
  146. package/packages/pi-coding-agent/dist/core/db-snapshot.d.ts +15 -0
  147. package/packages/pi-coding-agent/dist/core/db-snapshot.d.ts.map +1 -0
  148. package/packages/pi-coding-agent/dist/core/db-snapshot.js +66 -0
  149. package/packages/pi-coding-agent/dist/core/db-snapshot.js.map +1 -0
  150. package/packages/pi-coding-agent/dist/core/db-snapshot.test.d.ts +2 -0
  151. package/packages/pi-coding-agent/dist/core/db-snapshot.test.d.ts.map +1 -0
  152. package/packages/pi-coding-agent/dist/core/db-snapshot.test.js +24 -0
  153. package/packages/pi-coding-agent/dist/core/db-snapshot.test.js.map +1 -0
  154. package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts +3 -0
  155. package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts.map +1 -1
  156. package/packages/pi-coding-agent/dist/core/extensions/runner.js +17 -1
  157. package/packages/pi-coding-agent/dist/core/extensions/runner.js.map +1 -1
  158. package/packages/pi-coding-agent/dist/core/extensions/runner.test.js +99 -0
  159. package/packages/pi-coding-agent/dist/core/extensions/runner.test.js.map +1 -1
  160. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +7 -0
  161. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
  162. package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
  163. package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
  164. package/packages/pi-coding-agent/dist/core/model-registry.js +5 -0
  165. package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
  166. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +24 -0
  167. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
  168. package/packages/pi-coding-agent/dist/core/settings-manager.js +33 -0
  169. package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
  170. package/packages/pi-coding-agent/dist/core/slash-commands.d.ts.map +1 -1
  171. package/packages/pi-coding-agent/dist/core/slash-commands.js +1 -0
  172. package/packages/pi-coding-agent/dist/core/slash-commands.js.map +1 -1
  173. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/chat-frame-compaction-tone.test.js +6 -4
  174. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/chat-frame-compaction-tone.test.js.map +1 -1
  175. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js +54 -15
  176. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js.map +1 -1
  177. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.d.ts +26 -0
  178. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.d.ts.map +1 -0
  179. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.js +112 -0
  180. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.js.map +1 -0
  181. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.test.d.ts +2 -0
  182. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.test.d.ts.map +1 -0
  183. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.test.js +51 -0
  184. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.test.js.map +1 -0
  185. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  186. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js.map +1 -1
  187. package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.d.ts.map +1 -1
  188. package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.js +10 -9
  189. package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.js.map +1 -1
  190. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts +3 -0
  191. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  192. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js +11 -0
  193. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js.map +1 -1
  194. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.js +7 -6
  195. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.js.map +1 -1
  196. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts +17 -0
  197. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  198. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +109 -17
  199. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
  200. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  201. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +69 -2
  202. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  203. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.test.js +93 -1
  204. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.test.js.map +1 -1
  205. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js +1 -0
  206. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js.map +1 -1
  207. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts +1 -0
  208. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts.map +1 -1
  209. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.js.map +1 -1
  210. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +3 -0
  211. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  212. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +26 -0
  213. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  214. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.d.ts.map +1 -1
  215. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.js +20 -0
  216. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.js.map +1 -1
  217. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.test.d.ts +2 -0
  218. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.test.d.ts.map +1 -0
  219. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.test.js +79 -0
  220. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.test.js.map +1 -0
  221. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-schema.d.ts +12 -0
  222. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-schema.d.ts.map +1 -1
  223. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-schema.js +13 -0
  224. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-schema.js.map +1 -1
  225. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.d.ts +1 -1
  226. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  227. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js +18 -1
  228. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js.map +1 -1
  229. package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.d.ts.map +1 -1
  230. package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.js +36 -27
  231. package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.js.map +1 -1
  232. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.d.ts +11 -0
  233. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.d.ts.map +1 -0
  234. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.js +18 -0
  235. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.js.map +1 -0
  236. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.test.d.ts +2 -0
  237. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.test.d.ts.map +1 -0
  238. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.test.js +48 -0
  239. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.test.js.map +1 -0
  240. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage-safety-guard.test.d.ts +2 -0
  241. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage-safety-guard.test.d.ts.map +1 -0
  242. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage-safety-guard.test.js +10 -0
  243. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage-safety-guard.test.js.map +1 -0
  244. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.d.ts.map +1 -1
  245. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.js +3 -2
  246. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.js.map +1 -1
  247. package/packages/pi-coding-agent/src/core/agent-session-abort-order.test.ts +36 -0
  248. package/packages/pi-coding-agent/src/core/agent-session.ts +8 -0
  249. package/packages/pi-coding-agent/src/core/chat-controller-ordering.test.ts +89 -0
  250. package/packages/pi-coding-agent/src/core/compaction/compaction.ts +18 -0
  251. package/packages/pi-coding-agent/src/core/compaction-threshold.test.ts +121 -0
  252. package/packages/pi-coding-agent/src/core/db-snapshot.test.ts +32 -0
  253. package/packages/pi-coding-agent/src/core/db-snapshot.ts +66 -0
  254. package/packages/pi-coding-agent/src/core/extensions/runner.test.ts +110 -0
  255. package/packages/pi-coding-agent/src/core/extensions/runner.ts +19 -1
  256. package/packages/pi-coding-agent/src/core/extensions/types.ts +7 -0
  257. package/packages/pi-coding-agent/src/core/model-registry.ts +4 -0
  258. package/packages/pi-coding-agent/src/core/settings-manager.ts +51 -1
  259. package/packages/pi-coding-agent/src/core/slash-commands.ts +1 -0
  260. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/chat-frame-compaction-tone.test.ts +7 -5
  261. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/tool-execution.test.ts +78 -15
  262. package/packages/pi-coding-agent/src/modes/interactive/components/adaptive-layout.test.ts +59 -0
  263. package/packages/pi-coding-agent/src/modes/interactive/components/adaptive-layout.ts +160 -0
  264. package/packages/pi-coding-agent/src/modes/interactive/components/assistant-message.ts +1 -0
  265. package/packages/pi-coding-agent/src/modes/interactive/components/chat-frame.ts +10 -9
  266. package/packages/pi-coding-agent/src/modes/interactive/components/settings-selector.ts +15 -0
  267. package/packages/pi-coding-agent/src/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.ts +10 -9
  268. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +122 -17
  269. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.test.ts +99 -1
  270. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +92 -3
  271. package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.test.ts +1 -0
  272. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode-state.ts +1 -1
  273. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +28 -0
  274. package/packages/pi-coding-agent/src/modes/interactive/slash-command-handlers.test.ts +95 -0
  275. package/packages/pi-coding-agent/src/modes/interactive/slash-command-handlers.ts +24 -1
  276. package/packages/pi-coding-agent/src/modes/interactive/theme/theme-schema.ts +13 -0
  277. package/packages/pi-coding-agent/src/modes/interactive/theme/theme.ts +32 -2
  278. package/packages/pi-coding-agent/src/modes/interactive/theme/themes.ts +36 -27
  279. package/packages/pi-coding-agent/src/modes/interactive/tui-mode.test.ts +65 -0
  280. package/packages/pi-coding-agent/src/modes/interactive/tui-mode.ts +29 -0
  281. package/packages/pi-coding-agent/src/resources/extensions/memory/storage-safety-guard.test.ts +14 -0
  282. package/packages/pi-coding-agent/src/resources/extensions/memory/storage.ts +3 -2
  283. package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
  284. package/packages/pi-tui/dist/__tests__/style.test.d.ts +2 -0
  285. package/packages/pi-tui/dist/__tests__/style.test.d.ts.map +1 -0
  286. package/packages/pi-tui/dist/__tests__/style.test.js +63 -0
  287. package/packages/pi-tui/dist/__tests__/style.test.js.map +1 -0
  288. package/packages/pi-tui/dist/__tests__/tui.test.js +24 -3
  289. package/packages/pi-tui/dist/__tests__/tui.test.js.map +1 -1
  290. package/packages/pi-tui/dist/index.d.ts +1 -0
  291. package/packages/pi-tui/dist/index.d.ts.map +1 -1
  292. package/packages/pi-tui/dist/index.js +2 -0
  293. package/packages/pi-tui/dist/index.js.map +1 -1
  294. package/packages/pi-tui/dist/style.d.ts +41 -0
  295. package/packages/pi-tui/dist/style.d.ts.map +1 -0
  296. package/packages/pi-tui/dist/style.js +158 -0
  297. package/packages/pi-tui/dist/style.js.map +1 -0
  298. package/packages/pi-tui/dist/tui.d.ts +0 -1
  299. package/packages/pi-tui/dist/tui.d.ts.map +1 -1
  300. package/packages/pi-tui/dist/tui.js +21 -16
  301. package/packages/pi-tui/dist/tui.js.map +1 -1
  302. package/packages/pi-tui/src/__tests__/style.test.ts +76 -0
  303. package/packages/pi-tui/src/__tests__/tui.test.ts +29 -3
  304. package/packages/pi-tui/src/index.ts +9 -0
  305. package/packages/pi-tui/src/style.ts +225 -0
  306. package/packages/pi-tui/src/tui.ts +23 -16
  307. package/packages/pi-tui/tsconfig.tsbuildinfo +1 -1
  308. package/pkg/dist/modes/interactive/theme/theme-schema.d.ts +12 -0
  309. package/pkg/dist/modes/interactive/theme/theme-schema.d.ts.map +1 -1
  310. package/pkg/dist/modes/interactive/theme/theme-schema.js +13 -0
  311. package/pkg/dist/modes/interactive/theme/theme-schema.js.map +1 -1
  312. package/pkg/dist/modes/interactive/theme/theme.d.ts +1 -1
  313. package/pkg/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  314. package/pkg/dist/modes/interactive/theme/theme.js +18 -1
  315. package/pkg/dist/modes/interactive/theme/theme.js.map +1 -1
  316. package/pkg/dist/modes/interactive/theme/themes.d.ts.map +1 -1
  317. package/pkg/dist/modes/interactive/theme/themes.js +36 -27
  318. package/pkg/dist/modes/interactive/theme/themes.js.map +1 -1
  319. package/src/resources/GSD-WORKFLOW.md +2 -2
  320. package/src/resources/extensions/github-sync/templates.ts +38 -8
  321. package/src/resources/extensions/github-sync/tests/inline-code.test.ts +66 -0
  322. package/src/resources/extensions/gsd/auto/loop-deps.ts +1 -0
  323. package/src/resources/extensions/gsd/auto/loop.ts +67 -18
  324. package/src/resources/extensions/gsd/auto/phases.ts +77 -48
  325. package/src/resources/extensions/gsd/auto/resolve.ts +23 -1
  326. package/src/resources/extensions/gsd/auto/run-unit.ts +42 -15
  327. package/src/resources/extensions/gsd/auto-dashboard.ts +57 -8
  328. package/src/resources/extensions/gsd/auto-dispatch.ts +17 -0
  329. package/src/resources/extensions/gsd/auto-post-unit.ts +10 -10
  330. package/src/resources/extensions/gsd/auto-prompts.ts +133 -2
  331. package/src/resources/extensions/gsd/auto-recovery.ts +207 -7
  332. package/src/resources/extensions/gsd/auto-start.ts +7 -6
  333. package/src/resources/extensions/gsd/auto-supervisor.ts +7 -0
  334. package/src/resources/extensions/gsd/auto-timeout-recovery.ts +2 -2
  335. package/src/resources/extensions/gsd/auto.ts +92 -4
  336. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +37 -2
  337. package/src/resources/extensions/gsd/bootstrap/exec-tools.ts +27 -19
  338. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +39 -1
  339. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +135 -1
  340. package/src/resources/extensions/gsd/clean-root-preflight.ts +41 -3
  341. package/src/resources/extensions/gsd/commands/dispatcher.ts +6 -0
  342. package/src/resources/extensions/gsd/commands-extract-learnings.ts +17 -12
  343. package/src/resources/extensions/gsd/context-budget.ts +44 -2
  344. package/src/resources/extensions/gsd/crash-recovery.ts +67 -10
  345. package/src/resources/extensions/gsd/custom-workflow-engine.ts +24 -1
  346. package/src/resources/extensions/gsd/db/unit-dispatches.ts +41 -0
  347. package/src/resources/extensions/gsd/db-base-schema.ts +19 -2
  348. package/src/resources/extensions/gsd/db-migration-steps.ts +25 -0
  349. package/src/resources/extensions/gsd/detection.ts +128 -0
  350. package/src/resources/extensions/gsd/git-service.ts +46 -8
  351. package/src/resources/extensions/gsd/graph.ts +12 -5
  352. package/src/resources/extensions/gsd/gsd-db.ts +168 -13
  353. package/src/resources/extensions/gsd/guided-flow.ts +98 -16
  354. package/src/resources/extensions/gsd/memory-store.ts +77 -12
  355. package/src/resources/extensions/gsd/migrate/command.ts +47 -1
  356. package/src/resources/extensions/gsd/migration-auto-check.ts +129 -0
  357. package/src/resources/extensions/gsd/planning-path-scope.ts +35 -0
  358. package/src/resources/extensions/gsd/pr-evidence.ts +63 -5
  359. package/src/resources/extensions/gsd/pre-execution-checks.ts +7 -0
  360. package/src/resources/extensions/gsd/preferences-types.ts +1 -1
  361. package/src/resources/extensions/gsd/prompt-loader.ts +27 -2
  362. package/src/resources/extensions/gsd/prompts/complete-milestone.md +19 -19
  363. package/src/resources/extensions/gsd/prompts/parallel-research-slices.md +1 -1
  364. package/src/resources/extensions/gsd/prompts/plan-milestone.md +3 -1
  365. package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  366. package/src/resources/extensions/gsd/prompts/quick-task.md +1 -5
  367. package/src/resources/extensions/gsd/prompts/validate-milestone.md +2 -2
  368. package/src/resources/extensions/gsd/quick.ts +37 -2
  369. package/src/resources/extensions/gsd/safety/evidence-collector.ts +11 -2
  370. package/src/resources/extensions/gsd/tests/auto-abort-pause-regression.test.ts +7 -1
  371. package/src/resources/extensions/gsd/tests/auto-dashboard.test.ts +33 -0
  372. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +155 -5
  373. package/src/resources/extensions/gsd/tests/auto-phases-lifecycle.test.ts +56 -13
  374. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +184 -2
  375. package/src/resources/extensions/gsd/tests/clean-root-preflight.test.ts +88 -2
  376. package/src/resources/extensions/gsd/tests/commands-extract-learnings.test.ts +9 -0
  377. package/src/resources/extensions/gsd/tests/compaction-snapshot.test.ts +14 -1
  378. package/src/resources/extensions/gsd/tests/context-budget.test.ts +10 -1
  379. package/src/resources/extensions/gsd/tests/crash-handler-secondary.test.ts +55 -0
  380. package/src/resources/extensions/gsd/tests/crash-recovery-via-db.test.ts +22 -0
  381. package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +112 -6
  382. package/src/resources/extensions/gsd/tests/custom-workflow-engine.test.ts +40 -2
  383. package/src/resources/extensions/gsd/tests/db-migration-steps.integration.test.ts +428 -0
  384. package/src/resources/extensions/gsd/tests/db-schema-metadata.test.ts +2 -2
  385. package/src/resources/extensions/gsd/tests/detection.test.ts +140 -0
  386. package/src/resources/extensions/gsd/tests/dispatch-rule-coverage.test.ts +313 -0
  387. package/src/resources/extensions/gsd/tests/exec-history.test.ts +15 -0
  388. package/src/resources/extensions/gsd/tests/exec-sandbox.test.ts +65 -0
  389. package/src/resources/extensions/gsd/tests/fixtures/pr-body/commands-ship-basic.md +52 -0
  390. package/src/resources/extensions/gsd/tests/fixtures/pr-body/commands-ship-empty-optionals.md +42 -0
  391. package/src/resources/extensions/gsd/tests/fixtures/pr-body/swarm-lane-no-blockers.md +55 -0
  392. package/src/resources/extensions/gsd/tests/fixtures/pr-body/swarm-lane-with-blockers.md +60 -0
  393. package/src/resources/extensions/gsd/tests/graph-operations.test.ts +10 -0
  394. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +44 -0
  395. package/src/resources/extensions/gsd/tests/has-pending-deep-stage.test.ts +33 -1
  396. package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +54 -0
  397. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +234 -0
  398. package/src/resources/extensions/gsd/tests/memory-decay-factor.test.ts +90 -0
  399. package/src/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +48 -0
  400. package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +127 -0
  401. package/src/resources/extensions/gsd/tests/plan-slice.test.ts +50 -0
  402. package/src/resources/extensions/gsd/tests/plan-task.test.ts +21 -0
  403. package/src/resources/extensions/gsd/tests/pr-evidence-equivalence.test.ts +102 -0
  404. package/src/resources/extensions/gsd/tests/pr-evidence-hardening.test.ts +165 -0
  405. package/src/resources/extensions/gsd/tests/pre-execution-checks.test.ts +38 -0
  406. package/src/resources/extensions/gsd/tests/prompt-path-audit.test.ts +40 -0
  407. package/src/resources/extensions/gsd/tests/prompt-step-ordering.test.ts +19 -0
  408. package/src/resources/extensions/gsd/tests/quick-external-gsd.test.ts +40 -0
  409. package/src/resources/extensions/gsd/tests/right-sized-workflow-prompts.test.ts +192 -0
  410. package/src/resources/extensions/gsd/tests/safety-harness-false-positives.test.ts +29 -0
  411. package/src/resources/extensions/gsd/tests/schema-v27-v28-sequence.test.ts +156 -0
  412. package/src/resources/extensions/gsd/tests/signal-handlers.test.ts +27 -0
  413. package/src/resources/extensions/gsd/tests/smart-entry-complete.test.ts +38 -0
  414. package/src/resources/extensions/gsd/tests/stalled-tool-recovery.test.ts +49 -1
  415. package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +101 -2
  416. package/src/resources/extensions/gsd/tests/status-db-open.test.ts +9 -0
  417. package/src/resources/extensions/gsd/tests/unit-context-composer.test.ts +136 -4
  418. package/src/resources/extensions/gsd/tests/unit-dispatches.test.ts +30 -0
  419. package/src/resources/extensions/gsd/tests/unit-runtime.test.ts +37 -0
  420. package/src/resources/extensions/gsd/tests/uok-plan-v2-wiring.test.ts +1 -1
  421. package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +3 -0
  422. package/src/resources/extensions/gsd/tests/working-output-messages.test.ts +93 -0
  423. package/src/resources/extensions/gsd/tests/worktree-health-dispatch.test.ts +37 -6
  424. package/src/resources/extensions/gsd/tests/worktree-manager.test.ts +7 -0
  425. package/src/resources/extensions/gsd/tests/worktree-nested-git-safety.test.ts +9 -2
  426. package/src/resources/extensions/gsd/tests/worktree-resolver.test.ts +63 -1
  427. package/src/resources/extensions/gsd/tests/worktree-write-gate.test.ts +179 -0
  428. package/src/resources/extensions/gsd/tools/context-mode-tool-result.ts +25 -0
  429. package/src/resources/extensions/gsd/tools/exec-search-tool.ts +7 -7
  430. package/src/resources/extensions/gsd/tools/exec-tool.ts +4 -23
  431. package/src/resources/extensions/gsd/tools/memory-tools.ts +1 -0
  432. package/src/resources/extensions/gsd/tools/plan-slice.ts +13 -0
  433. package/src/resources/extensions/gsd/tools/plan-task.ts +10 -0
  434. package/src/resources/extensions/gsd/tools/resume-tool.ts +7 -7
  435. package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +1 -1
  436. package/src/resources/extensions/gsd/unit-context-composer.ts +19 -4
  437. package/src/resources/extensions/gsd/unit-runtime.ts +25 -0
  438. package/src/resources/extensions/gsd/working-output-messages.ts +120 -0
  439. package/src/resources/extensions/gsd/worktree-manager.ts +15 -4
  440. package/src/resources/extensions/gsd/worktree-resolver.ts +36 -15
  441. package/packages/contracts/tsconfig.tsbuildinfo +0 -1
  442. /package/dist/web/standalone/.next/static/{bQDK5_LtkGVS64AirQgQG → -5nHJWzSdG-WkPMul_khA}/_buildManifest.js +0 -0
  443. /package/dist/web/standalone/.next/static/{bQDK5_LtkGVS64AirQgQG → -5nHJWzSdG-WkPMul_khA}/_ssgManifest.js +0 -0
@@ -8,7 +8,7 @@
8
8
 
9
9
  import { loadFile, parseContinue, parseSummary, loadActiveOverrides, formatOverridesSection, parseTaskPlanFile } from "./files.js";
10
10
  import type { Override, UatType } from "./files.js";
11
- import { hasVerdict, getUatType } from "./verdict-parser.js";
11
+ import { hasVerdict, getUatType, extractVerdict } from "./verdict-parser.js";
12
12
  import { loadPrompt, inlineTemplate } from "./prompt-loader.js";
13
13
  import {
14
14
  resolveMilestoneFile, resolveSliceFile, resolveSlicePath,
@@ -35,10 +35,12 @@ import {
35
35
  import { formatDecisionsCompact, formatRequirementsCompact } from "./structured-data-formatter.js";
36
36
  import { readPhaseAnchor, formatAnchorForPrompt } from "./phase-anchor.js";
37
37
  import { composeContextModeInstructions, composeInlinedContext, type ArtifactResolver, type ContextModeRenderMode } from "./unit-context-composer.js";
38
+ import { readCompactionSnapshot } from "./compaction-snapshot.js";
38
39
  import { logWarning } from "./workflow-logger.js";
39
40
  import { inlineGraphSubgraph } from "./graph-context.js";
40
41
  import { buildExtractionStepsBlock } from "./commands-extract-learnings.js";
41
42
  import { resolveSkillManifest, warnIfManifestHasMissingSkills } from "./skill-manifest.js";
43
+ import { classifyProject, type ProjectClassification } from "./detection.js";
42
44
 
43
45
  // ─── Preamble Cap ─────────────────────────────────────────────────────────────
44
46
 
@@ -80,6 +82,108 @@ function resolveSummaryBudgetChars(): number {
80
82
  return resolvePromptBudgets().summaryBudgetChars;
81
83
  }
82
84
 
85
+ function formatProjectClassificationForPlanning(classification: ProjectClassification): string {
86
+ const sampleFiles = classification.contentFiles.slice(0, 8);
87
+ const sample = sampleFiles.length > 0 ? sampleFiles.map((file) => `\`${file}\``).join(", ") : "(none)";
88
+ const lines = [
89
+ "### Project Classification",
90
+ "",
91
+ `- **Kind:** ${classification.kind}`,
92
+ `- **Content files:** ${classification.contentFiles.length}`,
93
+ `- **Sample files:** ${sample}`,
94
+ `- **Reason:** ${classification.reason}`,
95
+ "",
96
+ ];
97
+
98
+ if (classification.kind === "untyped-existing") {
99
+ if (classification.contentFiles.length <= 2) {
100
+ lines.push(
101
+ "**Workflow sizing:** This is a tiny existing untyped project. Prefer exactly one slice unless the milestone request clearly spans multiple independent user-visible capabilities.",
102
+ );
103
+ } else if (classification.contentFiles.length <= 5) {
104
+ lines.push(
105
+ "**Workflow sizing:** This is a small existing untyped project. Prefer 1-2 slices unless the milestone request clearly spans multiple independent user-visible capabilities.",
106
+ );
107
+ } else {
108
+ lines.push(
109
+ "**Workflow sizing:** Existing untyped project. Use generic file-level workflow guidance and size slices by real capability boundaries, not by missing tooling markers.",
110
+ );
111
+ }
112
+ } else if (classification.kind === "greenfield") {
113
+ lines.push("**Workflow sizing:** No project content exists yet. Use normal greenfield sizing for the requested scope.");
114
+ } else if (classification.kind === "typed-existing") {
115
+ lines.push("**Workflow sizing:** Known project markers exist. Use normal ecosystem-aware planning guidance.");
116
+ } else {
117
+ lines.push("**Workflow sizing:** Invalid repository state. Planning should surface this as a blocker rather than inventing project structure.");
118
+ }
119
+
120
+ return lines.join("\n");
121
+ }
122
+
123
+ function normalizeArtifactRef(value: string): string {
124
+ return value.trim().replace(/^[-\s]+/, "").replace(/^["'`]+|["'`]+$/g, "").replaceAll("\\", "/").replace(/^\.\//, "");
125
+ }
126
+
127
+ function parseCoveredArtifacts(validationContent: string): Set<string> {
128
+ const covered = new Set<string>();
129
+ const lines = validationContent.split(/\r?\n/);
130
+ let inCoveredArtifacts = false;
131
+ for (const line of lines) {
132
+ if (/^\s*covered[-_]?artifacts\s*:/i.test(line)) {
133
+ inCoveredArtifacts = true;
134
+ const inline = line.split(/covered[-_]?artifacts\s*:/i)[1]?.trim();
135
+ if (inline && inline !== "[]") {
136
+ inline.replace(/^\[|\]$/g, "").split(",").map(normalizeArtifactRef).filter(Boolean).forEach((item) => covered.add(item));
137
+ }
138
+ continue;
139
+ }
140
+ if (!inCoveredArtifacts) continue;
141
+ if (/^\S/.test(line) && !/^\s*-/.test(line)) break;
142
+ const item = line.match(/^\s*-\s*(.+)$/)?.[1];
143
+ if (item) covered.add(normalizeArtifactRef(item));
144
+ }
145
+ return covered;
146
+ }
147
+
148
+ function isValidationFreshOrApplicable(validationContent: string | null, currentArtifacts: string[]): boolean {
149
+ if (!validationContent) return false;
150
+ if (!/validation_metadata:/i.test(validationContent)) return false;
151
+ const coveredArtifacts = parseCoveredArtifacts(validationContent);
152
+ if (coveredArtifacts.size === 0) return false;
153
+ return currentArtifacts
154
+ .map(normalizeArtifactRef)
155
+ .filter(Boolean)
156
+ .every((artifact) => coveredArtifacts.has(artifact));
157
+ }
158
+
159
+ function formatCloseoutReviewInstructions(validationContent: string | null, validationRel: string, currentArtifacts: string[]): string {
160
+ const verdict = validationContent ? extractVerdict(validationContent) : null;
161
+ const validationFresh = isValidationFreshOrApplicable(validationContent, currentArtifacts);
162
+ if (verdict === "pass" && validationFresh) {
163
+ return [
164
+ "### Passing Validation Artifact",
165
+ "",
166
+ `A passing validation artifact is present at \`${validationRel}\`. Treat it as authoritative for success criteria, requirement coverage, verification classes, and cross-slice integration.`,
167
+ "",
168
+ "Do not delegate fresh reviewer/security/tester audits and do not redo the validation evidence review unless the artifact is internally inconsistent with the inlined summaries. Focus this unit on final milestone narrative, learnings, PROJECT/requirements updates, and `gsd_complete_milestone`.",
169
+ ].join("\n");
170
+ }
171
+
172
+ if (verdict) {
173
+ return [
174
+ "### Validation Requires Attention",
175
+ "",
176
+ `A validation artifact is present at \`${validationRel}\` with verdict \`${verdict}\`, but it is missing freshness metadata or does not cover current milestone artifacts. Do not treat the milestone as complete unless the issues are resolved and evidence supports completion.`,
177
+ ].join("\n");
178
+ }
179
+
180
+ return [
181
+ "### No Passing Validation Artifact",
182
+ "",
183
+ `No passing validation artifact was found at \`${validationRel}\`. Use the full closeout review path before completion.`,
184
+ ].join("\n");
185
+ }
186
+
83
187
  function capPreamble(preamble: string): string {
84
188
  // Cap inlined context at min(historical 30K ceiling, scaled inline budget).
85
189
  // The ceiling preserves pre-fix behavior for large-window users; the scaled
@@ -102,13 +206,28 @@ function renderContextModeForPrompt(
102
206
  });
103
207
  }
104
208
 
209
+ function renderContextModeBlockForPrompt(
210
+ unitType: string,
211
+ base: string,
212
+ renderMode: ContextModeRenderMode = "standalone",
213
+ ): string {
214
+ const contextMode = renderContextModeForPrompt(unitType, base, renderMode);
215
+ if (!contextMode) return "";
216
+ if (renderMode === "nested") return contextMode;
217
+
218
+ const snapshot = readCompactionSnapshot(base);
219
+ if (!snapshot?.trim()) return contextMode;
220
+
221
+ return `${contextMode}\n\n## Context Snapshot\nSource: \`.gsd/last-snapshot.md\`\n\n${snapshot.trimEnd()}`;
222
+ }
223
+
105
224
  function prependContextModeToBlock(
106
225
  unitType: string,
107
226
  base: string,
108
227
  block: string,
109
228
  renderMode: ContextModeRenderMode = "standalone",
110
229
  ): string {
111
- const contextMode = renderContextModeForPrompt(unitType, base, renderMode);
230
+ const contextMode = renderContextModeBlockForPrompt(unitType, base, renderMode);
112
231
  if (!contextMode) return block;
113
232
  if (!block.trim()) return contextMode;
114
233
  return `${contextMode}\n\n${block}`;
@@ -1658,6 +1777,8 @@ export async function buildPlanMilestonePrompt(mid: string, midTitle: string, ba
1658
1777
  const researchAnchor = readPhaseAnchor(base, mid, "research-milestone");
1659
1778
  if (researchAnchor) inlined.push(formatAnchorForPrompt(researchAnchor));
1660
1779
 
1780
+ inlined.push(formatProjectClassificationForPlanning(classifyProject(base)));
1781
+
1661
1782
  inlined.push(await inlineFile(contextPath, contextRel, "Milestone Context"));
1662
1783
  const researchInline = await inlineFileOptional(researchPath, researchRel, "Milestone Research");
1663
1784
  if (researchInline) inlined.push(researchInline);
@@ -2348,6 +2469,9 @@ export async function buildCompleteMilestonePrompt(
2348
2469
  const inlineLevel = level ?? resolveInlineLevel();
2349
2470
  const roadmapPath = resolveMilestoneFile(base, mid, "ROADMAP");
2350
2471
  const roadmapRel = relMilestoneFile(base, mid, "ROADMAP");
2472
+ const validationPath = resolveMilestoneFile(base, mid, "VALIDATION");
2473
+ const validationRel = relMilestoneFile(base, mid, "VALIDATION");
2474
+ const validationContent = validationPath ? await loadFile(validationPath) : null;
2351
2475
 
2352
2476
  const inlined: string[] = [];
2353
2477
  inlined.push(await inlineFile(roadmapPath, roadmapRel, "Milestone Roadmap"));
@@ -2389,6 +2513,13 @@ export async function buildCompleteMilestonePrompt(
2389
2513
  `### On-demand Slice Summaries\n\nExcerpted above. Read the full file for any slice when the excerpt's section heads don't carry enough narrative for the milestone summary you're drafting:\n\n${pathList}`,
2390
2514
  );
2391
2515
  }
2516
+ const validationContext = [
2517
+ formatCloseoutReviewInstructions(validationContent, validationRel, [validationRel, roadmapRel, ...summaryRelPaths]),
2518
+ ];
2519
+ if (validationContent) {
2520
+ validationContext.push(`### Milestone Validation\nSource: \`${validationRel}\`\n\n${validationContent.trim()}`);
2521
+ }
2522
+ inlined.unshift(...validationContext);
2392
2523
 
2393
2524
  // Inline root GSD files (skip for minimal — completion can read these if needed)
2394
2525
  if (inlineLevel !== "minimal") {
@@ -14,7 +14,7 @@ import { appendEvent } from "./workflow-events.js";
14
14
  import { atomicWriteSync } from "./atomic-write.js";
15
15
  import { clearParseCache } from "./files.js";
16
16
  import { parseRoadmap as parseLegacyRoadmap, parsePlan as parseLegacyPlan } from "./parsers-legacy.js";
17
- import { isDbAvailable, getTask, getSlice, getSliceTasks, getPendingGates, updateTaskStatus, updateSliceStatus, insertSlice, getMilestone, refreshOpenDatabaseFromDisk } from "./gsd-db.js";
17
+ import { isDbAvailable, getTask, getSlice, getSliceTasks, getPendingGates, updateTaskStatus, updateSliceStatus, insertSlice, getMilestone, refreshOpenDatabaseFromDisk, getCompletedMilestoneTaskFileHints, getMilestoneCommitAttributionShas, recordMilestoneCommitAttribution } from "./gsd-db.js";
18
18
  import { isValidationTerminal } from "./state.js";
19
19
  import { getErrorMessage } from "./error-utils.js";
20
20
  import { logWarning, logError } from "./workflow-logger.js";
@@ -67,6 +67,60 @@ export {
67
67
 
68
68
  // ─── Artifact Resolution & Verification ───────────────────────────────────────
69
69
 
70
+ export type ArtifactRecoveryDbRefreshResult =
71
+ | { ok: true }
72
+ | { ok: false; fatal: boolean; message: string; reason: string };
73
+
74
+ export function refreshRecoveryDbForArtifact(
75
+ unitType: string,
76
+ unitId: string,
77
+ ): ArtifactRecoveryDbRefreshResult {
78
+ if (unitType !== "plan-slice" && unitType !== "execute-task") return { ok: true };
79
+ if (!isDbAvailable()) return { ok: true };
80
+
81
+ if (!refreshOpenDatabaseFromDisk()) {
82
+ return {
83
+ ok: false,
84
+ fatal: unitType === "execute-task",
85
+ reason: `${unitType}-db-refresh-failed`,
86
+ message: `Stuck recovery found ${unitType} ${unitId} artifacts, but the DB refresh failed.`,
87
+ };
88
+ }
89
+
90
+ if (unitType !== "execute-task") return { ok: true };
91
+
92
+ const { milestone: mid, slice: sid, task: tid } = parseUnitId(unitId);
93
+ if (!mid || !sid || !tid) {
94
+ return {
95
+ ok: false,
96
+ fatal: true,
97
+ reason: "execute-task-invalid-unit-id",
98
+ message: `Stuck recovery found execute-task ${unitId} artifacts, but the unit id could not be parsed for DB verification.`,
99
+ };
100
+ }
101
+
102
+ const task = getTask(mid, sid, tid);
103
+ if (!task) {
104
+ return {
105
+ ok: false,
106
+ fatal: true,
107
+ reason: "execute-task-artifact-db-missing",
108
+ message: `Stuck recovery found execute-task ${unitId} artifacts, but no matching DB task row exists after refresh.`,
109
+ };
110
+ }
111
+
112
+ if (!isClosedStatus(task.status)) {
113
+ return {
114
+ ok: false,
115
+ fatal: true,
116
+ reason: "execute-task-artifact-db-mismatch",
117
+ message: `Stuck recovery found execute-task ${unitId} artifacts, but the DB task status is still '${task.status}' after refresh.`,
118
+ };
119
+ }
120
+
121
+ return { ok: true };
122
+ }
123
+
70
124
  function hasCapturedWorkflowPrefs(base: string): boolean {
71
125
  const prefsPath = resolveExpectedArtifactPath("workflow-preferences", "WORKFLOW-PREFS", base);
72
126
  if (!prefsPath || !existsSync(prefsPath)) return false;
@@ -147,15 +201,29 @@ export function hasImplementationArtifacts(basePath: string, milestoneId?: strin
147
201
  // milestone commits instead of treating the self-diff as proof of no work.
148
202
  if (changedFiles.length === 0) {
149
203
  if (milestoneId && currentBranch === integrationBranch) {
150
- const tagged = getChangedFilesFromMilestoneTaggedCommits(basePath, milestoneId);
151
- if (!tagged.ok) return "unknown";
152
- if (tagged.matched) return classifyImplementationFiles(tagged.files);
204
+ const milestoneEvidence = getChangedFilesFromMilestoneEvidence(basePath, milestoneId);
205
+ if (!milestoneEvidence.ok) return "unknown";
206
+ if (milestoneEvidence.matched) return classifyImplementationFiles(milestoneEvidence.files);
153
207
  }
154
208
  if (currentBranch && currentBranch !== "HEAD") return "absent";
155
209
  return "unknown";
156
210
  }
157
211
 
158
- return classifyImplementationFiles(changedFiles);
212
+ const branchClassification = classifyImplementationFiles(changedFiles);
213
+ if (branchClassification === "present") return "present";
214
+
215
+ // A completing milestone branch can have a non-empty diff containing only
216
+ // .gsd/ closeout files after implementation commits already landed on the
217
+ // recorded integration branch. In that topology, the branch diff alone is
218
+ // insufficient; use the same milestone-tagged evidence fallback as the
219
+ // self-diff retry path before declaring the milestone implementation-free.
220
+ if (milestoneId) {
221
+ const milestoneEvidence = getChangedFilesFromMilestoneEvidence(basePath, milestoneId);
222
+ if (!milestoneEvidence.ok) return "unknown";
223
+ if (milestoneEvidence.matched) return classifyImplementationFiles(milestoneEvidence.files);
224
+ }
225
+
226
+ return "absent";
159
227
  } catch (e) {
160
228
  // Non-fatal — if git operations fail, return unknown so callers can decide
161
229
  logWarning("recovery", `implementation artifact check failed: ${(e as Error).message}`);
@@ -185,6 +253,10 @@ function isImplementationPath(file: string): boolean {
185
253
  return !file.startsWith(".gsd/") && !file.startsWith(".gsd\\");
186
254
  }
187
255
 
256
+ function normalizeRepoPath(file: string): string {
257
+ return file.trim().replace(/\\/g, "/").replace(/^\.\/+/, "");
258
+ }
259
+
188
260
  /**
189
261
  * Detect the main/master branch name.
190
262
  */
@@ -263,7 +335,7 @@ function getChangedFilesFromMilestoneTaggedCommits(
263
335
  "log", "--format=%H%x1f%B%x1e", "HEAD", "--", `.gsd/milestones/${milestoneId}`,
264
336
  ]);
265
337
  if (!scoped.ok) return scoped;
266
- if (scoped.matched) return scoped;
338
+ if (scoped.matched && classifyImplementationFiles(scoped.files) === "present") return scoped;
267
339
 
268
340
  // Fallback (#5033): when .gsd/ is gitignored / external / untracked, the
269
341
  // path-scoped scan matches no commits even though GSD-tagged commits
@@ -274,9 +346,137 @@ function getChangedFilesFromMilestoneTaggedCommits(
274
346
  // Intentionally unbounded — symmetric with the primary scan, and avoids
275
347
  // reintroducing the rolling-depth failure class removed in #4699 where
276
348
  // milestone evidence aged out behind unrelated activity.
277
- return scanGsdTaggedCommits(basePath, milestoneId, [
349
+ const unscoped = scanGsdTaggedCommits(basePath, milestoneId, [
278
350
  "log", "--format=%H%x1f%B%x1e", "HEAD",
279
351
  ]);
352
+ if (!unscoped.ok) return scoped.matched ? scoped : unscoped;
353
+ if (!unscoped.matched) return scoped;
354
+
355
+ return {
356
+ ok: true,
357
+ matched: true,
358
+ files: [...new Set([...scoped.files, ...unscoped.files])],
359
+ };
360
+ }
361
+
362
+ function getChangedFilesFromMilestoneEvidence(
363
+ basePath: string,
364
+ milestoneId: string,
365
+ ): { ok: boolean; matched: boolean; files: string[] } {
366
+ const tagged = getChangedFilesFromMilestoneTaggedCommits(basePath, milestoneId);
367
+ if (!tagged.ok) return tagged;
368
+ if (tagged.matched && classifyImplementationFiles(tagged.files) === "present") return tagged;
369
+
370
+ const attributed = getChangedFilesFromAttributedMilestoneCommits(basePath, milestoneId);
371
+ if (!attributed.ok) return tagged.matched ? tagged : attributed;
372
+ if (attributed.matched && classifyImplementationFiles(attributed.files) === "present") return attributed;
373
+
374
+ const backfilled = backfillChangedFilesFromUntaggedMilestoneCommits(basePath, milestoneId);
375
+ if (!backfilled.ok) return tagged.matched ? tagged : attributed.matched ? attributed : backfilled;
376
+ if (!backfilled.matched) {
377
+ if (tagged.matched) return tagged;
378
+ return attributed.matched ? attributed : backfilled;
379
+ }
380
+
381
+ return {
382
+ ok: true,
383
+ matched: true,
384
+ files: [...new Set([...tagged.files, ...attributed.files, ...backfilled.files])],
385
+ };
386
+ }
387
+
388
+ function getChangedFilesFromAttributedMilestoneCommits(
389
+ basePath: string,
390
+ milestoneId: string,
391
+ ): { ok: boolean; matched: boolean; files: string[] } {
392
+ try {
393
+ const shas = getMilestoneCommitAttributionShas(milestoneId);
394
+ if (shas.length === 0) return { ok: true, matched: false, files: [] };
395
+
396
+ const files = new Set<string>();
397
+ let matched = false;
398
+ for (const sha of shas) {
399
+ if (!isFullCommitSha(sha)) continue;
400
+ const commitFiles = getChangedFilesForCommit(basePath, sha);
401
+ if (commitFiles.length === 0) continue;
402
+ matched = true;
403
+ for (const file of commitFiles) files.add(file);
404
+ }
405
+ return { ok: true, matched, files: [...files] };
406
+ } catch (e) {
407
+ logWarning("recovery", `milestone attribution scan failed: ${(e as Error).message}`);
408
+ return { ok: false, matched: false, files: [] };
409
+ }
410
+ }
411
+
412
+ function backfillChangedFilesFromUntaggedMilestoneCommits(
413
+ basePath: string,
414
+ milestoneId: string,
415
+ ): { ok: boolean; matched: boolean; files: string[] } {
416
+ try {
417
+ const milestone = getMilestone(milestoneId);
418
+ const milestoneStartedAt = milestone?.created_at ? Math.floor(Date.parse(milestone.created_at) / 1000) * 1000 : NaN;
419
+ if (!Number.isFinite(milestoneStartedAt)) return { ok: true, matched: false, files: [] };
420
+
421
+ const taskFileHints = getCompletedMilestoneTaskFileHints(milestoneId);
422
+ if (taskFileHints.length === 0) return { ok: true, matched: false, files: [] };
423
+
424
+ const hintSet = new Set(taskFileHints.map(normalizeRepoPath).filter(Boolean));
425
+ if (hintSet.size === 0) return { ok: true, matched: false, files: [] };
426
+
427
+ const records = getCommitRecords(basePath);
428
+ const files = new Set<string>();
429
+ let matched = false;
430
+ for (const record of records) {
431
+ if (!isFullCommitSha(record.hash)) continue;
432
+ if (Date.parse(record.committedAt) < milestoneStartedAt) continue;
433
+ if (record.parents.trim().split(/\s+/).filter(Boolean).length > 1) continue;
434
+ if (commitMessageHasGsdTrailer(record.message)) continue;
435
+
436
+ const commitFiles = getChangedFilesForCommit(basePath, record.hash);
437
+ const implementationFiles = commitFiles.map(normalizeRepoPath).filter(isImplementationPath);
438
+ if (implementationFiles.length === 0) continue;
439
+ if (!implementationFiles.some((file) => hintSet.has(file))) continue;
440
+
441
+ matched = true;
442
+ for (const file of implementationFiles) files.add(file);
443
+ recordMilestoneCommitAttribution({
444
+ commitSha: record.hash,
445
+ milestoneId,
446
+ source: "backfill",
447
+ confidence: 0.8,
448
+ files: implementationFiles,
449
+ createdAt: new Date().toISOString(),
450
+ });
451
+ }
452
+
453
+ return { ok: true, matched, files: [...files] };
454
+ } catch (e) {
455
+ logWarning("recovery", `milestone attribution backfill failed: ${(e as Error).message}`);
456
+ return { ok: false, matched: false, files: [] };
457
+ }
458
+ }
459
+
460
+ function getCommitRecords(basePath: string): Array<{ hash: string; parents: string; committedAt: string; message: string }> {
461
+ const logOutput = execFileSync("git", ["log", "--format=%H%x1f%P%x1f%cI%x1f%B%x1e", "HEAD"], {
462
+ cwd: basePath,
463
+ stdio: ["ignore", "pipe", "pipe"],
464
+ encoding: "utf-8",
465
+ });
466
+ return logOutput
467
+ .split("\x1e")
468
+ .map((record) => record.trim())
469
+ .filter(Boolean)
470
+ .flatMap((record) => {
471
+ const parts = record.split("\x1f");
472
+ if (parts.length < 4) return [];
473
+ const [hash, parents, committedAt, ...messageParts] = parts;
474
+ return [{ hash: hash.trim(), parents: parents.trim(), committedAt: committedAt.trim(), message: messageParts.join("\x1f") }];
475
+ });
476
+ }
477
+
478
+ function isFullCommitSha(value: string): boolean {
479
+ return /^[0-9a-f]{40}$/i.test(value);
280
480
  }
281
481
 
282
482
  function scanGsdTaggedCommits(
@@ -664,12 +664,13 @@ export async function bootstrapAutoSession(
664
664
  }
665
665
 
666
666
  const effectivePrefs = loadEffectiveGSDPreferences(base)?.preferences;
667
- const deepProjectStagePending = !hasSurvivorBranch
668
- ? (await import("./auto-dispatch.js")).hasPendingDeepStage(
669
- effectivePrefs,
670
- base,
671
- )
672
- : false;
667
+ const { shouldRunDeepProjectSetup } = await import("./auto-dispatch.js");
668
+ const deepProjectStagePending = shouldRunDeepProjectSetup(
669
+ state,
670
+ effectivePrefs,
671
+ base,
672
+ { hasSurvivorBranch },
673
+ );
673
674
 
674
675
  if (deepProjectStagePending) {
675
676
  // Deep project-level setup runs before the first milestone exists. Let
@@ -32,6 +32,7 @@ let _currentSigtermHandler: (() => void) | null = null;
32
32
  export function registerSigtermHandler(
33
33
  currentBasePath: string,
34
34
  previousHandler: (() => void) | null,
35
+ onSignalCleanup?: () => void,
35
36
  ): () => void {
36
37
  // Remove the explicitly-passed previous handler
37
38
  if (previousHandler) {
@@ -43,6 +44,12 @@ export function registerSigtermHandler(
43
44
  for (const sig of CLEANUP_SIGNALS) process.off(sig, _currentSigtermHandler);
44
45
  }
45
46
  const handler = () => {
47
+ try {
48
+ onSignalCleanup?.();
49
+ } catch {
50
+ void 0;
51
+ // Signal cleanup is best-effort; lock cleanup and process exit still run.
52
+ }
46
53
  clearLock(currentBasePath);
47
54
  releaseSessionLock(currentBasePath);
48
55
  process.exit(0);
@@ -71,14 +71,14 @@ export async function recoverTimedOutUnit(
71
71
  recovery: status,
72
72
  });
73
73
 
74
- const durableComplete = status.summaryExists && status.taskChecked && status.nextActionAdvanced;
74
+ const durableComplete = status.dbComplete || (status.summaryExists && status.taskChecked && status.nextActionAdvanced);
75
75
  if (durableComplete) {
76
76
  writeUnitRuntimeRecord(basePath, unitType, unitId, currentUnitStartedAt, {
77
77
  phase: "finalized",
78
78
  recovery: status,
79
79
  });
80
80
  ctx.ui.notify(
81
- `${reason === "idle" ? "Idle" : "Timeout"} recovery: ${unitType} ${unitId} already completed on disk. Continuing auto-mode. (attempt ${attemptNumber})`,
81
+ `${reason === "idle" ? "Idle" : "Timeout"} recovery: ${unitType} ${unitId} already completed. Continuing auto-mode. (attempt ${attemptNumber})`,
82
82
  "info",
83
83
  );
84
84
  unitRecoveryCount.delete(recoveryKey);
@@ -59,6 +59,7 @@ import {
59
59
  isLockProcessAlive,
60
60
  formatCrashInfo,
61
61
  emitCrashRecoveredUnitEnd,
62
+ emitOpenUnitEndForUnit,
62
63
  } from "./crash-recovery.js";
63
64
  import {
64
65
  acquireSessionLock,
@@ -200,6 +201,8 @@ import {
200
201
  detectWorkingTreeActivity,
201
202
  } from "./auto-supervisor.js";
202
203
  import { isDbAvailable, getMilestone } from "./gsd-db.js";
204
+ import { markLatestActiveForWorkerCanceled } from "./db/unit-dispatches.js";
205
+ import { writeUnitRuntimeRecord } from "./unit-runtime.js";
203
206
  import { countPendingCaptures } from "./captures.js";
204
207
  import { CMUX_CHANNELS, type CmuxLogLevel } from "../shared/cmux-events.js";
205
208
  import { ensureDbOpen } from "./bootstrap/dynamic-tools.js";
@@ -242,6 +245,20 @@ import {
242
245
  type WorktreeResolverDeps,
243
246
  } from "./worktree-resolver.js";
244
247
  import { reorderForCaching } from "./prompt-ordering.js";
248
+ import { initTokenCounter } from "./token-counter.js";
249
+
250
+ // Warm the tiktoken encoder at extension startup so context-budget computations
251
+ // can use accurate token counts via countTokensSync without paying the load
252
+ // cost mid-prompt-build. Fire-and-forget — failure falls back to the
253
+ // provider-aware char-ratio estimator already used by getCharsPerToken().
254
+ // Catch rejections explicitly: an unhandled rejection at module-import time
255
+ // can destabilize startup before the engine logger is configured.
256
+ void initTokenCounter().catch((err) => {
257
+ logWarning(
258
+ "engine",
259
+ `token counter warm-up failed: ${err instanceof Error ? err.message : String(err)}`,
260
+ );
261
+ });
245
262
 
246
263
  // ─── Session State ─────────────────────────────────────────────────────────
247
264
 
@@ -293,11 +310,15 @@ const STATE_REBUILD_MIN_INTERVAL_MS = 30_000;
293
310
  * the DB is unavailable (e.g. fresh project before init) we skip registration
294
311
  * silently rather than blocking session start.
295
312
  */
296
- function registerAutoWorkerForSession(session: AutoSession): void {
313
+ function registerAutoWorkerForSession(
314
+ session: AutoSession,
315
+ projectRootOverride?: string,
316
+ ): void {
297
317
  if (session.workerId) return; // already registered (e.g. resume re-runs)
298
318
  try {
299
319
  const projectRootRealpath = normalizeRealPath(
300
- session.scope?.workspace.projectRoot
320
+ projectRootOverride
321
+ ?? session.scope?.workspace.projectRoot
301
322
  ?? (session.originalBasePath || session.basePath),
302
323
  );
303
324
  session.workerId = registerAutoWorker({ projectRootRealpath });
@@ -420,6 +441,17 @@ export function _synthesizePausedSessionRecoveryForTest(
420
441
  return synthesizePausedSessionRecovery(basePath, unitType, unitId, sessionFile);
421
442
  }
422
443
 
444
+ const DETACHED_AUTO_KEEPALIVE_INTERVAL_MS = 30_000;
445
+
446
+ function withDetachedAutoKeepalive<T>(run: Promise<T>): Promise<T> {
447
+ const keepAlive = setInterval(() => {}, DETACHED_AUTO_KEEPALIVE_INTERVAL_MS);
448
+ return run.finally(() => {
449
+ clearInterval(keepAlive);
450
+ });
451
+ }
452
+
453
+ export const _withDetachedAutoKeepaliveForTest = withDetachedAutoKeepalive;
454
+
423
455
  export function startAutoDetached(
424
456
  ctx: ExtensionCommandContext,
425
457
  pi: ExtensionAPI,
@@ -431,7 +463,7 @@ export function startAutoDetached(
431
463
  milestoneLock?: string | null;
432
464
  },
433
465
  ): void {
434
- void startAuto(ctx, pi, base, verboseMode, options).catch((err) => {
466
+ void withDetachedAutoKeepalive(startAuto(ctx, pi, base, verboseMode, options)).catch((err) => {
435
467
  const message = getErrorMessage(err);
436
468
  ctx.ui.notify(`Auto-start failed: ${message}`, "error");
437
469
  logWarning("engine", `auto start error: ${message}`, { file: "auto.ts" });
@@ -490,9 +522,54 @@ export {
490
522
  getBudgetEnforcementAction,
491
523
  } from "./auto-budget.js";
492
524
 
525
+ function closeOutSignalInterruptedUnit(currentBasePath: string): void {
526
+ const currentUnit = s.currentUnit;
527
+ if (!currentUnit) return;
528
+
529
+ const reason = "Auto-mode process received a termination signal";
530
+ const errorContext: ErrorContext = {
531
+ message: reason,
532
+ category: "aborted",
533
+ isTransient: false,
534
+ };
535
+ const basePath = s.basePath || currentBasePath;
536
+
537
+ try {
538
+ emitOpenUnitEndForUnit(basePath, currentUnit.type, currentUnit.id, "cancelled", errorContext);
539
+ } catch (err) {
540
+ logWarning("engine", `signal unit-end cleanup failed: ${getErrorMessage(err)}`, { file: "auto.ts" });
541
+ }
542
+
543
+ try {
544
+ writeUnitRuntimeRecord(basePath, currentUnit.type, currentUnit.id, currentUnit.startedAt, {
545
+ phase: "crashed",
546
+ lastProgressAt: Date.now(),
547
+ lastProgressKind: "signal",
548
+ });
549
+ } catch (err) {
550
+ logWarning("engine", `signal runtime cleanup failed: ${getErrorMessage(err)}`, { file: "auto.ts" });
551
+ }
552
+
553
+ try {
554
+ if (s.workerId) markLatestActiveForWorkerCanceled(s.workerId, "signal-exit");
555
+ } catch (err) {
556
+ logWarning("engine", `signal dispatch cleanup failed: ${getErrorMessage(err)}`, { file: "auto.ts" });
557
+ }
558
+
559
+ try {
560
+ resolveAgentEndCancelled(errorContext);
561
+ } catch (err) {
562
+ logWarning("engine", `signal resolve cleanup failed: ${getErrorMessage(err)}`, { file: "auto.ts" });
563
+ }
564
+ }
565
+
493
566
  /** Wrapper: register SIGTERM handler and store reference. */
494
567
  function registerSigtermHandler(currentBasePath: string): void {
495
- s.sigtermHandler = _registerSigtermHandler(currentBasePath, s.sigtermHandler);
568
+ s.sigtermHandler = _registerSigtermHandler(
569
+ currentBasePath,
570
+ s.sigtermHandler,
571
+ () => closeOutSignalInterruptedUnit(currentBasePath),
572
+ );
496
573
  }
497
574
 
498
575
  /** Wrapper: deregister SIGTERM handler and clear reference. */
@@ -957,6 +1034,8 @@ export async function stopAuto(
957
1034
  if (s.workerId) {
958
1035
  markWorkerStopping(s.workerId);
959
1036
  }
1037
+ s.workerId = null;
1038
+ s.milestoneLeaseToken = null;
960
1039
  } catch (e) {
961
1040
  debugLog("stop-cleanup-coordination", { error: e instanceof Error ? e.message : String(e) });
962
1041
  }
@@ -1960,6 +2039,10 @@ export async function startAuto(
1960
2039
  : new URL("../../../resource-loader.js", import.meta.url).href;
1961
2040
  const { initResources } = await import(resourceLoaderPath);
1962
2041
  initResources(agentDir);
2042
+ // initResources() uses synchronous fs APIs, so the prompt-template cache
2043
+ // can be primed immediately — no need for the legacy 1s setTimeout deferral.
2044
+ const { primeCache } = await import("./prompt-loader.js");
2045
+ primeCache();
1963
2046
  // Open the project DB before rebuild/derive so resume uses DB-backed
1964
2047
  // state instead of falling back to stale markdown parsing (#2940).
1965
2048
  await openProjectDbIfPresent(s.basePath);
@@ -2046,6 +2129,11 @@ export async function startAuto(
2046
2129
  buildResolver,
2047
2130
  };
2048
2131
 
2132
+ // Register the worker before bootstrap enters a milestone worktree.
2133
+ // This ensures enterMilestone can claim a lease and seed dispatch claims
2134
+ // for crash-recovery fidelity (#5405).
2135
+ registerAutoWorkerForSession(s, base);
2136
+
2049
2137
  const ready = await bootstrapAutoSession(
2050
2138
  s,
2051
2139
  ctx,