gsd-pi 2.80.0-dev.c5c38454b → 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 (266) 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/gsd/auto/loop.js +32 -1
  5. package/dist/resources/extensions/gsd/auto/phases.js +66 -45
  6. package/dist/resources/extensions/gsd/auto/resolve.js +17 -0
  7. package/dist/resources/extensions/gsd/auto/run-unit.js +13 -1
  8. package/dist/resources/extensions/gsd/auto-post-unit.js +10 -10
  9. package/dist/resources/extensions/gsd/auto-prompts.js +124 -2
  10. package/dist/resources/extensions/gsd/auto-recovery.js +43 -1
  11. package/dist/resources/extensions/gsd/auto-supervisor.js +8 -1
  12. package/dist/resources/extensions/gsd/auto-timeout-recovery.js +2 -2
  13. package/dist/resources/extensions/gsd/auto.js +77 -5
  14. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +21 -2
  15. package/dist/resources/extensions/gsd/bootstrap/exec-tools.js +27 -20
  16. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +21 -0
  17. package/dist/resources/extensions/gsd/clean-root-preflight.js +42 -4
  18. package/dist/resources/extensions/gsd/commands/dispatcher.js +5 -0
  19. package/dist/resources/extensions/gsd/context-budget.js +37 -2
  20. package/dist/resources/extensions/gsd/crash-recovery.js +56 -10
  21. package/dist/resources/extensions/gsd/db/unit-dispatches.js +39 -0
  22. package/dist/resources/extensions/gsd/db-base-schema.js +4 -2
  23. package/dist/resources/extensions/gsd/db-migration-steps.js +6 -0
  24. package/dist/resources/extensions/gsd/detection.js +106 -0
  25. package/dist/resources/extensions/gsd/git-service.js +36 -4
  26. package/dist/resources/extensions/gsd/gsd-db.js +46 -13
  27. package/dist/resources/extensions/gsd/guided-flow.js +80 -14
  28. package/dist/resources/extensions/gsd/memory-store.js +69 -12
  29. package/dist/resources/extensions/gsd/migrate/command.js +40 -1
  30. package/dist/resources/extensions/gsd/migration-auto-check.js +87 -0
  31. package/dist/resources/extensions/gsd/planning-path-scope.js +26 -0
  32. package/dist/resources/extensions/gsd/pre-execution-checks.js +7 -0
  33. package/dist/resources/extensions/gsd/prompt-loader.js +28 -2
  34. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +19 -19
  35. package/dist/resources/extensions/gsd/prompts/parallel-research-slices.md +1 -1
  36. package/dist/resources/extensions/gsd/prompts/plan-milestone.md +3 -1
  37. package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  38. package/dist/resources/extensions/gsd/prompts/quick-task.md +1 -5
  39. package/dist/resources/extensions/gsd/prompts/validate-milestone.md +2 -2
  40. package/dist/resources/extensions/gsd/quick.js +34 -2
  41. package/dist/resources/extensions/gsd/safety/evidence-collector.js +10 -2
  42. package/dist/resources/extensions/gsd/tools/context-mode-tool-result.js +15 -0
  43. package/dist/resources/extensions/gsd/tools/exec-search-tool.js +5 -0
  44. package/dist/resources/extensions/gsd/tools/exec-tool.js +3 -15
  45. package/dist/resources/extensions/gsd/tools/memory-tools.js +1 -0
  46. package/dist/resources/extensions/gsd/tools/plan-slice.js +9 -0
  47. package/dist/resources/extensions/gsd/tools/plan-task.js +9 -0
  48. package/dist/resources/extensions/gsd/tools/resume-tool.js +5 -0
  49. package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +1 -1
  50. package/dist/resources/extensions/gsd/unit-context-composer.js +12 -3
  51. package/dist/resources/extensions/gsd/unit-runtime.js +22 -0
  52. package/dist/resources/extensions/gsd/worktree-manager.js +16 -14
  53. package/dist/resources/extensions/gsd/worktree-resolver.js +33 -17
  54. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  55. package/dist/web/standalone/.next/BUILD_ID +1 -1
  56. package/dist/web/standalone/.next/app-path-routes-manifest.json +7 -7
  57. package/dist/web/standalone/.next/build-manifest.json +2 -2
  58. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  59. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  60. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  61. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  62. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  63. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  64. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  65. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  66. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  67. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  68. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  69. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  70. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  71. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  72. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  73. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  74. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  75. package/dist/web/standalone/.next/server/app/index.html +1 -1
  76. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  77. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  78. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  79. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  80. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  81. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  82. package/dist/web/standalone/.next/server/app-paths-manifest.json +7 -7
  83. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  84. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  85. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  86. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  87. package/package.json +3 -3
  88. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  89. package/packages/mcp-server/dist/workflow-tools.js +22 -17
  90. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  91. package/packages/mcp-server/src/workflow-tools.test.ts +75 -2
  92. package/packages/mcp-server/src/workflow-tools.ts +30 -16
  93. package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
  94. package/packages/native/tsconfig.tsbuildinfo +1 -1
  95. package/packages/pi-coding-agent/dist/core/agent-session-abort-order.test.js +32 -0
  96. package/packages/pi-coding-agent/dist/core/agent-session-abort-order.test.js.map +1 -1
  97. package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
  98. package/packages/pi-coding-agent/dist/core/agent-session.js +8 -0
  99. package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
  100. package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js +32 -0
  101. package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js.map +1 -1
  102. package/packages/pi-coding-agent/dist/core/compaction/compaction.d.ts +11 -0
  103. package/packages/pi-coding-agent/dist/core/compaction/compaction.d.ts.map +1 -1
  104. package/packages/pi-coding-agent/dist/core/compaction/compaction.js +9 -0
  105. package/packages/pi-coding-agent/dist/core/compaction/compaction.js.map +1 -1
  106. package/packages/pi-coding-agent/dist/core/compaction-threshold.test.d.ts +2 -0
  107. package/packages/pi-coding-agent/dist/core/compaction-threshold.test.d.ts.map +1 -0
  108. package/packages/pi-coding-agent/dist/core/compaction-threshold.test.js +103 -0
  109. package/packages/pi-coding-agent/dist/core/compaction-threshold.test.js.map +1 -0
  110. package/packages/pi-coding-agent/dist/core/db-snapshot.d.ts +15 -0
  111. package/packages/pi-coding-agent/dist/core/db-snapshot.d.ts.map +1 -0
  112. package/packages/pi-coding-agent/dist/core/db-snapshot.js +66 -0
  113. package/packages/pi-coding-agent/dist/core/db-snapshot.js.map +1 -0
  114. package/packages/pi-coding-agent/dist/core/db-snapshot.test.d.ts +2 -0
  115. package/packages/pi-coding-agent/dist/core/db-snapshot.test.d.ts.map +1 -0
  116. package/packages/pi-coding-agent/dist/core/db-snapshot.test.js +24 -0
  117. package/packages/pi-coding-agent/dist/core/db-snapshot.test.js.map +1 -0
  118. package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts +1 -0
  119. package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts.map +1 -1
  120. package/packages/pi-coding-agent/dist/core/extensions/runner.js +3 -0
  121. package/packages/pi-coding-agent/dist/core/extensions/runner.js.map +1 -1
  122. package/packages/pi-coding-agent/dist/core/extensions/runner.test.js +2 -0
  123. package/packages/pi-coding-agent/dist/core/extensions/runner.test.js.map +1 -1
  124. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +7 -0
  125. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
  126. package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
  127. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +20 -0
  128. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
  129. package/packages/pi-coding-agent/dist/core/settings-manager.js +25 -0
  130. package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
  131. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts +1 -0
  132. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  133. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +3 -0
  134. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
  135. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  136. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +15 -5
  137. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  138. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.test.js +53 -0
  139. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.test.js.map +1 -1
  140. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  141. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +3 -0
  142. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  143. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage-safety-guard.test.d.ts +2 -0
  144. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage-safety-guard.test.d.ts.map +1 -0
  145. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage-safety-guard.test.js +10 -0
  146. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage-safety-guard.test.js.map +1 -0
  147. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.d.ts.map +1 -1
  148. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.js +3 -2
  149. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.js.map +1 -1
  150. package/packages/pi-coding-agent/src/core/agent-session-abort-order.test.ts +36 -0
  151. package/packages/pi-coding-agent/src/core/agent-session.ts +8 -0
  152. package/packages/pi-coding-agent/src/core/chat-controller-ordering.test.ts +38 -0
  153. package/packages/pi-coding-agent/src/core/compaction/compaction.ts +18 -0
  154. package/packages/pi-coding-agent/src/core/compaction-threshold.test.ts +121 -0
  155. package/packages/pi-coding-agent/src/core/db-snapshot.test.ts +32 -0
  156. package/packages/pi-coding-agent/src/core/db-snapshot.ts +66 -0
  157. package/packages/pi-coding-agent/src/core/extensions/runner.test.ts +2 -0
  158. package/packages/pi-coding-agent/src/core/extensions/runner.ts +3 -0
  159. package/packages/pi-coding-agent/src/core/extensions/types.ts +7 -0
  160. package/packages/pi-coding-agent/src/core/settings-manager.ts +39 -1
  161. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +4 -0
  162. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.test.ts +56 -0
  163. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +24 -7
  164. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +3 -0
  165. package/packages/pi-coding-agent/src/resources/extensions/memory/storage-safety-guard.test.ts +14 -0
  166. package/packages/pi-coding-agent/src/resources/extensions/memory/storage.ts +3 -2
  167. package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
  168. package/packages/pi-tui/dist/tui.d.ts.map +1 -1
  169. package/packages/pi-tui/dist/tui.js +18 -8
  170. package/packages/pi-tui/dist/tui.js.map +1 -1
  171. package/packages/pi-tui/src/tui.ts +20 -8
  172. package/packages/pi-tui/tsconfig.tsbuildinfo +1 -1
  173. package/src/resources/GSD-WORKFLOW.md +2 -2
  174. package/src/resources/extensions/gsd/auto/loop-deps.ts +1 -0
  175. package/src/resources/extensions/gsd/auto/loop.ts +50 -8
  176. package/src/resources/extensions/gsd/auto/phases.ts +77 -48
  177. package/src/resources/extensions/gsd/auto/resolve.ts +23 -1
  178. package/src/resources/extensions/gsd/auto/run-unit.ts +18 -1
  179. package/src/resources/extensions/gsd/auto-post-unit.ts +10 -10
  180. package/src/resources/extensions/gsd/auto-prompts.ts +133 -2
  181. package/src/resources/extensions/gsd/auto-recovery.ts +54 -0
  182. package/src/resources/extensions/gsd/auto-supervisor.ts +7 -0
  183. package/src/resources/extensions/gsd/auto-timeout-recovery.ts +2 -2
  184. package/src/resources/extensions/gsd/auto.ts +92 -4
  185. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +21 -1
  186. package/src/resources/extensions/gsd/bootstrap/exec-tools.ts +27 -19
  187. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +22 -0
  188. package/src/resources/extensions/gsd/clean-root-preflight.ts +41 -3
  189. package/src/resources/extensions/gsd/commands/dispatcher.ts +6 -0
  190. package/src/resources/extensions/gsd/context-budget.ts +44 -2
  191. package/src/resources/extensions/gsd/crash-recovery.ts +67 -10
  192. package/src/resources/extensions/gsd/db/unit-dispatches.ts +41 -0
  193. package/src/resources/extensions/gsd/db-base-schema.ts +4 -2
  194. package/src/resources/extensions/gsd/db-migration-steps.ts +8 -0
  195. package/src/resources/extensions/gsd/detection.ts +128 -0
  196. package/src/resources/extensions/gsd/git-service.ts +46 -8
  197. package/src/resources/extensions/gsd/gsd-db.ts +50 -13
  198. package/src/resources/extensions/gsd/guided-flow.ts +96 -14
  199. package/src/resources/extensions/gsd/memory-store.ts +77 -12
  200. package/src/resources/extensions/gsd/migrate/command.ts +47 -1
  201. package/src/resources/extensions/gsd/migration-auto-check.ts +129 -0
  202. package/src/resources/extensions/gsd/planning-path-scope.ts +35 -0
  203. package/src/resources/extensions/gsd/pre-execution-checks.ts +7 -0
  204. package/src/resources/extensions/gsd/preferences-types.ts +1 -1
  205. package/src/resources/extensions/gsd/prompt-loader.ts +27 -2
  206. package/src/resources/extensions/gsd/prompts/complete-milestone.md +19 -19
  207. package/src/resources/extensions/gsd/prompts/parallel-research-slices.md +1 -1
  208. package/src/resources/extensions/gsd/prompts/plan-milestone.md +3 -1
  209. package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  210. package/src/resources/extensions/gsd/prompts/quick-task.md +1 -5
  211. package/src/resources/extensions/gsd/prompts/validate-milestone.md +2 -2
  212. package/src/resources/extensions/gsd/quick.ts +37 -2
  213. package/src/resources/extensions/gsd/safety/evidence-collector.ts +11 -2
  214. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +72 -1
  215. package/src/resources/extensions/gsd/tests/auto-phases-lifecycle.test.ts +56 -13
  216. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +14 -1
  217. package/src/resources/extensions/gsd/tests/clean-root-preflight.test.ts +88 -2
  218. package/src/resources/extensions/gsd/tests/compaction-snapshot.test.ts +14 -1
  219. package/src/resources/extensions/gsd/tests/context-budget.test.ts +10 -1
  220. package/src/resources/extensions/gsd/tests/crash-handler-secondary.test.ts +55 -0
  221. package/src/resources/extensions/gsd/tests/crash-recovery-via-db.test.ts +22 -0
  222. package/src/resources/extensions/gsd/tests/detection.test.ts +140 -0
  223. package/src/resources/extensions/gsd/tests/dispatch-rule-coverage.test.ts +313 -0
  224. package/src/resources/extensions/gsd/tests/exec-history.test.ts +15 -0
  225. package/src/resources/extensions/gsd/tests/exec-sandbox.test.ts +65 -0
  226. package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +54 -0
  227. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +234 -0
  228. package/src/resources/extensions/gsd/tests/memory-decay-factor.test.ts +90 -0
  229. package/src/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +48 -0
  230. package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +127 -0
  231. package/src/resources/extensions/gsd/tests/plan-slice.test.ts +50 -0
  232. package/src/resources/extensions/gsd/tests/plan-task.test.ts +21 -0
  233. package/src/resources/extensions/gsd/tests/pre-execution-checks.test.ts +38 -0
  234. package/src/resources/extensions/gsd/tests/prompt-path-audit.test.ts +40 -0
  235. package/src/resources/extensions/gsd/tests/prompt-step-ordering.test.ts +19 -0
  236. package/src/resources/extensions/gsd/tests/quick-external-gsd.test.ts +40 -0
  237. package/src/resources/extensions/gsd/tests/right-sized-workflow-prompts.test.ts +192 -0
  238. package/src/resources/extensions/gsd/tests/safety-harness-false-positives.test.ts +29 -0
  239. package/src/resources/extensions/gsd/tests/schema-v27-v28-sequence.test.ts +156 -0
  240. package/src/resources/extensions/gsd/tests/signal-handlers.test.ts +27 -0
  241. package/src/resources/extensions/gsd/tests/smart-entry-complete.test.ts +38 -0
  242. package/src/resources/extensions/gsd/tests/stalled-tool-recovery.test.ts +49 -1
  243. package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +101 -2
  244. package/src/resources/extensions/gsd/tests/status-db-open.test.ts +9 -0
  245. package/src/resources/extensions/gsd/tests/unit-context-composer.test.ts +136 -4
  246. package/src/resources/extensions/gsd/tests/unit-dispatches.test.ts +30 -0
  247. package/src/resources/extensions/gsd/tests/unit-runtime.test.ts +37 -0
  248. package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +3 -0
  249. package/src/resources/extensions/gsd/tests/worktree-health-dispatch.test.ts +37 -6
  250. package/src/resources/extensions/gsd/tests/worktree-manager.test.ts +7 -0
  251. package/src/resources/extensions/gsd/tests/worktree-nested-git-safety.test.ts +9 -2
  252. package/src/resources/extensions/gsd/tests/worktree-resolver.test.ts +63 -1
  253. package/src/resources/extensions/gsd/tools/context-mode-tool-result.ts +25 -0
  254. package/src/resources/extensions/gsd/tools/exec-search-tool.ts +7 -7
  255. package/src/resources/extensions/gsd/tools/exec-tool.ts +4 -23
  256. package/src/resources/extensions/gsd/tools/memory-tools.ts +1 -0
  257. package/src/resources/extensions/gsd/tools/plan-slice.ts +13 -0
  258. package/src/resources/extensions/gsd/tools/plan-task.ts +10 -0
  259. package/src/resources/extensions/gsd/tools/resume-tool.ts +7 -7
  260. package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +1 -1
  261. package/src/resources/extensions/gsd/unit-context-composer.ts +19 -4
  262. package/src/resources/extensions/gsd/unit-runtime.ts +25 -0
  263. package/src/resources/extensions/gsd/worktree-manager.ts +15 -4
  264. package/src/resources/extensions/gsd/worktree-resolver.ts +36 -15
  265. /package/dist/web/standalone/.next/static/{TCSim36ZpcPu2WgeoC45g → -5nHJWzSdG-WkPMul_khA}/_buildManifest.js +0 -0
  266. /package/dist/web/standalone/.next/static/{TCSim36ZpcPu2WgeoC45g → -5nHJWzSdG-WkPMul_khA}/_ssgManifest.js +0 -0
package/README.md CHANGED
@@ -259,6 +259,7 @@ Full documentation is in the [`docs/`](./docs/) directory:
259
259
 
260
260
  - **[Architecture](./docs/dev/architecture.md)** — system design and dispatch pipeline
261
261
  - **[CI/CD Pipeline](./docs/dev/ci-cd-pipeline.md)** — three-stage promotion pipeline (Dev → Test → Prod)
262
+ - **[E2E Testing](./tests/e2e/README.md)** — real-process CLI/runtime coverage and CI runner expectations
262
263
  - **[Pipeline Simplification (ADR-003)](./docs/dev/ADR-003-pipeline-simplification.md)** — merged research into planning, mechanical completion
263
264
  - **[VS Code Extension](./vscode-extension/README.md)** — chat participant, sidebar dashboard, RPC integration
264
265
 
@@ -361,7 +362,7 @@ The database is authoritative for milestones, slices, tasks, requirements, decis
361
362
 
362
363
  2. **Context pre-loading** — The dispatch prompt includes inlined task plans, slice plans, prior task summaries, dependency summaries, roadmap excerpts, and decisions register. The LLM starts with everything it needs instead of spending tool calls reading files.
363
364
 
364
- 3. **Context Mode** — Context Mode is enabled by default and gives every auto-mode unit guidance for preserving context. Agents are steered toward `gsd_exec` for noisy scans, builds, tests, and diagnostics; full stdout/stderr is saved under `.gsd/exec/` while only a short digest enters the conversation. `gsd_exec_search` lets agents reuse prior runs instead of repeating expensive checks, and `gsd_resume` reads `.gsd/last-snapshot.md` after compaction or resume. Opt out with `context_mode.enabled: false`; tune sandbox timeout/output caps with `context_mode.exec_timeout_ms`, `context_mode.exec_stdout_cap_bytes`, and `context_mode.exec_digest_chars`.
365
+ 3. **Context Mode** — Context Mode is enabled by default and gives eligible auto-mode units guidance for preserving context. Agents are steered toward `gsd_exec` for noisy scans, builds, tests, and diagnostics; capped stdout/stderr and metadata are saved under `.gsd/exec/` while only a short digest enters the conversation. `gsd_exec_search` lets agents reuse prior runs instead of repeating expensive checks, and `gsd_resume` reads a prior compaction snapshot from `.gsd/last-snapshot.md` when one exists. Opt out with `context_mode.enabled: false` to disable Context Mode guidance, snapshot injection, `gsd_exec`, `gsd_exec_search`, and `gsd_resume`; tune sandbox timeout/output caps and environment forwarding with `context_mode.exec_timeout_ms`, `context_mode.exec_stdout_cap_bytes`, `context_mode.exec_digest_chars`, and `context_mode.exec_env_allowlist`.
365
366
 
366
367
  4. **Git isolation** — When `git.isolation` is set to `worktree` or `branch`, each milestone runs on its own `milestone/<MID>` branch (in a worktree or in-place). All slice work commits sequentially — no branch switching, no merge conflicts. When the milestone completes, it's squash-merged to main as one clean commit. The default is `none` (work on the current branch), configurable via preferences. If `worktree` is configured in a repo with no committed `HEAD`, GSD temporarily behaves as `none` until the first commit exists because git worktrees need a committed start point.
367
368
 
@@ -659,10 +660,11 @@ auto_report: true
659
660
  | `unique_milestone_ids` | Uses unique milestone names to avoid clashes when working in teams of people |
660
661
  | `git.isolation` | `none` (default), `worktree`, or `branch` — enable worktree or branch isolation for milestone work. `worktree` requires a committed `HEAD`; zero-commit repos temporarily run as `none` |
661
662
  | `git.manage_gitignore` | Set `false` to prevent GSD from modifying `.gitignore` |
662
- | `context_mode.enabled` | Context Mode is default-on; set `false` to disable `gsd_exec`, exec history guidance, and resume snapshots |
663
+ | `context_mode.enabled` | Context Mode is default-on; set `false` to disable prompt guidance, snapshot injection, `gsd_exec`, `gsd_exec_search`, and `gsd_resume` |
663
664
  | `context_mode.exec_timeout_ms` | Timeout for sandboxed `gsd_exec` runs (default: 30000) |
664
665
  | `context_mode.exec_stdout_cap_bytes` | Persisted stdout cap for `gsd_exec` output (default: 1048576) |
665
666
  | `context_mode.exec_digest_chars` | Trailing stdout characters returned to the agent context (default: 300) |
667
+ | `context_mode.exec_env_allowlist` | Environment variables forwarded to sandboxed `gsd_exec` runs in addition to `PATH` and `HOME` |
666
668
  | `verification_commands` | Array of shell commands to run after task execution (e.g., `["npm run lint", "npm run test"]`) |
667
669
  | `verification_auto_fix` | Auto-retry on verification failures (default: true) |
668
670
  | `verification_max_retries` | Max retries for verification failures (default: 2) |
@@ -1 +1 @@
1
- 7088672cce649c64
1
+ 9ae4402464ddbafa
@@ -28,7 +28,7 @@ Then do the thing `STATE.md` says to do next.
28
28
  ## The Hierarchy
29
29
 
30
30
  ```
31
- Milestone → a shippable version (4-10 slices)
31
+ Milestone → a shippable version (1-10 slices, sized to the work)
32
32
  Slice → one demoable vertical capability (1-7 tasks)
33
33
  Task → one context-window-sized unit of work (fits in one session)
34
34
  ```
@@ -331,7 +331,7 @@ The **Don't Hand-Roll** and **Common Pitfalls** sections prevent the most expens
331
331
 
332
332
  **For a milestone (roadmap):**
333
333
  1. Read `M###-CONTEXT.md`, `M###-RESEARCH.md`, and `.gsd/DECISIONS.md` if they exist.
334
- 2. Decompose the vision into 4-10 demoable vertical slices.
334
+ 2. Decompose the vision into 1-10 demoable vertical slices. Prefer one slice for tiny, single-file, or static work unless the request clearly spans independent capabilities.
335
335
  3. Order by risk (high-risk first to validate feasibility early).
336
336
  4. Write `M###-ROADMAP.md` with checkboxes, risk levels, dependencies, demo sentences.
337
337
  5. **Write the boundary map** — for each slice, specify what it produces (functions, types, interfaces, endpoints) and what it consumes from upstream slices. This forces interface thinking before implementation and enables deterministic verification that slices actually connect.
@@ -25,6 +25,8 @@ import { normalizeRealPath } from "../paths.js";
25
25
  import { decideCooldownRecovery, decideDispatchClaim, decideEngineDispatch, decideFinalizeResult, decideInfrastructureError, decideIterationErrorRecovery, decideMemoryPressure, decideModelPolicyBlocked, decideMinRequestInterval, decideWorkflowLoop, formatDispatchExceptionSummary, formatUnhandledDispatchErrorSummary, resolveUnitRequestTimestamp, shouldUseCustomEnginePath, } from "./workflow-kernel.js";
26
26
  import { hydrateCustomVerifyRetryCounts, saveCustomVerifyRetryCounts, } from "./custom-verify-retry-store.js";
27
27
  import { settleDispatchCompleted, settleDispatchFailed, } from "./workflow-dispatch-ledger.js";
28
+ import { emitOpenUnitEndForUnit } from "../crash-recovery.js";
29
+ import { writeUnitRuntimeRecord } from "../unit-runtime.js";
28
30
  import { openDispatchClaim } from "./workflow-dispatch-claim.js";
29
31
  import { completeWorkflowIteration } from "./workflow-iteration-completion.js";
30
32
  import { createWorkflowJournalReporter } from "./workflow-journal-reporter.js";
@@ -133,6 +135,24 @@ async function enforceMinRequestInterval(s, prefs) {
133
135
  await new Promise(r => setTimeout(r, decision.waitMs));
134
136
  }
135
137
  }
138
+ function closeOutCrashedUnit(s, iterData, err) {
139
+ const summary = formatDispatchExceptionSummary({ error: err });
140
+ try {
141
+ emitOpenUnitEndForUnit(s.basePath, iterData.unitType, iterData.unitId, "cancelled", {
142
+ message: summary,
143
+ category: "unit-exception",
144
+ isTransient: false,
145
+ });
146
+ writeUnitRuntimeRecord(s.basePath, iterData.unitType, iterData.unitId, s.currentUnit?.startedAt ?? Date.now(), {
147
+ phase: "crashed",
148
+ lastProgressAt: Date.now(),
149
+ lastProgressKind: "unit-exception",
150
+ });
151
+ }
152
+ catch (closeoutErr) {
153
+ logWarning("dispatch", `unit crash closeout failed: ${closeoutErr instanceof Error ? closeoutErr.message : String(closeoutErr)}`);
154
+ }
155
+ }
136
156
  /**
137
157
  * Main auto-mode execution loop. Iterates: derive → dispatch → guards →
138
158
  * runUnit → finalize → repeat. Exits when s.active becomes false or a
@@ -392,7 +412,17 @@ export async function autoLoop(ctx, pi, s, deps, options) {
392
412
  }
393
413
  // ── Unit execution (shared with dev path) ──
394
414
  await enforceMinRequestInterval(s, prefs);
395
- const unitPhaseResult = await runUnitPhaseViaContract(dispatchContract, ic, iterData, loopState, undefined, unitDispatchDeps);
415
+ let unitPhaseResult;
416
+ try {
417
+ unitPhaseResult = await runUnitPhaseViaContract(dispatchContract, ic, iterData, loopState, undefined, unitDispatchDeps);
418
+ }
419
+ catch (err) {
420
+ if (err instanceof ModelPolicyDispatchBlockedError) {
421
+ throw err;
422
+ }
423
+ closeOutCrashedUnit(s, iterData, err);
424
+ throw err;
425
+ }
396
426
  if (unitPhaseResult.action === "next") {
397
427
  const requestTimestamp = resolveUnitRequestTimestamp(unitPhaseResult.data);
398
428
  if (requestTimestamp !== undefined)
@@ -580,6 +610,7 @@ export async function autoLoop(ctx, pi, s, deps, options) {
580
610
  if (err instanceof ModelPolicyDispatchBlockedError) {
581
611
  throw err;
582
612
  }
613
+ closeOutCrashedUnit(s, iterData, err);
583
614
  dispatchSettled = settleDispatchFailed(dispatchId, formatDispatchExceptionSummary({ error: err }), {
584
615
  markFailed: markDispatchFailed,
585
616
  logWriteFailure: logDispatchLedgerWriteFailure,
@@ -13,22 +13,22 @@ import { detectStuck } from "./detect-stuck.js";
13
13
  import { runUnit } from "./run-unit.js";
14
14
  import { debugLog } from "../debug-logger.js";
15
15
  import { resolveWorktreeProjectRoot, normalizeWorktreePathForCompare } from "../worktree-root.js";
16
- import { PROJECT_FILES, hasProjectFileInAncestor } from "../detection.js";
16
+ import { classifyProject } from "../detection.js";
17
17
  import { MergeConflictError } from "../git-service.js";
18
18
  import { setCurrentPhase, clearCurrentPhase } from "../../shared/gsd-phase-state.js";
19
19
  import { pauseAutoForProviderError } from "../provider-error-pause.js";
20
20
  import { resumeAutoAfterProviderDelay } from "../bootstrap/provider-error-resume.js";
21
21
  import { join, basename } from "node:path";
22
- import { existsSync, cpSync, readdirSync } from "node:fs";
22
+ import { existsSync, cpSync } from "node:fs";
23
23
  import { logWarning, logError, _resetLogs, drainLogs, drainAndSummarize, formatForNotification, hasAnyIssues, } from "../workflow-logger.js";
24
24
  import { gsdRoot } from "../paths.js";
25
25
  import { atomicWriteSync } from "../atomic-write.js";
26
- import { verifyExpectedArtifact, diagnoseExpectedArtifact, buildLoopRemediationSteps } from "../auto-recovery.js";
26
+ import { verifyExpectedArtifact, diagnoseExpectedArtifact, buildLoopRemediationSteps, refreshRecoveryDbForArtifact } from "../auto-recovery.js";
27
27
  import { writeUnitRuntimeRecord } from "../unit-runtime.js";
28
28
  import { withTimeout, FINALIZE_PRE_TIMEOUT_MS, FINALIZE_POST_TIMEOUT_MS } from "./finalize-timeout.js";
29
29
  import { getEligibleSlices } from "../slice-parallel-eligibility.js";
30
30
  import { startSliceParallel } from "../slice-parallel-orchestrator.js";
31
- import { isDbAvailable, getMilestoneSlices, refreshOpenDatabaseFromDisk } from "../gsd-db.js";
31
+ import { isDbAvailable, getMilestoneSlices } from "../gsd-db.js";
32
32
  import { ensurePlanV2Graph, isEmptyPlanV2GraphResult, isMissingFinalizedContextResult } from "../uok/plan-v2.js";
33
33
  import { resolveUokFlags } from "../uok/flags.js";
34
34
  import { UokGateRunner } from "../uok/gate-runner.js";
@@ -42,13 +42,6 @@ import { getWorkflowTransportSupportError, getRequiredWorkflowToolsForAutoUnit,
42
42
  function isSamePathLocal(a, b) {
43
43
  return normalizeWorktreePathForCompare(a) === normalizeWorktreePathForCompare(b);
44
44
  }
45
- function refreshPlanSliceRecoveryDbIfNeeded(unitType) {
46
- if (unitType !== "plan-slice")
47
- return true;
48
- if (!isDbAvailable())
49
- return true;
50
- return refreshOpenDatabaseFromDisk();
51
- }
52
45
  // ─── Session timeout auto-resume state ────────────────────────────────────────
53
46
  let consecutiveSessionTimeouts = 0;
54
47
  const MAX_SESSION_TIMEOUT_AUTO_RESUMES = 3;
@@ -498,7 +491,7 @@ export async function runPreDispatch(ic, loopState) {
498
491
  }
499
492
  // #2909: postflight — restore stashed changes after successful merge
500
493
  if (preflightTransition.stashPushed) {
501
- deps.postflightPopStash(s.originalBasePath || s.basePath, s.currentMilestoneId, ctx.ui.notify.bind(ctx.ui));
494
+ deps.postflightPopStash(s.originalBasePath || s.basePath, s.currentMilestoneId, preflightTransition.stashMarker, ctx.ui.notify.bind(ctx.ui));
502
495
  }
503
496
  // PR creation (auto_pr) is handled inside mergeMilestoneToMain (#2302)
504
497
  deps.invalidateAllCaches();
@@ -574,7 +567,7 @@ export async function runPreDispatch(ic, loopState) {
574
567
  }
575
568
  // #2909: postflight — restore stashed changes after successful merge
576
569
  if (preflightAllComplete.stashPushed) {
577
- deps.postflightPopStash(s.originalBasePath || s.basePath, s.currentMilestoneId, ctx.ui.notify.bind(ctx.ui));
570
+ deps.postflightPopStash(s.originalBasePath || s.basePath, s.currentMilestoneId, preflightAllComplete.stashMarker, ctx.ui.notify.bind(ctx.ui));
578
571
  }
579
572
  // PR creation (auto_pr) is handled inside mergeMilestoneToMain (#2302)
580
573
  }
@@ -660,7 +653,7 @@ export async function runPreDispatch(ic, loopState) {
660
653
  }
661
654
  // #2909: postflight — restore stashed changes after successful merge
662
655
  if (preflightComplete.stashPushed) {
663
- deps.postflightPopStash(s.originalBasePath || s.basePath, s.currentMilestoneId, ctx.ui.notify.bind(ctx.ui));
656
+ deps.postflightPopStash(s.originalBasePath || s.basePath, s.currentMilestoneId, preflightComplete.stashMarker, ctx.ui.notify.bind(ctx.ui));
664
657
  }
665
658
  // PR creation (auto_pr) is handled inside mergeMilestoneToMain (#2302)
666
659
  }
@@ -795,11 +788,18 @@ export async function runDispatch(ic, preData, loopState) {
795
788
  level: 1,
796
789
  action: "artifact-found",
797
790
  });
798
- ctx.ui.notify(`Stuck recovery: artifact for ${unitType} ${unitId} found on disk. Invalidating caches.`, "info");
799
- if (!refreshPlanSliceRecoveryDbIfNeeded(unitType)) {
800
- ctx.ui.notify(`Stuck recovery found ${unitType} ${unitId} artifacts, but the DB refresh failed. Keeping stuck state for retry.`, "warning");
791
+ const recoveryDb = refreshRecoveryDbForArtifact(unitType, unitId);
792
+ if (!recoveryDb.ok) {
793
+ ctx.ui.notify(recoveryDb.fatal
794
+ ? `${recoveryDb.message} Pausing auto-mode for manual recovery.`
795
+ : `${recoveryDb.message} Keeping stuck state for retry.`, "warning");
796
+ if (recoveryDb.fatal) {
797
+ await deps.pauseAuto(ctx, pi);
798
+ return { action: "break", reason: recoveryDb.reason };
799
+ }
801
800
  return { action: "continue" };
802
801
  }
802
+ ctx.ui.notify(`Stuck recovery: artifact for ${unitType} ${unitId} found on disk. Invalidating caches.`, "info");
803
803
  deps.invalidateAllCaches();
804
804
  loopState.recentUnits.length = 0;
805
805
  loopState.stuckRecoveryAttempts = 0;
@@ -818,13 +818,20 @@ export async function runDispatch(ic, preData, loopState) {
818
818
  level: 2,
819
819
  action: "artifact-found",
820
820
  });
821
- ctx.ui.notify(`Stuck recovery: artifact for ${unitType} ${unitId} found on disk after cache invalidation. Continuing.`, "info");
822
- if (refreshPlanSliceRecoveryDbIfNeeded(unitType)) {
821
+ const recoveryDb = refreshRecoveryDbForArtifact(unitType, unitId);
822
+ if (recoveryDb.ok) {
823
+ ctx.ui.notify(`Stuck recovery: artifact for ${unitType} ${unitId} found on disk after cache invalidation. Continuing.`, "info");
823
824
  loopState.recentUnits.length = 0;
824
825
  loopState.stuckRecoveryAttempts = 0;
825
826
  return { action: "continue" };
826
827
  }
827
- ctx.ui.notify(`Stuck recovery found ${unitType} ${unitId} artifacts, but the DB refresh failed. Stopping for manual recovery.`, "warning");
828
+ ctx.ui.notify(recoveryDb.fatal
829
+ ? `${recoveryDb.message} Pausing auto-mode for manual recovery.`
830
+ : `${recoveryDb.message} Stopping for manual recovery.`, "warning");
831
+ if (recoveryDb.fatal) {
832
+ await deps.pauseAuto(ctx, pi);
833
+ return { action: "break", reason: recoveryDb.reason };
834
+ }
828
835
  }
829
836
  debugLog("autoLoop", {
830
837
  phase: "stuck-detected",
@@ -1084,8 +1091,9 @@ export async function runUnitPhase(ic, iterData, loopState, sidecarItem) {
1084
1091
  // Verify the working directory is a valid git checkout with project
1085
1092
  // files before dispatching work. A broken worktree causes agents to
1086
1093
  // hallucinate summaries since they cannot read or write any files.
1087
- // Uses the shared PROJECT_FILES list from detection.ts to support all
1088
- // ecosystems (Rust, Go, Python, Java, etc.), not just JS.
1094
+ // Uses project classification so project presence is not conflated with
1095
+ // ecosystem marker detection. Static/minimal repos become untyped-existing.
1096
+ let projectClassification = null;
1089
1097
  if (s.basePath && unitType === "execute-task") {
1090
1098
  const gitMarker = join(s.basePath, ".git");
1091
1099
  const hasGit = deps.existsSync(gitMarker);
@@ -1096,30 +1104,26 @@ export async function runUnitPhase(ic, iterData, loopState, sidecarItem) {
1096
1104
  await deps.stopAuto(ctx, pi, msg);
1097
1105
  return { action: "break", reason: "worktree-invalid" };
1098
1106
  }
1099
- const hasProjectFile = PROJECT_FILES.some((f) => deps.existsSync(join(s.basePath, f)));
1100
- const hasSrcDir = deps.existsSync(join(s.basePath, "src"));
1101
- // Xcode bundles have project-specific names (*.xcodeproj, *.xcworkspace)
1102
- // that cannot be matched by exact filename — scan the directory by suffix.
1103
- let hasXcodeBundle = false;
1104
- try {
1105
- const entries = deps.existsSync(s.basePath) ? readdirSync(s.basePath) : [];
1106
- hasXcodeBundle = entries.some((e) => e.endsWith(".xcodeproj") || e.endsWith(".xcworkspace"));
1107
+ projectClassification = classifyProject(s.basePath);
1108
+ if (projectClassification.kind === "invalid-repo") {
1109
+ const msg = `Worktree health check failed: ${s.basePath} classified as invalid-repo (${projectClassification.reason}) — refusing to dispatch ${unitType} ${unitId}`;
1110
+ debugLog("runUnitPhase", { phase: "worktree-health-invalid-repo", basePath: s.basePath, classification: projectClassification });
1111
+ if (projectClassification.reason === "missing .git" && hasGit) {
1112
+ ctx.ui.notify(`Warning: ${s.basePath} project classification could not confirm .git; assuming it has no project content yet — proceeding as greenfield project because worktree health reported .git present`, "warning");
1113
+ }
1114
+ else {
1115
+ ctx.ui.notify(msg, "error");
1116
+ await deps.stopAuto(ctx, pi, msg);
1117
+ return { action: "break", reason: "worktree-invalid" };
1118
+ }
1107
1119
  }
1108
- catch (err) {
1109
- debugLog("runUnitPhase", { phase: "xcode-bundle-scan-failed", basePath: s.basePath, error: String(err) });
1110
- }
1111
- // Monorepo support (#2347): if no project files in the worktree directory,
1112
- // walk parent directories up to the filesystem root. In monorepos,
1113
- // package.json / Cargo.toml etc. live in a parent directory.
1114
- const hasProjectFileInParent = !hasProjectFile && !hasSrcDir && !hasXcodeBundle
1115
- ? hasProjectFileInAncestor(s.basePath, deps.existsSync)
1116
- : false;
1117
- if (!hasProjectFile && !hasSrcDir && !hasXcodeBundle && !hasProjectFileInParent) {
1118
- // Greenfield projects won't have project files yet — the first task creates them.
1119
- // Log a warning but allow execution to proceed. The .git check above is sufficient
1120
- // to ensure we're in a valid working directory.
1121
- debugLog("runUnitPhase", { phase: "worktree-health-warn-greenfield", basePath: s.basePath, hasProjectFile, hasSrcDir, hasXcodeBundle });
1122
- ctx.ui.notify(`Warning: ${s.basePath} has no recognized project files — proceeding as greenfield project`, "warning");
1120
+ else if (projectClassification.kind === "greenfield") {
1121
+ debugLog("runUnitPhase", { phase: "worktree-health-greenfield", basePath: s.basePath, classification: projectClassification });
1122
+ ctx.ui.notify(`Warning: ${s.basePath} has no project content yet — proceeding as greenfield project`, "warning");
1123
+ }
1124
+ else if (projectClassification.kind === "untyped-existing") {
1125
+ debugLog("runUnitPhase", { phase: "worktree-health-untyped-existing", basePath: s.basePath, classification: projectClassification });
1126
+ ctx.ui.notify(`Notice: ${s.basePath} has existing project content but no recognized tooling markers — using generic file-level workflow guidance`, "info");
1123
1127
  }
1124
1128
  }
1125
1129
  // Detect retry and capture previous tier for escalation
@@ -1182,6 +1186,16 @@ export async function runUnitPhase(ic, iterData, loopState, sidecarItem) {
1182
1186
  }
1183
1187
  // Prompt injection
1184
1188
  let finalPrompt = prompt;
1189
+ if (unitType === "execute-task") {
1190
+ projectClassification ??= classifyProject(s.basePath);
1191
+ if (projectClassification.kind === "untyped-existing") {
1192
+ const samples = projectClassification.contentFiles.slice(0, 8).join(", ") || "project files";
1193
+ finalPrompt +=
1194
+ "\n\n**Project classification:** Existing untyped project. No recognized build/tooling markers were detected, " +
1195
+ "so use generic file-level workflow guidance. Task plans and completion summaries must list every concrete " +
1196
+ `project file changed in \`files\` or \`expected_output\`. Detected content sample: ${samples}.`;
1197
+ }
1198
+ }
1185
1199
  if (s.pendingVerificationRetry) {
1186
1200
  const retryCtx = s.pendingVerificationRetry;
1187
1201
  s.pendingVerificationRetry = null;
@@ -1665,6 +1679,13 @@ export async function runFinalize(ic, iterData, loopState, sidecarItem) {
1665
1679
  }
1666
1680
  // Both pre and post verification completed without timeout — reset counter
1667
1681
  loopState.consecutiveFinalizeTimeouts = 0;
1682
+ if (preUnitSnapshot) {
1683
+ writeUnitRuntimeRecord(s.basePath, preUnitSnapshot.type, preUnitSnapshot.id, preUnitSnapshot.startedAt, {
1684
+ phase: "finalized",
1685
+ lastProgressAt: Date.now(),
1686
+ lastProgressKind: "finalize-success",
1687
+ });
1688
+ }
1668
1689
  s.currentUnit = null;
1669
1690
  clearCurrentPhase();
1670
1691
  // Surface accumulated workflow-logger issues for this unit to the user.
@@ -17,6 +17,7 @@ import { bumpTurnGeneration } from "./turn-epoch.js";
17
17
  // scoped pendingResolve + pendingAgentEndQueue pattern.
18
18
  let _currentResolve = null;
19
19
  let _sessionSwitchInFlight = false;
20
+ let _pendingSwitchCancellation = null;
20
21
  // ─── Setters (needed for cross-module mutation) ─────────────────────────────
21
22
  export function _setCurrentResolve(fn) {
22
23
  _currentResolve = fn;
@@ -27,6 +28,11 @@ export function _setSessionSwitchInFlight(v) {
27
28
  export function _clearCurrentResolve() {
28
29
  _currentResolve = null;
29
30
  }
31
+ export function _consumePendingSwitchCancellation() {
32
+ const pending = _pendingSwitchCancellation;
33
+ _pendingSwitchCancellation = null;
34
+ return pending;
35
+ }
30
36
  // ─── resolveAgentEnd ─────────────────────────────────────────────────────────
31
37
  /**
32
38
  * Called from the agent_end event handler in index.ts to resolve the
@@ -90,8 +96,18 @@ export function resolveAgentEndCancelled(errorContext) {
90
96
  debugLog("resolveAgentEndCancelled", { status: "resolving-cancelled" });
91
97
  const r = _currentResolve;
92
98
  _currentResolve = null;
99
+ _pendingSwitchCancellation = null;
93
100
  r({ status: "cancelled", ...(errorContext ? { errorContext } : {}) });
101
+ return true;
102
+ }
103
+ if (_sessionSwitchInFlight) {
104
+ bumpTurnGeneration(`cancelled-during-switch:${errorContext?.category ?? "unknown"}`);
105
+ _pendingSwitchCancellation = errorContext ? { errorContext } : {};
106
+ debugLog("resolveAgentEndCancelled", { status: "queued-during-switch" });
107
+ return false;
94
108
  }
109
+ debugLog("resolveAgentEndCancelled", { status: "no-pending-resolve" });
110
+ return false;
95
111
  }
96
112
  // ─── resetPendingResolve (test helper) ───────────────────────────────────────
97
113
  /**
@@ -101,6 +117,7 @@ export function resolveAgentEndCancelled(errorContext) {
101
117
  export function _resetPendingResolve() {
102
118
  _currentResolve = null;
103
119
  _sessionSwitchInFlight = false;
120
+ _pendingSwitchCancellation = null;
104
121
  }
105
122
  export function _hasPendingResolveForTest() {
106
123
  return _currentResolve !== null;
@@ -1,6 +1,6 @@
1
1
  // GSD-2 + src/resources/extensions/gsd/auto/run-unit.ts - Runs one GSD auto-mode unit from session creation through agent completion.
2
2
  import { NEW_SESSION_TIMEOUT_MS } from "./session.js";
3
- import { _clearCurrentResolve, _setCurrentResolve, _setSessionSwitchInFlight } from "./resolve.js";
3
+ import { _clearCurrentResolve, _consumePendingSwitchCancellation, _setCurrentResolve, _setSessionSwitchInFlight, } from "./resolve.js";
4
4
  import { getCurrentTurnGeneration, runWithTurnGeneration, } from "./turn-epoch.js";
5
5
  import { debugLog } from "../debug-logger.js";
6
6
  import { logWarning } from "../workflow-logger.js";
@@ -71,6 +71,7 @@ export async function runUnit(ctx, pi, s, unitType, unitId, prompt) {
71
71
  catch (sessionErr) {
72
72
  if (sessionTimeoutHandle)
73
73
  clearTimeout(sessionTimeoutHandle);
74
+ _consumePendingSwitchCancellation();
74
75
  const msg = sessionErr instanceof Error ? sessionErr.message : String(sessionErr);
75
76
  debugLog("runUnit", {
76
77
  phase: "session-error",
@@ -83,15 +84,18 @@ export async function runUnit(ctx, pi, s, unitType, unitId, prompt) {
83
84
  if (sessionTimeoutHandle)
84
85
  clearTimeout(sessionTimeoutHandle);
85
86
  if (sessionResult.cancelled) {
87
+ _consumePendingSwitchCancellation();
86
88
  debugLog("runUnit-session-timeout", { unitType, unitId });
87
89
  return { status: "cancelled", errorContext: { message: "Session creation timed out", category: "timeout", isTransient: true } };
88
90
  }
89
91
  if (!s.active) {
92
+ _consumePendingSwitchCancellation();
90
93
  return { status: "cancelled" };
91
94
  }
92
95
  if (s.currentUnitModel && typeof pi.setModel === "function") {
93
96
  const restored = await pi.setModel(s.currentUnitModel, { persist: false });
94
97
  if (!restored) {
98
+ _consumePendingSwitchCancellation();
95
99
  const message = `Failed to restore configured model ${s.currentUnitModel.provider}/${s.currentUnitModel.id} after session creation`;
96
100
  ctx.ui.notify(`${message}. Cancelling unit before dispatch.`, "warning");
97
101
  return {
@@ -111,6 +115,14 @@ export async function runUnit(ctx, pi, s, unitType, unitId, prompt) {
111
115
  const unitPromise = new Promise((resolve) => {
112
116
  _setCurrentResolve(resolve);
113
117
  });
118
+ const pendingSwitchCancellation = _consumePendingSwitchCancellation();
119
+ if (pendingSwitchCancellation) {
120
+ _clearCurrentResolve();
121
+ return {
122
+ status: "cancelled",
123
+ ...(pendingSwitchCancellation.errorContext ? { errorContext: pendingSwitchCancellation.errorContext } : {}),
124
+ };
125
+ }
114
126
  // ── Provider request-readiness pre-check (#4555) ──
115
127
  // Verify the provider can accept requests before dispatching. If the token
116
128
  // has expired since bootstrap, return cancelled immediately so the unit is
@@ -25,7 +25,7 @@ import { verifyExpectedArtifact, resolveExpectedArtifactPath, writeBlockerPlaceh
25
25
  import { regenerateIfMissing } from "./workflow-projections.js";
26
26
  import { syncStateToProjectRoot } from "./auto-worktree.js";
27
27
  import { normalizeWorktreePathForCompare } from "./worktree-root.js";
28
- import { isDbAvailable, getTask, getSlice, getMilestone, updateTaskStatus, _getAdapter } from "./gsd-db.js";
28
+ import { isDbAvailable, getTask, getSlice, getMilestone, updateTaskStatus, _getAdapter, getVerificationEvidence } from "./gsd-db.js";
29
29
  import { renderPlanCheckboxes } from "./markdown-renderer.js";
30
30
  import { consumeSignal } from "./session-status-io.js";
31
31
  import { checkPostUnitHooks, isRetryPending, consumeRetryTrigger, persistHookState, resolveHookArtifactPath, } from "./post-unit-hooks.js";
@@ -719,21 +719,21 @@ export async function postUnitPreVerification(pctx, opts) {
719
719
  }
720
720
  }
721
721
  // Evidence cross-reference (execute-task only)
722
- // Verification evidence is passed via the complete-task tool call and
723
- // stored in the SUMMARY.md on disk not available as structured data
724
- // in the DB. The evidence collector tracks actual bash tool calls, so
725
- // we can still detect units that claimed success but ran no commands.
722
+ // Only compare against concrete command evidence persisted by the task
723
+ // completion tool. A prose Verify field can be satisfied later by the
724
+ // host verification gate, so it is not enough to accuse the unit.
726
725
  if (safetyConfig.evidence_cross_reference && s.currentUnit.type === "execute-task") {
727
726
  try {
728
727
  const actual = getEvidence();
729
728
  const bashCalls = actual.filter(e => e.kind === "bash");
730
- // If the task is marked complete but zero bash commands were run,
731
- // it's suspicious — the LLM may have fabricated results.
732
729
  if (sMid && sSid && sTid && isDbAvailable()) {
733
730
  const taskRow = getTask(sMid, sSid, sTid);
734
- if (taskRow?.status === "complete" && taskRow.verify && bashCalls.length === 0) {
735
- logWarning("safety", "task marked complete with verification commands but no bash calls were executed");
736
- ctx.ui.notify(`Safety: task ${sTid} has verification commands but no bash calls were recorded`, "warning");
731
+ const claimedCommands = getVerificationEvidence(sMid, sSid, sTid)
732
+ .map((row) => row.command)
733
+ .filter((command) => typeof command === "string" && command.trim().length > 0);
734
+ if (taskRow?.status === "complete" && claimedCommands.length > 0 && bashCalls.length === 0) {
735
+ logWarning("safety", "task claimed verification command evidence but no execution tool calls were recorded");
736
+ ctx.ui.notify(`Safety: task ${sTid} claimed command evidence but no execution tool calls were recorded`, "warning");
737
737
  }
738
738
  }
739
739
  }