@telora/daemon 0.20.2 → 0.20.9

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 (587) hide show
  1. package/build-info.json +7 -3
  2. package/dist/ai-inspection-context.d.ts.map +1 -1
  3. package/dist/ai-inspection-context.js +5 -3
  4. package/dist/ai-inspection-context.js.map +1 -1
  5. package/dist/assembly-engine.d.ts.map +1 -1
  6. package/dist/assembly-engine.js +4 -2
  7. package/dist/assembly-engine.js.map +1 -1
  8. package/dist/audit-hooks.d.ts.map +1 -1
  9. package/dist/audit-hooks.js +3 -1
  10. package/dist/audit-hooks.js.map +1 -1
  11. package/dist/auth-liveness.d.ts.map +1 -1
  12. package/dist/auth-liveness.js +4 -2
  13. package/dist/auth-liveness.js.map +1 -1
  14. package/dist/auto-update.d.ts.map +1 -1
  15. package/dist/auto-update.js +14 -12
  16. package/dist/auto-update.js.map +1 -1
  17. package/dist/backends/codex/codex-backend.d.ts.map +1 -1
  18. package/dist/backends/codex/codex-backend.js +4 -2
  19. package/dist/backends/codex/codex-backend.js.map +1 -1
  20. package/dist/backends/codex/codex-mcp-proxy.d.ts.map +1 -1
  21. package/dist/backends/codex/codex-mcp-proxy.js +3 -1
  22. package/dist/backends/codex/codex-mcp-proxy.js.map +1 -1
  23. package/dist/backends/lacuna/lifecycle.d.ts.map +1 -1
  24. package/dist/backends/lacuna/lifecycle.js +4 -2
  25. package/dist/backends/lacuna/lifecycle.js.map +1 -1
  26. package/dist/backends/resolve.d.ts.map +1 -1
  27. package/dist/backends/resolve.js +3 -1
  28. package/dist/backends/resolve.js.map +1 -1
  29. package/dist/cascade-restatement.d.ts.map +1 -1
  30. package/dist/cascade-restatement.js +15 -13
  31. package/dist/cascade-restatement.js.map +1 -1
  32. package/dist/ci-escalation.d.ts.map +1 -1
  33. package/dist/ci-escalation.js +4 -2
  34. package/dist/ci-escalation.js.map +1 -1
  35. package/dist/ci-sensor-loop.d.ts.map +1 -1
  36. package/dist/ci-sensor-loop.js +6 -4
  37. package/dist/ci-sensor-loop.js.map +1 -1
  38. package/dist/ci-status-git.d.ts +5 -6
  39. package/dist/ci-status-git.d.ts.map +1 -1
  40. package/dist/ci-status-git.js +7 -7
  41. package/dist/ci-status-git.js.map +1 -1
  42. package/dist/cli/connect.d.ts.map +1 -1
  43. package/dist/cli/connect.js +9 -2
  44. package/dist/cli/connect.js.map +1 -1
  45. package/dist/cli/open.d.ts.map +1 -1
  46. package/dist/cli/open.js +9 -2
  47. package/dist/cli/open.js.map +1 -1
  48. package/dist/cli/session-state.d.ts.map +1 -1
  49. package/dist/cli/session-state.js +2 -0
  50. package/dist/cli/session-state.js.map +1 -1
  51. package/dist/cli/verify-setup.d.ts.map +1 -1
  52. package/dist/cli/verify-setup.js +9 -4
  53. package/dist/cli/verify-setup.js.map +1 -1
  54. package/dist/close-loop-dispatcher.d.ts.map +1 -1
  55. package/dist/close-loop-dispatcher.js +6 -4
  56. package/dist/close-loop-dispatcher.js.map +1 -1
  57. package/dist/completion/advanceability.d.ts.map +1 -1
  58. package/dist/completion/advanceability.js +3 -1
  59. package/dist/completion/advanceability.js.map +1 -1
  60. package/dist/completion/agent-completion.d.ts.map +1 -1
  61. package/dist/completion/agent-completion.js +11 -9
  62. package/dist/completion/agent-completion.js.map +1 -1
  63. package/dist/completion/event-escalations.d.ts.map +1 -1
  64. package/dist/completion/event-escalations.js +10 -8
  65. package/dist/completion/event-escalations.js.map +1 -1
  66. package/dist/completion/event-phases.d.ts +104 -0
  67. package/dist/completion/event-phases.d.ts.map +1 -0
  68. package/dist/completion/event-phases.js +572 -0
  69. package/dist/completion/event-phases.js.map +1 -0
  70. package/dist/completion/event.d.ts +1 -1
  71. package/dist/completion/event.d.ts.map +1 -1
  72. package/dist/completion/event.js +36 -592
  73. package/dist/completion/event.js.map +1 -1
  74. package/dist/completion/merge-phase.d.ts.map +1 -1
  75. package/dist/completion/merge-phase.js +14 -12
  76. package/dist/completion/merge-phase.js.map +1 -1
  77. package/dist/completion/read-only-pristine.d.ts.map +1 -1
  78. package/dist/completion/read-only-pristine.js +6 -4
  79. package/dist/completion/read-only-pristine.js.map +1 -1
  80. package/dist/completion/review-exit-phase.d.ts.map +1 -1
  81. package/dist/completion/review-exit-phase.js +28 -26
  82. package/dist/completion/review-exit-phase.js.map +1 -1
  83. package/dist/completion/status-advance-phase.d.ts.map +1 -1
  84. package/dist/completion/status-advance-phase.js +16 -14
  85. package/dist/completion/status-advance-phase.js.map +1 -1
  86. package/dist/completion/team-completion.d.ts.map +1 -1
  87. package/dist/completion/team-completion.js +16 -14
  88. package/dist/completion/team-completion.js.map +1 -1
  89. package/dist/completion/team-lifecycle.d.ts.map +1 -1
  90. package/dist/completion/team-lifecycle.js +32 -33
  91. package/dist/completion/team-lifecycle.js.map +1 -1
  92. package/dist/completion/terminated-work-reconcile.d.ts.map +1 -1
  93. package/dist/completion/terminated-work-reconcile.js +11 -9
  94. package/dist/completion/terminated-work-reconcile.js.map +1 -1
  95. package/dist/condition-evaluators.d.ts.map +1 -1
  96. package/dist/condition-evaluators.js +3 -1
  97. package/dist/condition-evaluators.js.map +1 -1
  98. package/dist/config-reconcile.d.ts.map +1 -1
  99. package/dist/config-reconcile.js +3 -1
  100. package/dist/config-reconcile.js.map +1 -1
  101. package/dist/config.d.ts +4 -4
  102. package/dist/config.d.ts.map +1 -1
  103. package/dist/config.js +56 -42
  104. package/dist/config.js.map +1 -1
  105. package/dist/control-state.d.ts.map +1 -1
  106. package/dist/control-state.js +26 -24
  107. package/dist/control-state.js.map +1 -1
  108. package/dist/crash-recovery.d.ts.map +1 -1
  109. package/dist/crash-recovery.js +43 -41
  110. package/dist/crash-recovery.js.map +1 -1
  111. package/dist/daemon-logs.d.ts.map +1 -1
  112. package/dist/daemon-logs.js +21 -3
  113. package/dist/daemon-logs.js.map +1 -1
  114. package/dist/daemon-process.d.ts +0 -1
  115. package/dist/daemon-process.d.ts.map +1 -1
  116. package/dist/daemon-process.js +15 -10
  117. package/dist/daemon-process.js.map +1 -1
  118. package/dist/delivery-advance-block.d.ts +4 -4
  119. package/dist/delivery-advance-block.d.ts.map +1 -1
  120. package/dist/delivery-advance-block.js +25 -18
  121. package/dist/delivery-advance-block.js.map +1 -1
  122. package/dist/delivery-guards.d.ts.map +1 -1
  123. package/dist/delivery-guards.js +11 -9
  124. package/dist/delivery-guards.js.map +1 -1
  125. package/dist/delivery-lifecycle.d.ts.map +1 -1
  126. package/dist/delivery-lifecycle.js +23 -21
  127. package/dist/delivery-lifecycle.js.map +1 -1
  128. package/dist/delivery-merge.d.ts.map +1 -1
  129. package/dist/delivery-merge.js +10 -8
  130. package/dist/delivery-merge.js.map +1 -1
  131. package/dist/delivery-reconciler-apply.d.ts.map +1 -1
  132. package/dist/delivery-reconciler-apply.js +5 -8
  133. package/dist/delivery-reconciler-apply.js.map +1 -1
  134. package/dist/delivery-reconciler-shadow.d.ts.map +1 -1
  135. package/dist/delivery-reconciler-shadow.js +5 -6
  136. package/dist/delivery-reconciler-shadow.js.map +1 -1
  137. package/dist/directive/close-loop-stage.d.ts.map +1 -1
  138. package/dist/directive/close-loop-stage.js +9 -7
  139. package/dist/directive/close-loop-stage.js.map +1 -1
  140. package/dist/directive/directive-assembly.d.ts.map +1 -1
  141. package/dist/directive/directive-assembly.js +3 -1
  142. package/dist/directive/directive-assembly.js.map +1 -1
  143. package/dist/directive/directive-dispatch.d.ts.map +1 -1
  144. package/dist/directive/directive-dispatch.js +16 -14
  145. package/dist/directive/directive-dispatch.js.map +1 -1
  146. package/dist/directive/directive-queue.d.ts +27 -0
  147. package/dist/directive/directive-queue.d.ts.map +1 -1
  148. package/dist/directive/directive-queue.js +28 -0
  149. package/dist/directive/directive-queue.js.map +1 -1
  150. package/dist/directive/phase-sync.d.ts.map +1 -1
  151. package/dist/directive/phase-sync.js +8 -6
  152. package/dist/directive/phase-sync.js.map +1 -1
  153. package/dist/directive-executor.d.ts +1 -1
  154. package/dist/directive-executor.d.ts.map +1 -1
  155. package/dist/directive-executor.js +12 -10
  156. package/dist/directive-executor.js.map +1 -1
  157. package/dist/discovery-poll.d.ts.map +1 -1
  158. package/dist/discovery-poll.js +8 -6
  159. package/dist/discovery-poll.js.map +1 -1
  160. package/dist/drain-teardown.d.ts.map +1 -1
  161. package/dist/drain-teardown.js +6 -19
  162. package/dist/drain-teardown.js.map +1 -1
  163. package/dist/drift-eval-loop.d.ts.map +1 -1
  164. package/dist/drift-eval-loop.js +10 -8
  165. package/dist/drift-eval-loop.js.map +1 -1
  166. package/dist/escalation-provenance.d.ts.map +1 -1
  167. package/dist/escalation-provenance.js +8 -3
  168. package/dist/escalation-provenance.js.map +1 -1
  169. package/dist/focus-engine.d.ts.map +1 -1
  170. package/dist/focus-engine.js +36 -34
  171. package/dist/focus-engine.js.map +1 -1
  172. package/dist/focus-executor.d.ts +2 -1
  173. package/dist/focus-executor.d.ts.map +1 -1
  174. package/dist/focus-executor.js +17 -15
  175. package/dist/focus-executor.js.map +1 -1
  176. package/dist/focus-lifecycle.d.ts.map +1 -1
  177. package/dist/focus-lifecycle.js +56 -44
  178. package/dist/focus-lifecycle.js.map +1 -1
  179. package/dist/focus-loop-registry.d.ts.map +1 -1
  180. package/dist/focus-loop-registry.js +38 -36
  181. package/dist/focus-loop-registry.js.map +1 -1
  182. package/dist/focus-merge.d.ts.map +1 -1
  183. package/dist/focus-merge.js +40 -34
  184. package/dist/focus-merge.js.map +1 -1
  185. package/dist/focus-provisioning.d.ts.map +1 -1
  186. package/dist/focus-provisioning.js +15 -13
  187. package/dist/focus-provisioning.js.map +1 -1
  188. package/dist/focus-respawn-guard.d.ts.map +1 -1
  189. package/dist/focus-respawn-guard.js +4 -2
  190. package/dist/focus-respawn-guard.js.map +1 -1
  191. package/dist/focus-stage-lifecycle/planning-release.d.ts.map +1 -1
  192. package/dist/focus-stage-lifecycle/planning-release.js +10 -8
  193. package/dist/focus-stage-lifecycle/planning-release.js.map +1 -1
  194. package/dist/focus-stage-lifecycle/review-convergence.d.ts.map +1 -1
  195. package/dist/focus-stage-lifecycle/review-convergence.js +11 -9
  196. package/dist/focus-stage-lifecycle/review-convergence.js.map +1 -1
  197. package/dist/focus-stage-lifecycle/wedge-watchdog.d.ts.map +1 -1
  198. package/dist/focus-stage-lifecycle/wedge-watchdog.js +7 -5
  199. package/dist/focus-stage-lifecycle/wedge-watchdog.js.map +1 -1
  200. package/dist/focus-stage-lifecycle.d.ts.map +1 -1
  201. package/dist/focus-stage-lifecycle.js +7 -5
  202. package/dist/focus-stage-lifecycle.js.map +1 -1
  203. package/dist/focus-team-state.d.ts +18 -9
  204. package/dist/focus-team-state.d.ts.map +1 -1
  205. package/dist/focus-team-state.js +37 -18
  206. package/dist/focus-team-state.js.map +1 -1
  207. package/dist/focus-worktree-state.d.ts.map +1 -1
  208. package/dist/focus-worktree-state.js +6 -4
  209. package/dist/focus-worktree-state.js.map +1 -1
  210. package/dist/git/branch.d.ts.map +1 -1
  211. package/dist/git/branch.js +7 -5
  212. package/dist/git/branch.js.map +1 -1
  213. package/dist/git/genesis.d.ts.map +1 -1
  214. package/dist/git/genesis.js +3 -1
  215. package/dist/git/genesis.js.map +1 -1
  216. package/dist/git/integration.d.ts.map +1 -1
  217. package/dist/git/integration.js +4 -2
  218. package/dist/git/integration.js.map +1 -1
  219. package/dist/git/merge-helpers.d.ts.map +1 -1
  220. package/dist/git/merge-helpers.js +6 -4
  221. package/dist/git/merge-helpers.js.map +1 -1
  222. package/dist/git/merge-lock.d.ts +0 -7
  223. package/dist/git/merge-lock.d.ts.map +1 -1
  224. package/dist/git/merge-lock.js +5 -3
  225. package/dist/git/merge-lock.js.map +1 -1
  226. package/dist/git/merge.d.ts.map +1 -1
  227. package/dist/git/merge.js +10 -8
  228. package/dist/git/merge.js.map +1 -1
  229. package/dist/git/state-detector.d.ts.map +1 -1
  230. package/dist/git/state-detector.js +9 -7
  231. package/dist/git/state-detector.js.map +1 -1
  232. package/dist/git/utils.d.ts.map +1 -1
  233. package/dist/git/utils.js +6 -4
  234. package/dist/git/utils.js.map +1 -1
  235. package/dist/git.d.ts.map +1 -1
  236. package/dist/git.js +16 -14
  237. package/dist/git.js.map +1 -1
  238. package/dist/guard-evaluator.d.ts.map +1 -1
  239. package/dist/guard-evaluator.js +3 -1
  240. package/dist/guard-evaluator.js.map +1 -1
  241. package/dist/heartbeat.d.ts.map +1 -1
  242. package/dist/heartbeat.js +14 -11
  243. package/dist/heartbeat.js.map +1 -1
  244. package/dist/host-control/catalog.d.ts.map +1 -1
  245. package/dist/host-control/catalog.js +4 -2
  246. package/dist/host-control/catalog.js.map +1 -1
  247. package/dist/host-control/client.d.ts.map +1 -1
  248. package/dist/host-control/client.js +3 -1
  249. package/dist/host-control/client.js.map +1 -1
  250. package/dist/host-control/sizing.d.ts.map +1 -1
  251. package/dist/host-control/sizing.js +4 -1
  252. package/dist/host-control/sizing.js.map +1 -1
  253. package/dist/index.js +110 -127
  254. package/dist/index.js.map +1 -1
  255. package/dist/install-probe-tick.d.ts.map +1 -1
  256. package/dist/install-probe-tick.js +6 -4
  257. package/dist/install-probe-tick.js.map +1 -1
  258. package/dist/intake-control.d.ts +0 -16
  259. package/dist/intake-control.d.ts.map +1 -1
  260. package/dist/intake-control.js +4 -2
  261. package/dist/intake-control.js.map +1 -1
  262. package/dist/listener-auto-advance.d.ts.map +1 -1
  263. package/dist/listener-auto-advance.js +26 -24
  264. package/dist/listener-auto-advance.js.map +1 -1
  265. package/dist/listener.d.ts.map +1 -1
  266. package/dist/listener.js +59 -39
  267. package/dist/listener.js.map +1 -1
  268. package/dist/log.d.ts +84 -0
  269. package/dist/log.d.ts.map +1 -0
  270. package/dist/log.js +182 -0
  271. package/dist/log.js.map +1 -0
  272. package/dist/loops/context-audit-pass.d.ts.map +1 -1
  273. package/dist/loops/context-audit-pass.js +8 -5
  274. package/dist/loops/context-audit-pass.js.map +1 -1
  275. package/dist/loops/engine.d.ts.map +1 -1
  276. package/dist/loops/engine.js +26 -24
  277. package/dist/loops/engine.js.map +1 -1
  278. package/dist/loops/llm-client.d.ts.map +1 -1
  279. package/dist/loops/llm-client.js +4 -2
  280. package/dist/loops/llm-client.js.map +1 -1
  281. package/dist/loops/llm-flag.d.ts +4 -28
  282. package/dist/loops/llm-flag.d.ts.map +1 -1
  283. package/dist/loops/llm-flag.js +11 -30
  284. package/dist/loops/llm-flag.js.map +1 -1
  285. package/dist/loops/prompt-builder.d.ts.map +1 -1
  286. package/dist/loops/prompt-builder.js +3 -1
  287. package/dist/loops/prompt-builder.js.map +1 -1
  288. package/dist/merge-back-loop.d.ts.map +1 -1
  289. package/dist/merge-back-loop.js +20 -18
  290. package/dist/merge-back-loop.js.map +1 -1
  291. package/dist/merge-detector.d.ts.map +1 -1
  292. package/dist/merge-detector.js +7 -5
  293. package/dist/merge-detector.js.map +1 -1
  294. package/dist/nexus/endpoint-clearance.d.ts.map +1 -1
  295. package/dist/nexus/endpoint-clearance.js +3 -1
  296. package/dist/nexus/endpoint-clearance.js.map +1 -1
  297. package/dist/nexus/escalation-bridge.d.ts.map +1 -1
  298. package/dist/nexus/escalation-bridge.js +3 -1
  299. package/dist/nexus/escalation-bridge.js.map +1 -1
  300. package/dist/nexus/incoming.d.ts.map +1 -1
  301. package/dist/nexus/incoming.js +3 -1
  302. package/dist/nexus/incoming.js.map +1 -1
  303. package/dist/nexus/intake-spawn.d.ts.map +1 -1
  304. package/dist/nexus/intake-spawn.js +6 -4
  305. package/dist/nexus/intake-spawn.js.map +1 -1
  306. package/dist/nexus/intake.d.ts.map +1 -1
  307. package/dist/nexus/intake.js +3 -1
  308. package/dist/nexus/intake.js.map +1 -1
  309. package/dist/nexus/return.d.ts.map +1 -1
  310. package/dist/nexus/return.js +3 -1
  311. package/dist/nexus/return.js.map +1 -1
  312. package/dist/non-interactive-init.d.ts.map +1 -1
  313. package/dist/non-interactive-init.js +0 -0
  314. package/dist/non-interactive-init.js.map +1 -1
  315. package/dist/otlp/port-manager.d.ts.map +1 -1
  316. package/dist/otlp/port-manager.js +7 -5
  317. package/dist/otlp/port-manager.js.map +1 -1
  318. package/dist/otlp/receiver.d.ts.map +1 -1
  319. package/dist/otlp/receiver.js +14 -12
  320. package/dist/otlp/receiver.js.map +1 -1
  321. package/dist/output-monitor.d.ts.map +1 -1
  322. package/dist/output-monitor.js +6 -4
  323. package/dist/output-monitor.js.map +1 -1
  324. package/dist/prd/controller.d.ts.map +1 -1
  325. package/dist/prd/controller.js +7 -5
  326. package/dist/prd/controller.js.map +1 -1
  327. package/dist/prd/focus-pushdown.d.ts.map +1 -1
  328. package/dist/prd/focus-pushdown.js +3 -1
  329. package/dist/prd/focus-pushdown.js.map +1 -1
  330. package/dist/product-scaffold.d.ts.map +1 -1
  331. package/dist/product-scaffold.js +8 -6
  332. package/dist/product-scaffold.js.map +1 -1
  333. package/dist/queries/context-assembly.d.ts.map +1 -1
  334. package/dist/queries/context-assembly.js +3 -1
  335. package/dist/queries/context-assembly.js.map +1 -1
  336. package/dist/queries/deliveries.d.ts.map +1 -1
  337. package/dist/queries/deliveries.js +7 -5
  338. package/dist/queries/deliveries.js.map +1 -1
  339. package/dist/queries/focuses.d.ts.map +1 -1
  340. package/dist/queries/focuses.js +3 -1
  341. package/dist/queries/focuses.js.map +1 -1
  342. package/dist/queries/guards.d.ts.map +1 -1
  343. package/dist/queries/guards.js +3 -1
  344. package/dist/queries/guards.js.map +1 -1
  345. package/dist/queries/issues.d.ts.map +1 -1
  346. package/dist/queries/issues.js +3 -1
  347. package/dist/queries/issues.js.map +1 -1
  348. package/dist/queries/system-session.d.ts.map +1 -1
  349. package/dist/queries/system-session.js +5 -3
  350. package/dist/queries/system-session.js.map +1 -1
  351. package/dist/queries/verification.d.ts.map +1 -1
  352. package/dist/queries/verification.js +6 -4
  353. package/dist/queries/verification.js.map +1 -1
  354. package/dist/reality-pole-capture.d.ts.map +1 -1
  355. package/dist/reality-pole-capture.js +7 -4
  356. package/dist/reality-pole-capture.js.map +1 -1
  357. package/dist/reality-pole.d.ts.map +1 -1
  358. package/dist/reality-pole.js +4 -2
  359. package/dist/reality-pole.js.map +1 -1
  360. package/dist/resolvers/agent-escalations.d.ts.map +1 -1
  361. package/dist/resolvers/agent-escalations.js +3 -1
  362. package/dist/resolvers/agent-escalations.js.map +1 -1
  363. package/dist/resolvers/agent-session-summaries.d.ts.map +1 -1
  364. package/dist/resolvers/agent-session-summaries.js +3 -1
  365. package/dist/resolvers/agent-session-summaries.js.map +1 -1
  366. package/dist/resolvers/deployment-profile.d.ts.map +1 -1
  367. package/dist/resolvers/deployment-profile.js +3 -1
  368. package/dist/resolvers/deployment-profile.js.map +1 -1
  369. package/dist/resolvers/escalation-answers.d.ts.map +1 -1
  370. package/dist/resolvers/escalation-answers.js +4 -2
  371. package/dist/resolvers/escalation-answers.js.map +1 -1
  372. package/dist/resolvers/focus-anchoring-injections.d.ts.map +1 -1
  373. package/dist/resolvers/focus-anchoring-injections.js +3 -1
  374. package/dist/resolvers/focus-anchoring-injections.js.map +1 -1
  375. package/dist/resolvers/focus-injections.d.ts.map +1 -1
  376. package/dist/resolvers/focus-injections.js +3 -1
  377. package/dist/resolvers/focus-injections.js.map +1 -1
  378. package/dist/resolvers/focus-last-review-report.d.ts.map +1 -1
  379. package/dist/resolvers/focus-last-review-report.js +3 -1
  380. package/dist/resolvers/focus-last-review-report.js.map +1 -1
  381. package/dist/resolvers/focus-reality-tree.d.ts.map +1 -1
  382. package/dist/resolvers/focus-reality-tree.js +3 -1
  383. package/dist/resolvers/focus-reality-tree.js.map +1 -1
  384. package/dist/resolvers/git-diff-against-base.d.ts.map +1 -1
  385. package/dist/resolvers/git-diff-against-base.js +3 -1
  386. package/dist/resolvers/git-diff-against-base.js.map +1 -1
  387. package/dist/resolvers/guards-evaluation-results.d.ts.map +1 -1
  388. package/dist/resolvers/guards-evaluation-results.js +3 -1
  389. package/dist/resolvers/guards-evaluation-results.js.map +1 -1
  390. package/dist/resolvers/loop-context.d.ts.map +1 -1
  391. package/dist/resolvers/loop-context.js +6 -2
  392. package/dist/resolvers/loop-context.js.map +1 -1
  393. package/dist/resolvers/loop-delivery-index.d.ts.map +1 -1
  394. package/dist/resolvers/loop-delivery-index.js +3 -1
  395. package/dist/resolvers/loop-delivery-index.js.map +1 -1
  396. package/dist/resolvers/loop-documents.d.ts.map +1 -1
  397. package/dist/resolvers/loop-documents.js +3 -1
  398. package/dist/resolvers/loop-documents.js.map +1 -1
  399. package/dist/resolvers/loop-expected-effects.d.ts.map +1 -1
  400. package/dist/resolvers/loop-expected-effects.js +3 -1
  401. package/dist/resolvers/loop-expected-effects.js.map +1 -1
  402. package/dist/resolvers/loop-frt-statement.d.ts.map +1 -1
  403. package/dist/resolvers/loop-frt-statement.js +3 -1
  404. package/dist/resolvers/loop-frt-statement.js.map +1 -1
  405. package/dist/resolvers/loop-injection.d.ts.map +1 -1
  406. package/dist/resolvers/loop-injection.js +3 -1
  407. package/dist/resolvers/loop-injection.js.map +1 -1
  408. package/dist/resolvers/loop-persona.d.ts.map +1 -1
  409. package/dist/resolvers/loop-persona.js +3 -1
  410. package/dist/resolvers/loop-persona.js.map +1 -1
  411. package/dist/resolvers/loop-questions.d.ts.map +1 -1
  412. package/dist/resolvers/loop-questions.js +3 -1
  413. package/dist/resolvers/loop-questions.js.map +1 -1
  414. package/dist/resolvers/loop-upstream-udes.d.ts.map +1 -1
  415. package/dist/resolvers/loop-upstream-udes.js +3 -1
  416. package/dist/resolvers/loop-upstream-udes.js.map +1 -1
  417. package/dist/resolvers/prd-context.d.ts.map +1 -1
  418. package/dist/resolvers/prd-context.js +3 -1
  419. package/dist/resolvers/prd-context.js.map +1 -1
  420. package/dist/resolvers/reality-metrics.d.ts.map +1 -1
  421. package/dist/resolvers/reality-metrics.js +3 -1
  422. package/dist/resolvers/reality-metrics.js.map +1 -1
  423. package/dist/resolvers/reality-projections.d.ts.map +1 -1
  424. package/dist/resolvers/reality-projections.js +3 -1
  425. package/dist/resolvers/reality-projections.js.map +1 -1
  426. package/dist/resolvers/reality-tree-snapshot.d.ts.map +1 -1
  427. package/dist/resolvers/reality-tree-snapshot.js +3 -1
  428. package/dist/resolvers/reality-tree-snapshot.js.map +1 -1
  429. package/dist/resolvers/retired-injections.d.ts.map +1 -1
  430. package/dist/resolvers/retired-injections.js +3 -1
  431. package/dist/resolvers/retired-injections.js.map +1 -1
  432. package/dist/resolvers/review-outcomes.d.ts.map +1 -1
  433. package/dist/resolvers/review-outcomes.js +3 -1
  434. package/dist/resolvers/review-outcomes.js.map +1 -1
  435. package/dist/resolvers/security-advisory.d.ts.map +1 -1
  436. package/dist/resolvers/security-advisory.js +3 -1
  437. package/dist/resolvers/security-advisory.js.map +1 -1
  438. package/dist/resolvers/shared/wiki.d.ts.map +1 -1
  439. package/dist/resolvers/shared/wiki.js +5 -3
  440. package/dist/resolvers/shared/wiki.js.map +1 -1
  441. package/dist/resolvers/wiki-topic.d.ts.map +1 -1
  442. package/dist/resolvers/wiki-topic.js +3 -1
  443. package/dist/resolvers/wiki-topic.js.map +1 -1
  444. package/dist/resolvers/workflow-stages.d.ts.map +1 -1
  445. package/dist/resolvers/workflow-stages.js +3 -1
  446. package/dist/resolvers/workflow-stages.js.map +1 -1
  447. package/dist/schema-version-gate.d.ts.map +1 -1
  448. package/dist/schema-version-gate.js +16 -18
  449. package/dist/schema-version-gate.js.map +1 -1
  450. package/dist/security-finding-gate.d.ts.map +1 -1
  451. package/dist/security-finding-gate.js +3 -1
  452. package/dist/security-finding-gate.js.map +1 -1
  453. package/dist/security-rescan-resolution.d.ts.map +1 -1
  454. package/dist/security-rescan-resolution.js +4 -2
  455. package/dist/security-rescan-resolution.js.map +1 -1
  456. package/dist/security-scan-engine.d.ts.map +1 -1
  457. package/dist/security-scan-engine.js +9 -6
  458. package/dist/security-scan-engine.js.map +1 -1
  459. package/dist/self-update.d.ts.map +1 -1
  460. package/dist/self-update.js +48 -39
  461. package/dist/self-update.js.map +1 -1
  462. package/dist/sensor-sweep.d.ts.map +1 -1
  463. package/dist/sensor-sweep.js +5 -2
  464. package/dist/sensor-sweep.js.map +1 -1
  465. package/dist/session-stamp.d.ts.map +1 -1
  466. package/dist/session-stamp.js +4 -2
  467. package/dist/session-stamp.js.map +1 -1
  468. package/dist/spawn/model-routing.d.ts +18 -0
  469. package/dist/spawn/model-routing.d.ts.map +1 -1
  470. package/dist/spawn/model-routing.js +53 -18
  471. package/dist/spawn/model-routing.js.map +1 -1
  472. package/dist/spawn/process-spawn.d.ts.map +1 -1
  473. package/dist/spawn/process-spawn.js +29 -22
  474. package/dist/spawn/process-spawn.js.map +1 -1
  475. package/dist/spawn/prompt-assembly.d.ts.map +1 -1
  476. package/dist/spawn/prompt-assembly.js +6 -4
  477. package/dist/spawn/prompt-assembly.js.map +1 -1
  478. package/dist/spawn/spawn-decision.d.ts.map +1 -1
  479. package/dist/spawn/spawn-decision.js +16 -14
  480. package/dist/spawn/spawn-decision.js.map +1 -1
  481. package/dist/spawn/worktree-session.d.ts.map +1 -1
  482. package/dist/spawn/worktree-session.js +13 -11
  483. package/dist/spawn/worktree-session.js.map +1 -1
  484. package/dist/spawn-cooldown.d.ts +0 -7
  485. package/dist/spawn-cooldown.d.ts.map +1 -1
  486. package/dist/spawn-cooldown.js +3 -1
  487. package/dist/spawn-cooldown.js.map +1 -1
  488. package/dist/spawn-environment.d.ts.map +1 -1
  489. package/dist/spawn-environment.js +4 -1
  490. package/dist/spawn-environment.js.map +1 -1
  491. package/dist/spawner/helpers.d.ts.map +1 -1
  492. package/dist/spawner/helpers.js +19 -17
  493. package/dist/spawner/helpers.js.map +1 -1
  494. package/dist/spawner/lifecycle.d.ts.map +1 -1
  495. package/dist/spawner/lifecycle.js +17 -13
  496. package/dist/spawner/lifecycle.js.map +1 -1
  497. package/dist/spawner/liveness.d.ts.map +1 -1
  498. package/dist/spawner/liveness.js +7 -5
  499. package/dist/spawner/liveness.js.map +1 -1
  500. package/dist/spawner/resolution.d.ts.map +1 -1
  501. package/dist/spawner/resolution.js +7 -5
  502. package/dist/spawner/resolution.js.map +1 -1
  503. package/dist/spawner/stream-handlers.d.ts +6 -55
  504. package/dist/spawner/stream-handlers.d.ts.map +1 -1
  505. package/dist/spawner/stream-handlers.js +10 -172
  506. package/dist/spawner/stream-handlers.js.map +1 -1
  507. package/dist/spawner/timeout.d.ts.map +1 -1
  508. package/dist/spawner/timeout.js +11 -9
  509. package/dist/spawner/timeout.js.map +1 -1
  510. package/dist/staleness.d.ts.map +1 -1
  511. package/dist/staleness.js +4 -1
  512. package/dist/staleness.js.map +1 -1
  513. package/dist/stall-detector-config.d.ts +6 -17
  514. package/dist/stall-detector-config.d.ts.map +1 -1
  515. package/dist/stall-detector-config.js +10 -36
  516. package/dist/stall-detector-config.js.map +1 -1
  517. package/dist/stall-detectors.d.ts.map +1 -1
  518. package/dist/stall-detectors.js +6 -4
  519. package/dist/stall-detectors.js.map +1 -1
  520. package/dist/state-cascade.d.ts.map +1 -1
  521. package/dist/state-cascade.js +9 -7
  522. package/dist/state-cascade.js.map +1 -1
  523. package/dist/stop-request.d.ts.map +1 -1
  524. package/dist/stop-request.js +6 -2
  525. package/dist/stop-request.js.map +1 -1
  526. package/dist/supabase.d.ts.map +1 -1
  527. package/dist/supabase.js +3 -1
  528. package/dist/supabase.js.map +1 -1
  529. package/dist/telemetry-writer.d.ts.map +1 -1
  530. package/dist/telemetry-writer.js +11 -9
  531. package/dist/telemetry-writer.js.map +1 -1
  532. package/dist/trigger-executor.d.ts.map +1 -1
  533. package/dist/trigger-executor.js +28 -26
  534. package/dist/trigger-executor.js.map +1 -1
  535. package/dist/unified-engine-lifecycle.d.ts +19 -11
  536. package/dist/unified-engine-lifecycle.d.ts.map +1 -1
  537. package/dist/unified-engine-lifecycle.js +71 -70
  538. package/dist/unified-engine-lifecycle.js.map +1 -1
  539. package/dist/unified-init.d.ts.map +1 -1
  540. package/dist/unified-init.js +41 -36
  541. package/dist/unified-init.js.map +1 -1
  542. package/dist/unified-shell-config.d.ts +8 -1
  543. package/dist/unified-shell-config.d.ts.map +1 -1
  544. package/dist/unified-shell-config.js +57 -34
  545. package/dist/unified-shell-config.js.map +1 -1
  546. package/dist/unified-shell-status.d.ts.map +1 -1
  547. package/dist/unified-shell-status.js +39 -37
  548. package/dist/unified-shell-status.js.map +1 -1
  549. package/dist/unified-shell.d.ts.map +1 -1
  550. package/dist/unified-shell.js +37 -41
  551. package/dist/unified-shell.js.map +1 -1
  552. package/dist/verification-baseline.d.ts.map +1 -1
  553. package/dist/verification-baseline.js +7 -4
  554. package/dist/verification-baseline.js.map +1 -1
  555. package/dist/verification-config.d.ts +3 -3
  556. package/dist/verification-config.d.ts.map +1 -1
  557. package/dist/verification-config.js +8 -19
  558. package/dist/verification-config.js.map +1 -1
  559. package/dist/verification-default-on.d.ts +6 -6
  560. package/dist/verification-default-on.d.ts.map +1 -1
  561. package/dist/verification-default-on.js +9 -7
  562. package/dist/verification-default-on.js.map +1 -1
  563. package/dist/verification-deterministic.d.ts.map +1 -1
  564. package/dist/verification-deterministic.js +6 -3
  565. package/dist/verification-deterministic.js.map +1 -1
  566. package/dist/verification-engine.d.ts.map +1 -1
  567. package/dist/verification-engine.js +20 -16
  568. package/dist/verification-engine.js.map +1 -1
  569. package/dist/verification-feedback.d.ts.map +1 -1
  570. package/dist/verification-feedback.js +4 -2
  571. package/dist/verification-feedback.js.map +1 -1
  572. package/dist/version-check.d.ts.map +1 -1
  573. package/dist/version-check.js +5 -3
  574. package/dist/version-check.js.map +1 -1
  575. package/dist/worker-pidfile.d.ts.map +1 -1
  576. package/dist/worker-pidfile.js +8 -3
  577. package/dist/worker-pidfile.js.map +1 -1
  578. package/dist/worktree/focus.d.ts.map +1 -1
  579. package/dist/worktree/focus.js +15 -13
  580. package/dist/worktree/focus.js.map +1 -1
  581. package/dist/worktree/safety.d.ts.map +1 -1
  582. package/dist/worktree/safety.js +13 -11
  583. package/dist/worktree/safety.js.map +1 -1
  584. package/dist/worktree.d.ts.map +1 -1
  585. package/dist/worktree.js +41 -39
  586. package/dist/worktree.js.map +1 -1
  587. package/package.json +3 -3
@@ -5,18 +5,15 @@
5
5
  * team lead completes a work cycle: terminate if done, send more work, or
6
6
  * (for planning teams) refresh from MCP-written deliveries / escalate.
7
7
  */
8
- import { sendMessage, DELIVERY_STATUS, loadEnvConfig } from '@telora/daemon-core';
9
- import { isStatusCascadable, isStatusAgentActionable } from '../stage-classifier.js';
8
+ import { loadEnvConfig } from '@telora/daemon-core';
9
+ import { isStatusCascadable } from '../stage-classifier.js';
10
10
  import { getFocusDeliveries, getFocusIssues } from '../queries/focuses.js';
11
11
  import { getActiveFocuses } from '../supabase.js';
12
- import { buildWakeMessage, buildReEngagementMessage } from '../focus-prompt-builder.js';
13
- import { OPEN_ISSUE_STATUSES } from '../constants.js';
14
- import { isAuditPhaseFocus } from '../audit-shape.js';
15
12
  import { terminateTeam as defaultTerminateTeam } from './team-lifecycle.js';
16
13
  import { createEscalation as defaultCreateEscalation, hasOpenTeamFailureEscalation as defaultHasOpenTeamFailureEscalation, } from '../queries/issues.js';
17
14
  import { advanceDeliveryStatuses as defaultAdvanceDeliveryStatuses, runReviewExitHandler as defaultRunReviewExitHandler, } from './team-completion.js';
18
15
  import { fetchEffectiveWorkflowWithTransitions as defaultFetchEffectiveWorkflowWithTransitions } from '../queries/workflows.js';
19
- import { decideCompletionAction, shouldCheckNonAdvanceable } from './completion-decision.js';
16
+ import { decideCompletionAction } from './completion-decision.js';
20
17
  import { takeProgressSnapshot, snapshotsEqual } from './progress-snapshot.js';
21
18
  // Review-window + non-advanceability predicates extracted to ./advanceability.ts
22
19
  // (keeps this file under the structural size lint). Imported for use below and
@@ -27,29 +24,21 @@ export { isInReviewWindow, isDeliveryNonAdvanceable, findNonAdvanceableDelivery
27
24
  import { getFocusWorktree as defaultGetFocusWorktree } from '../focus-worktree-state.js';
28
25
  import { worktreeHasUncommittedChanges as defaultWorktreeHasUncommittedChanges } from '../worktree/index.js';
29
26
  import { readWorktreeHeadSha as defaultReadWorktreeHeadSha } from '../git/index.js';
30
- import { resolveAutoRemediationBudget } from '../delivery-advance-block.js';
31
27
  // Escalation builders + their reason-string constants live in
32
28
  // event-escalations.ts; re-exported here so existing importers are unaffected.
33
- import { escalatePlanningFailure, escalateNoProgress, escalateNonAdvanceable, escalateDeliveryAdvancementStalled, } from './event-escalations.js';
34
29
  export { PLANNING_NO_DELIVERIES_REASON, NO_PROGRESS_ESCALATION_REASON, NON_ADVANCEABLE_ESCALATION_REASON, NON_ADVANCEABLE_CAUSE, DELIVERY_ADVANCEMENT_STALLED_REASON, DELIVERY_ADVANCEMENT_STALLED_CAUSE, escalatePlanningFailure, escalateNoProgress, escalateNonAdvanceable, escalateDeliveryAdvancementStalled, } from './event-escalations.js';
35
- import { recordDeliveryAdvancementCycle } from './delivery-advancement-guard.js';
36
30
  import { reconcileTerminatedTeamWork as defaultReconcileTerminatedTeamWork, } from './terminated-work-reconcile.js';
31
+ import { applyPlanningRefresh, routeReviewWindowCompletion, runWakeCycleSelfHeal, computeCompletionFacts, checkDeliveryAdvancementStall, executeCompletionAction, } from './event-phases.js';
32
+ import { createLogger } from '../log.js';
33
+ const log = createLogger({ module: 'focus-executor' });
37
34
  /**
38
35
  * Maximum consecutive re-engagement cycles with no measurable progress before
39
36
  * the scope-coverage guard escalates. Configurable via env so operators can
40
37
  * relax or tighten the bound without a code change.
41
38
  */
42
39
  export const SCOPE_COVERAGE_NO_PROGRESS_LIMIT = loadEnvConfig().TELORA_SCOPE_COVERAGE_NO_PROGRESS_LIMIT;
43
- export function resolveDeliveryAdvancementNoProgressLimit(envConfig = loadEnvConfig(), env = process.env) {
44
- const typedConfig = envConfig;
45
- if (typeof typedConfig.TELORA_DELIVERY_ADVANCEMENT_NO_PROGRESS_LIMIT === 'number') {
46
- return typedConfig.TELORA_DELIVERY_ADVANCEMENT_NO_PROGRESS_LIMIT;
47
- }
48
- const raw = env.TELORA_DELIVERY_ADVANCEMENT_NO_PROGRESS_LIMIT;
49
- if (raw === undefined || raw === '')
50
- return 3;
51
- const parsed = Number(raw);
52
- return Number.isInteger(parsed) && parsed > 0 ? parsed : 3;
40
+ export function resolveDeliveryAdvancementNoProgressLimit(envConfig = loadEnvConfig()) {
41
+ return envConfig.TELORA_DELIVERY_ADVANCEMENT_NO_PROGRESS_LIMIT;
53
42
  }
54
43
  export const DELIVERY_ADVANCEMENT_NO_PROGRESS_LIMIT = resolveDeliveryAdvancementNoProgressLimit();
55
44
  async function defaultGetReviewRequestedAt(config, focusId) {
@@ -93,594 +82,49 @@ const defaultDeps = {
93
82
  * deep nesting inside an async Promise chain inside an event listener.
94
83
  */
95
84
  export async function handleCompletionEvent(ctx, deps = defaultDeps) {
96
- const { focusId, focusName, teamState, completionDetector, proc } = ctx;
85
+ const { focusId, focusName, teamState } = ctx;
97
86
  const [currentDeliveries, currentIssues] = await Promise.all([
98
87
  deps.getFocusDeliveries(focusId),
99
88
  deps.getFocusIssues(focusId),
100
89
  ]);
101
- // Wake-cycle refresh (Option A): planning teams spawn with knownDeliveryIds
102
- // empty. The MCP-driven planning turn writes deliveries to the DB, so we
103
- // re-snapshot them here before any downstream merge/completion logic looks
104
- // at the set. WHY Option A: keeps the refresh on the existing wake path
105
- // instead of polling, so it runs exactly when new deliveries become visible.
106
- if (teamState.planningPhase) {
107
- if (currentDeliveries.length === 0) {
108
- console.warn(`[focus-executor] Planning team for "${focusName}" produced no deliveries ` +
109
- `-- escalating and terminating`);
110
- await escalatePlanningFailure({
111
- organizationId: teamState.organizationId,
112
- sessionId: teamState.leadSessionId ?? '',
113
- focusId,
114
- focusName,
115
- agentRoleId: teamState.roleId,
116
- }, deps);
117
- teamState.shutdownReason = 'work_complete';
118
- deps.terminateTeam(focusId);
119
- return;
120
- }
121
- for (const d of currentDeliveries) {
122
- teamState.knownDeliveryIds.add(d.id);
123
- teamState.deliveryStageIds.set(d.id, d.currentWorkflowStageId);
124
- }
125
- teamState.planningPhase = false;
126
- console.log(`[focus-executor] Planning refresh for "${focusName}": ` +
127
- `${currentDeliveries.length} delivery(ies) discovered`);
128
- }
129
- // Review-window handling. Resolve review_requested_at FRESH because it can be
130
- // set/cleared between spawn and this event.
131
- let reviewRequestedAt = null;
132
- if (deps.getReviewRequestedAt) {
133
- try {
134
- reviewRequestedAt = await deps.getReviewRequestedAt(ctx.config, focusId);
135
- }
136
- catch (err) {
137
- console.warn(`[focus-executor] getReviewRequestedAt failed for "${focusName}": ${err.message}`);
138
- // Fail open: a read failure must not block the existing teardown logic.
139
- }
140
- }
141
- if (isInReviewWindow(reviewRequestedAt, currentDeliveries)) {
142
- const verifyCount = currentDeliveries.filter(d => d.executionStatus === DELIVERY_STATUS.VERIFY).length;
143
- if (teamState.sessionType === 'review') {
144
- // A REVIEW pass just completed -- it emitted its result, which IS the
145
- // "a review cycle ran" signal. Close the focus DETERMINISTICALLY now,
146
- // keyed on THIS completion event rather than on process exit. A review
147
- // resumes its prior session to preserve context (INJ-D) and runs as an
148
- // interactive stream-json process that emits a result but never exits;
149
- // the old exit-keyed close therefore waited forever (the review-resume
150
- // deadlock). runReviewExitHandler routes verify deliveries by the
151
- // deterministic signal -- a pass that filed zero new issues and has no
152
- // open work closes verify->done; open work routes verify->verify_failed
153
- // to re-iterate -- and clears review_requested_at. The review team is then
154
- // torn down: closure has ALREADY happened, so it no longer matters whether
155
- // the process exits cleanly or is SIGTERM'd, and the next review cycle
156
- // resumes the same session id to review only the new delta.
157
- //
158
- // succeeded is the ACTUAL result of the review turn (!isError), NOT a
159
- // hardcoded true. A review that erred (e.g. the CLI failed to start, or a
160
- // model/engine mismatch) emits a result event with isError=true and files
161
- // no defects -- the old `succeeded: true` made that indistinguishable from
162
- // a genuinely clean review, so an errored/no-op review auto-approved
163
- // verify->done WITHOUT a real review. With the real flag, an errored
164
- // review leaves the delivery in verify; the next auto-review poll
165
- // re-triggers, and a persistently failing review is bounded by the
166
- // review_window_stalled escalation.
167
- const reviewSucceeded = !ctx.completionInfo.result.isError;
168
- console.log(`[focus-executor] Review pass completed for "${focusName}" (${verifyCount} ` +
169
- `delivery(ies) in verify, succeeded=${reviewSucceeded}) -- routing deterministically ` +
170
- `by filed issues, then terminating.`);
171
- // Coverage evidence: spawn-tip (what the review saw, carried on the team
172
- // state) is stamped as the watermark; exit-tip (HEAD now) is compared to
173
- // it to detect mid-review drift. Exit-tip is best-effort via injected git
174
- // deps; null makes coverage unconfirmable (auto-approve withheld).
175
- const reviewSpawnTip = teamState.reviewSpawnTip;
176
- let currentBranchTip = null;
177
- try {
178
- const wt = deps.getFocusWorktree?.(focusId);
179
- if (wt?.worktreePath) {
180
- currentBranchTip = deps.readWorktreeHeadSha?.(wt.worktreePath) ?? null;
181
- }
182
- }
183
- catch (err) {
184
- console.warn(`[focus-executor] Could not resolve exit-tip for "${focusName}" coverage gate: ${err.message}`);
185
- }
186
- try {
187
- await deps.runReviewExitHandler({
188
- focusId,
189
- focusName,
190
- organizationId: teamState.organizationId,
191
- productId: teamState.productId,
192
- sessionId: teamState.leadSessionId ?? '',
193
- sessionType: 'review',
194
- succeeded: reviewSucceeded,
195
- reviewSpawnTip,
196
- currentBranchTip,
197
- });
198
- }
199
- catch (err) {
200
- console.warn(`[focus-executor] Deterministic review routing failed for "${focusName}": ${err.message}`);
201
- }
202
- teamState.shutdownReason = 'work_complete';
203
- deps.terminateTeam(focusId);
204
- return;
205
- }
206
- // A non-review (coding) team completed a cycle while a review is in flight.
207
- // Do NOT tear it down on the all-terminal / no-actionable-work branches
208
- // below: `verify` is cascadable, so they would SIGTERM the team mid-review.
209
- // Leave it running; the review team's own completion closes the focus.
210
- console.log(`[focus-executor] "${focusName}" is in a review window (review_requested_at set, ` +
211
- `${verifyCount} delivery(ies) in verify) and the completing session is a ` +
212
- `${teamState.sessionType} pass -- not tearing down; the review pass completion routes ` +
213
- `the verify deliveries.`);
90
+ // 1. Planning refresh: discover MCP-written deliveries (or escalate an
91
+ // empty planning result and terminate).
92
+ if (await applyPlanningRefresh(ctx, deps, currentDeliveries))
214
93
  return;
215
- }
216
- // Scope-coverage guard: only terminate when EVERY delivery is in a
217
- // terminal-for-the-team state (verify, done, cancelled). The
218
- // existing isStatusCascadable() classifier matches that set; non-terminal
219
- // states (planning, queued, verify_failed, coding, paused) block termination
220
- // so the team is re-engaged on remaining work instead of idle-quitting
221
- // while unfinished deliveries are still in flight.
94
+ // 2. Review-window routing: a review pass closes the focus deterministically
95
+ // and terminates; a coding pass mid-review is left running.
96
+ if (await routeReviewWindowCompletion(ctx, deps, currentDeliveries) !== 'not_in_review')
97
+ return;
98
+ // 3. Scope-coverage termination: only terminate when EVERY delivery is in a
99
+ // terminal-for-the-team state (verify, done, cancelled) -- non-terminal
100
+ // states keep the team engaged instead of idle-quitting.
222
101
  const nonTerminal = currentDeliveries.filter(d => !isStatusCascadable(d.executionStatus ?? ''));
223
102
  if (nonTerminal.length === 0) {
224
- console.log(`[focus-executor] All deliveries terminal for "${focusName}" -- terminating team`);
103
+ log.info(`All deliveries terminal for "${focusName}" -- terminating team`);
225
104
  teamState.shutdownReason = 'work_complete';
226
105
  deps.terminateTeam(focusId);
227
106
  return;
228
107
  }
229
- // Determine the team's actionable workload up front: both the verify_failed
230
- // self-heal and the no-progress guard below depend on whether any
231
- // agent-actionable delivery (queued / coding / verify_failed) still carries
232
- // an open issue the team can act on.
233
- const actionableDeliveryIds = new Set(currentDeliveries
234
- .filter(d => isStatusAgentActionable(d.executionStatus ?? ''))
235
- .map(d => d.id));
236
- const hasActionableWithOpenIssues = currentIssues.some(i => actionableDeliveryIds.has(i.deliveryId) && OPEN_ISSUE_STATUSES.has(i.status));
237
- // Self-heal BEFORE the no-progress guard. When actionable deliveries exist
238
- // but none carry open issues, the team has nothing left to code. The common
239
- // cause is a review that bounced a delivery to verify_failed while all its
240
- // issues are already Done. The daemon's transparent
241
- // verify_failed -> coding -> verify stepping (advanceDeliveryStatuses) clears
242
- // that. Running it here -- before snapshotting -- means a benign "no work to
243
- // do" cycle either terminates the team cleanly (all deliveries now terminal)
244
- // or shows up as real progress (the delivery status moved), instead of being
245
- // counted as coder no-progress. Either way it can no longer accrue toward a
246
- // spurious team_failure escalation. (Previously this advance ran only AFTER
247
- // the guard, so the counter could reach the limit and escalate first.)
248
- let effectiveDeliveries = currentDeliveries;
249
- if (actionableDeliveryIds.size > 0 && !hasActionableWithOpenIssues) {
250
- try {
251
- await deps.advanceDeliveryStatuses(ctx.config, teamState, teamState.leadSessionId ?? '');
252
- }
253
- catch (err) {
254
- console.warn(`[focus-executor] Wake-cycle advanceDeliveryStatuses failed for "${focusName}": ${err.message}`);
255
- }
256
- // Re-fetch and re-check termination after the advance -- if every delivery
257
- // is now terminal-for-the-team, exit cleanly and hand off to review
258
- // routing / verification instead of sending a stale re-engagement.
259
- try {
260
- effectiveDeliveries = await deps.getFocusDeliveries(focusId);
261
- const stillNonTerminal = effectiveDeliveries.filter(d => !isStatusCascadable(d.executionStatus ?? ''));
262
- if (stillNonTerminal.length === 0) {
263
- console.log(`[focus-executor] All deliveries terminal after wake-cycle advance for "${focusName}" -- terminating team`);
264
- teamState.shutdownReason = 'work_complete';
265
- deps.terminateTeam(focusId);
266
- return;
267
- }
268
- }
269
- catch (err) {
270
- console.warn(`[focus-executor] Failed to re-fetch deliveries after wake-cycle advance for "${focusName}": ${err.message}`);
271
- }
272
- }
273
- // Recompute the picture on the post-advance state. The team owns exactly two
274
- // kinds of non-terminal work: planning deliveries (it scopes them) and
275
- // agent-actionable deliveries that still carry open issues (it codes them).
276
- const postNonTerminal = effectiveDeliveries.filter(d => !isStatusCascadable(d.executionStatus ?? ''));
277
- const postActionableIds = new Set(effectiveDeliveries
278
- .filter(d => isStatusAgentActionable(d.executionStatus ?? ''))
279
- .map(d => d.id));
280
- const postHasActionableOpen = currentIssues.some(i => postActionableIds.has(i.deliveryId) && OPEN_ISSUE_STATUSES.has(i.status));
281
- // A read_only focus is awaiting the human ratify gate (audit intake): its
282
- // `planning` deliveries are held, not team work the team is failing to
283
- // advance. Counting them as actionable made a not-yet-ratified audit focus
284
- // accrue no-progress cycles it could never clear -> a spurious no-progress
285
- // team_failure (escalation aa58d9c3, focus "Test Quality Audit"). Resolve the
286
- // ratification state FRESH (it can flip read_only=false the moment a human
287
- // ratifies) and fail open to readOnly=false so a lookup failure preserves the
288
- // prior (escalate-eligible) behavior rather than silently muting the guard.
289
- // Resolve the audit-phase state FRESH: an explicit read_only=true, OR the
290
- // second structural signal (audit-origin AND not-yet-ratified). The second
291
- // signal makes a flag-absent audit focus (read_only silently null) still count
292
- // as audit-phase here, so its held `planning` deliveries are NOT treated as
293
- // team-actionable -- degrading gracefully instead of the spurious no-progress
294
- // team_failure (injection #7; escalation aa58d9c3 does not reproduce). Fail
295
- // open to NOT-audit-phase so a lookup failure preserves the prior
296
- // (escalate-eligible) behavior rather than silently muting the guard.
297
- let focusAuditPhase = false;
298
- if (deps.getFocusRatificationState) {
299
- try {
300
- const st = await deps.getFocusRatificationState(ctx.config, focusId);
301
- focusAuditPhase = isAuditPhaseFocus({
302
- readOnly: st.readOnly,
303
- auditOrigin: st.auditOrigin ?? false,
304
- ratified: st.ratifiedAt != null,
305
- });
306
- }
307
- catch (err) {
308
- console.warn(`[focus-executor] getFocusRatificationState failed for "${focusName}", ` +
309
- `treating as not-audit-phase: ${err.message}`);
310
- }
311
- }
312
- // Planning deliveries count as team-actionable ONLY on a non-audit-phase focus
313
- // (where the team is expected to scope them). On an audit-phase focus they await
314
- // ratification, so they do not keep the team on the no-progress path. A
315
- // genuinely stuck planning delivery on a NORMAL focus still escalates
316
- // (true-positive preserved). Queued deliveries with zero open issues already
317
- // contribute nothing here -- postHasActionableOpen requires an open issue on an
318
- // agent-actionable delivery -- so a freshly-scoped, not-yet-started queued
319
- // delivery does not, on its own, accrue no-progress cycles.
320
- const hasPlanning = !focusAuditPhase && effectiveDeliveries.some(d => (d.executionStatus ?? '') === 'planning');
321
- const teamHasActionableWork = hasPlanning || postHasActionableOpen;
322
- // Resolve the focus worktree once for BOTH the git-activity snapshot
323
- // dimension and the dirty re-wake guard below. A team that wrote AND committed
324
- // code this cycle moved the focus branch HEAD even when no DB state moved;
325
- // feeding that sha into the snapshot makes the commit reset the no-progress
326
- // counter through the EXISTING madeProgress/snapshotsEqual machinery (no
327
- // change to the madeProgress logic is needed). null when no worktree is
328
- // registered or git could not be read -- the watchdog then behaves exactly as
329
- // before on the git axis.
330
- const resolveWorktree = deps.getFocusWorktree ?? defaultGetFocusWorktree;
331
- const readHeadSha = deps.readWorktreeHeadSha ?? defaultReadWorktreeHeadSha;
332
- const focusWorktreePath = resolveWorktree(focusId)?.worktreePath ?? null;
333
- const focusHeadSha = focusWorktreePath !== null ? readHeadSha(focusWorktreePath) : null;
334
- // Capture the (post-advance) snapshot for progress detection. Treat both null
335
- // and undefined as "no prior snapshot" -- legacy test fixtures may not set
336
- // the field, and a fresh team has lastProgressSnapshot=null. Snapshotting the
337
- // effective (refreshed) deliveries ensures a self-heal that moved a delivery
338
- // out of verify_failed registers as progress and resets the counter.
339
- //
340
- // Computed BEFORE the no-actionable-work branch so the non-advanceable guard
341
- // below can gate on "no progress this cycle" -- the branch returns early, so
342
- // the original (post-branch) snapshot location was never reached on this path.
343
- const snapshot = takeProgressSnapshot(effectiveDeliveries, currentIssues, focusHeadSha);
344
- const priorSnapshot = teamState.lastProgressSnapshot ?? null;
345
- // NB: a GENUINE review window is already handled ABOVE by the isInReviewWindow
346
- // early-return (review_requested_at set AND a delivery still in `verify` ->
347
- // the coding team does not tear down and never reaches this counter logic;
348
- // deliveries stay in `verify` throughout a review per the lifecycle). So a
349
- // blanket "review_requested_at => progress" flag here would NOT protect a
350
- // genuine review (that path never gets here) -- it would only fire in the
351
- // degenerate state "review_requested_at set, NO verify delivery" (deliveries
352
- // already bounced to verify_failed, or an in_review delivery). Treating THAT
353
- // as focus-wide progress masks a co-resident genuinely-stuck queued/coding
354
- // delivery the team owns and is not moving (the team-stall the watchdog must
355
- // still catch). It is unnecessary besides: the bounced-to-verify_failed case
356
- // is covered by verifyFailedPendingRequeue below, an in_review delivery is
357
- // non-actionable and routes to terminate_no_actionable_work, and a review
358
- // that never runs is caught by the review-window staleness detector. So there
359
- // is deliberately NO review_requested_at progress flag here.
360
- // verify_failed-pending-requeue is daemon-owned advancement. A delivery left
361
- // in verify_failed is waiting on the daemon's transparent
362
- // verify_failed -> queued requeue (listener-auto-advance); the TEAM cannot
363
- // move it until that requeue lands, even though verify_failed is
364
- // agent-actionable and keeps teamHasActionableWork true. Without this, a
365
- // verify_failed delivery that sits flat across cycles (same status, same
366
- // gate_state, same iteration_count -- the entry bump already happened) would
367
- // accrue toward a spurious team_failure no-progress escalation purely on
368
- // daemon requeue latency. The injection calls this out explicitly: exclude
369
- // verify_failed-pending-requeue from the team_failure no-progress path. The
370
- // safety backfill that makes this patience safe is the D3 verify_failed
371
- // iteration-cap detector -- a genuinely unbounded re-code cycle escalates with
372
- // its own verify_gate_unclearable reason, so excluding it here cannot hide a
373
- // real wedge.
374
- //
375
- // CRITICAL SCOPING: this resets the no-progress counter (madeProgress) but is
376
- // deliberately NOT folded into advancedThisCycle below. advancedThisCycle
377
- // gates the structural dead-end guard, which fires only on the !actionable
378
- // path. A verify_failed delivery that is genuinely non-advanceable (NULL
379
- // workflow stage -> the daemon can never requeue it) survives the pre-guard
380
- // self-heal advance and lands on that !actionable path; it MUST still escalate
381
- // as a non-advanceable dead end (the spawn -> idle-exit -> respawn thrash).
382
- // "Pending requeue" means the daemon can requeue it; a NULL-stage verify_failed
383
- // is NOT pending requeue, it is wedged. Counting it as advancement would
384
- // re-open the very thrash this watchdog exists to stop.
385
- const verifyFailedPendingRequeue = effectiveDeliveries.some(d => (d.executionStatus ?? '') === 'verify_failed');
386
- const madeProgress = verifyFailedPendingRequeue || priorSnapshot === null
387
- ? true
388
- : !snapshotsEqual(priorSnapshot, snapshot);
389
- // Did the team *positively advance* state this cycle? Distinct from
390
- // madeProgress: a null prior snapshot (a FRESH or RESPAWNED team) is NOT
391
- // evidence of advancement -- the team simply has no baseline to compare
392
- // against. madeProgress treats a null prior as `true` so a team's first cycle
393
- // is not penalised toward the multi-cycle no-progress counter; but the
394
- // structural dead-end guard below must NOT inherit that optimism. In the real
395
- // thrash the daemon terminates and respawns the team every poll, so the
396
- // completion handler ALWAYS sees priorSnapshot === null -> madeProgress always
397
- // true -> the dead-end guard (when gated on !madeProgress) was unreachable and
398
- // the focus churned forever. Gate the dead-end guard on !advancedThisCycle
399
- // instead: false for a null prior (eligible -> can escalate), and false for an
400
- // unchanged snapshot (genuinely stuck), but true when the team actually moved
401
- // state this cycle (a live, progressing team is not wedged).
402
- // verify_failed-pending-requeue is deliberately NOT included here (see the
403
- // CRITICAL SCOPING note above): a NULL-stage verify_failed reaches the
404
- // !actionable dead-end guard and must remain eligible to escalate.
405
- const advancedThisCycle = priorSnapshot !== null && !snapshotsEqual(priorSnapshot, snapshot);
406
- // ── Decide: handoff vs terminate ────────────────────────────────────────
407
- // The decision tree is pure and isolated in completion-decision.ts
408
- // (decideCompletionAction); this handler computes the facts and executes
409
- // the chosen action's side effects.
410
- //
411
- // Non-advanceability lookup, gated by shouldCheckNonAdvanceable: scoped to
412
- // agent-actionable deliveries (queued / coding / verify_failed). Those are
413
- // the states the team is *expected* to move; when one carries no open
414
- // issues AND cannot be advanced by the daemon either, it is a true dead
415
- // end -> the thrash loop. Daemon/review-managed states (in_review, paused)
416
- // are deliberately excluded: another subsystem owns advancing them, so a
417
- // NULL/manual stage there is not a thrash dead end and must keep
418
- // terminating cleanly. Gated on !advancedThisCycle (NOT !madeProgress):
419
- // the daemon respawns the team every poll, so a wedged focus reaches here
420
- // with a null prior snapshot EVERY cycle. !madeProgress was therefore never
421
- // true on the live thrash path and the guard never fired -- the focus
422
- // churned a fresh team indefinitely.
423
- let stuck = null;
424
- if (shouldCheckNonAdvanceable({ teamHasActionableWork, advancedThisCycle })) {
425
- const candidates = postNonTerminal.filter(d => isStatusAgentActionable(d.executionStatus ?? ''));
426
- stuck = await findNonAdvanceableDelivery(candidates, deps.fetchEffectiveWorkflow);
427
- }
428
- // The no-progress counter value AFTER this cycle's reset-or-increment.
429
- // Only committed to teamState on the actionable-work paths below -- the
430
- // no-actionable-work terminations deliberately do not touch the counter
431
- // (matching the pre-decision-extraction behavior).
432
- const updatedNoProgressCycles = madeProgress ? 0 : (teamState.noProgressCycles ?? 0) + 1;
433
- // Measurable progress this cycle (any axis moved, including a fresh commit on
434
- // the focus branch) means the prior dirty episode -- if any -- is no longer
435
- // stalled, so the commit-nudge budget resets alongside the no-progress
436
- // counter. Committed to teamState on the same branches that commit the
437
- // no-progress counter (the actionable-work paths), keeping the two in lockstep.
438
- if (madeProgress) {
439
- teamState.commitNudgeAttempts = 0;
440
- }
108
+ // 4. Wake-cycle self-heal: clear verify_failed-with-all-Done via the
109
+ // daemon's transparent advance before any progress accounting.
110
+ const heal = await runWakeCycleSelfHeal(ctx, deps, currentDeliveries, currentIssues);
111
+ if (heal.terminated)
112
+ return;
113
+ // 5. Compute the factual picture once, then decide (pure).
114
+ const facts = await computeCompletionFacts(ctx, deps, currentDeliveries, heal.effectiveDeliveries, currentIssues);
441
115
  const action = decideCompletionAction({
442
- teamHasActionableWork,
443
- advancedThisCycle,
444
- hasNonAdvanceableStuck: stuck !== null,
445
- noProgressCycles: updatedNoProgressCycles,
116
+ teamHasActionableWork: facts.teamHasActionableWork,
117
+ advancedThisCycle: facts.advancedThisCycle,
118
+ hasNonAdvanceableStuck: facts.stuck !== null,
119
+ noProgressCycles: facts.updatedNoProgressCycles,
446
120
  noProgressLimit: SCOPE_COVERAGE_NO_PROGRESS_LIMIT,
447
- hasActionableOpenIssues: postHasActionableOpen,
121
+ hasActionableOpenIssues: facts.postHasActionableOpen,
448
122
  });
449
- const shouldRecordDeliveryAdvancementCycle = teamHasActionableWork &&
450
- action.kind !== 'escalate_no_progress_and_terminate' &&
451
- (priorSnapshot === null || !madeProgress);
452
- const deliveryAdvancement = shouldRecordDeliveryAdvancementCycle
453
- ? recordDeliveryAdvancementCycle(focusId, effectiveDeliveries,
454
- // Reuse the SAME issue + head-sha values event.ts already computed for the
455
- // madeProgress snapshot above -- passed through, never re-derived -- so the
456
- // guard's cross-respawn progress axes stay consistent with madeProgress
457
- // without a second issue fetch or git read (AC#5). These broaden the guard
458
- // beyond execution_status so a done delivery pinned at `coding` alongside a
459
- // freshly-added queued sibling no longer reads as a stall while real issue
460
- // transitions and commits land (escalation a4a4d0ac).
461
- currentIssues, focusHeadSha, DELIVERY_ADVANCEMENT_NO_PROGRESS_LIMIT)
462
- : null;
463
- if (deliveryAdvancement?.thresholdReached) {
464
- let alreadyEscalated = false;
465
- try {
466
- alreadyEscalated = await deps.hasOpenTeamFailureEscalation(focusId);
467
- }
468
- catch (err) {
469
- console.warn(`[focus-executor] Open-escalation check failed for "${focusName}", proceeding to escalate delivery-advancement stall: ${err.message}`);
470
- }
471
- if (!alreadyEscalated) {
472
- await escalateDeliveryAdvancementStalled({
473
- organizationId: teamState.organizationId,
474
- sessionId: teamState.leadSessionId ?? '',
475
- focusId,
476
- focusName,
477
- agentRoleId: teamState.roleId,
478
- cycles: deliveryAdvancement.noAdvancementCycles,
479
- deliveries: effectiveDeliveries,
480
- }, deps);
481
- }
482
- else {
483
- console.log(`[focus-executor] Delivery-advancement stall for "${focusName}" but an open ` +
484
- `team_failure escalation already exists -- terminating without filing a duplicate`);
485
- }
486
- teamState.shutdownReason = 'work_complete';
487
- deps.terminateTeam(focusId);
123
+ // 6. Cross-respawn delivery-advancement watchdog (may escalate + terminate).
124
+ if (await checkDeliveryAdvancementStall(ctx, deps, facts, action, DELIVERY_ADVANCEMENT_NO_PROGRESS_LIMIT))
488
125
  return;
489
- }
490
- switch (action.kind) {
491
- case 'escalate_non_advanceable_and_terminate': {
492
- // No team-actionable work AND the team did not advance state this cycle
493
- // AND at least one remaining non-terminal delivery is *non-advanceable*
494
- // (NULL stage, or no automatic transition out of its current stage):
495
- // nothing -- not the team, not the daemon -- will ever move it.
496
- // Terminating cleanly would just feed the spawn -> idle-exit -> respawn
497
- // thrash loop. Per the asymmetry principle the daemon must escalate,
498
- // never silently churn. File EXACTLY ONE escalation (idempotent: skip if
499
- // one is already open) and terminate. The escalation is filed as
500
- // escalation_kind 'team_failure', which the existing respawn guard
501
- // already suppresses, so the focus is not immediately respawned.
502
- let alreadyEscalated = false;
503
- try {
504
- alreadyEscalated = await deps.hasOpenTeamFailureEscalation(focusId);
505
- }
506
- catch (err) {
507
- console.warn(`[focus-executor] Open-escalation check failed for "${focusName}", proceeding to escalate: ${err.message}`);
508
- }
509
- if (!alreadyEscalated) {
510
- console.warn(`[focus-executor] Non-advanceable delivery for "${focusName}" -- ` +
511
- `"${stuck.name}" [${stuck.executionStatus ?? 'draft'}] cannot be advanced ` +
512
- `(stage=${stuck.currentWorkflowStageId ?? 'NULL'}); escalating and terminating ` +
513
- `instead of respawning`);
514
- await escalateNonAdvanceable({
515
- organizationId: teamState.organizationId,
516
- sessionId: teamState.leadSessionId ?? '',
517
- focusId,
518
- focusName,
519
- agentRoleId: teamState.roleId,
520
- stuck: stuck,
521
- remaining: postNonTerminal,
522
- }, deps);
523
- }
524
- else {
525
- console.log(`[focus-executor] Non-advanceable delivery for "${focusName}" but an open ` +
526
- `escalation already exists -- terminating without filing a duplicate`);
527
- }
528
- teamState.lastProgressSnapshot = snapshot;
529
- teamState.shutdownReason = 'work_complete';
530
- deps.terminateTeam(focusId);
531
- return;
532
- }
533
- case 'terminate_no_actionable_work': {
534
- // Every remaining non-terminal delivery is in a daemon/review-managed
535
- // state the coding team cannot advance (in_review, paused, or a
536
- // verify_failed the self-heal could not yet clear). The team is NOT
537
- // stuck on work it can do -- it has nothing to do. Terminate cleanly
538
- // and let the daemon (on-exit advance), the listener auto-advance, and
539
- // the review routing move these deliveries.
540
- console.log(`[focus-executor] No team-actionable work remains for "${focusName}" -- ` +
541
- `${postNonTerminal.length} non-terminal delivery(ies) in daemon-managed states ` +
542
- `(in_review / paused / verify-pending); terminating cleanly without escalation`);
543
- teamState.shutdownReason = 'work_complete';
544
- deps.terminateTeam(focusId);
545
- return;
546
- }
547
- case 'escalate_no_progress_and_terminate': {
548
- // ── Git-aware re-wake-before-terminate ────────────────────────────────
549
- // This is the path that was KILLING PRODUCERS: a coding team that wrote
550
- // all its code but did not commit it sees no delivery/issue STATE move, so
551
- // it crosses the no-progress threshold and -- without this guard -- is
552
- // escalated + terminated. Once it is dead the commit-gate has no live team
553
- // to re-wake, so the uncommitted work is stranded until a human commits.
554
- //
555
- // Before escalating, check whether the team actually PRODUCED work: a
556
- // dirty focus worktree (uncommitted changes) means there is real work to
557
- // preserve. If so AND a live lead is still reachable AND the commit-nudge
558
- // budget is not exhausted, nudge the lead to commit instead of killing it.
559
- // The team stays alive, commits its own work, and the delivery advances
560
- // out of coding through the normal commit-gate -- no manual commit, no
561
- // escalation. A genuinely idle team (clean tree, OR no live lead, OR
562
- // budget exhausted) falls through to the unchanged escalate+terminate
563
- // behavior below, so a team that will never commit still escalates.
564
- const worktreeIsDirty = focusWorktreePath !== null &&
565
- (deps.worktreeHasUncommittedChanges ?? defaultWorktreeHasUncommittedChanges)(focusWorktreePath);
566
- const liveLead = teamState.leadStdin;
567
- // Budget mirrors the advance-block auto-remediation budget so the two
568
- // re-wake paths (this one and delivery-advance-block's) bound retries the
569
- // same way; resolveAutoRemediationBudget reads TELORA_AUTO_REMEDIATION_BUDGET.
570
- const commitNudgeBudget = resolveAutoRemediationBudget();
571
- const nudgesSpent = teamState.commitNudgeAttempts ?? 0;
572
- if (worktreeIsDirty && liveLead && nudgesSpent < commitNudgeBudget) {
573
- // Keep the producing team alive: nudge it to commit, record the
574
- // snapshot + bump the per-episode nudge counter, reset the completion
575
- // detector so the next 'complete' event is a fresh cycle, and RETURN
576
- // WITHOUT terminating or escalating.
577
- teamState.commitNudgeAttempts = nudgesSpent + 1;
578
- teamState.lastProgressSnapshot = snapshot;
579
- console.warn(`[focus-executor] No-progress threshold reached for "${focusName}", but the ` +
580
- `focus worktree has uncommitted changes and a live lead is reachable -- ` +
581
- `re-waking the team to commit instead of terminating ` +
582
- `(commit-nudge attempt ${teamState.commitNudgeAttempts}/${commitNudgeBudget}).`);
583
- sendMessage(liveLead, `Your focus worktree has uncommitted changes, so the daemon cannot advance ` +
584
- `the delivery and will not terminate the team yet. Please commit your work in ` +
585
- `${focusWorktreePath}, and move every finished issue to In Review (its delivery ` +
586
- `advances out of coding once its work issues are Done/Verified/In Review). The ` +
587
- `daemon advances automatically once the worktree is clean. (commit-nudge attempt ` +
588
- `${teamState.commitNudgeAttempts}/${commitNudgeBudget})`);
589
- completionDetector.reset();
590
- return;
591
- }
592
- // ── Reconcile captured work into the verify gate ──────────────────────
593
- // The commit-nudge re-wake did not fire (clean tree, OR no live lead, OR
594
- // budget exhausted). Before escalating, rescue any committed work: the
595
- // reconciler safety-commits a dirty worktree so the team's bytes are
596
- // captured, and if the focus branch now carries committed work, it flips
597
- // each finished coding delivery's In-Progress issues -> In Review and
598
- // advances coding -> verify so the work re-enters the review gate instead
599
- // of dead-ending in coding (the recurring stall this focus fixes). The
600
- // no-progress team_failure escalation below fires ONLY when nothing was
601
- // reconciled -- i.e. genuinely nothing was produced.
602
- const reconcile = deps.reconcileTerminatedTeamWork ?? defaultReconcileTerminatedTeamWork;
603
- let reconciled = { reconciledCount: 0, workCaptured: false };
604
- try {
605
- reconciled = await reconcile({
606
- config: ctx.config,
607
- teamState,
608
- deliveries: effectiveDeliveries,
609
- issues: currentIssues,
610
- worktreePath: focusWorktreePath,
611
- sessionId: teamState.leadSessionId ?? '',
612
- });
613
- }
614
- catch (err) {
615
- console.warn(`[focus-executor] Terminated-work reconciliation failed for "${focusName}": ${err.message}`);
616
- }
617
- if (reconciled.reconciledCount > 0) {
618
- // Committed work re-entered the gate -- terminate cleanly, NO team_failure.
619
- teamState.commitNudgeAttempts = 0;
620
- teamState.noProgressCycles = updatedNoProgressCycles;
621
- teamState.lastProgressSnapshot = snapshot;
622
- console.log(`[focus-executor] Terminated team for "${focusName}" had committed work -- ` +
623
- `reconciled ${reconciled.reconciledCount} coding delivery(ies) into verify; ` +
624
- `terminating WITHOUT a team_failure escalation.`);
625
- teamState.shutdownReason = 'work_complete';
626
- deps.terminateTeam(focusId);
627
- return;
628
- }
629
- // Crossed the no-progress threshold: escalate and shut the team down so
630
- // a human can intervene. Reached only when the team HAS actionable work
631
- // it owns -- a planning delivery to scope, or a coding delivery whose
632
- // issues stay open -- yet has not moved it for N consecutive cycles. The
633
- // no-actionable-work cases (verify_failed-with-all-Done, in_review,
634
- // paused) terminate cleanly above and never reach here, so this
635
- // escalation now means a genuinely stuck team, not a daemon-managed
636
- // transition. The worktree is clean here (or no lead / budget spent), so
637
- // the dirty episode -- if there was one -- is over: reset the nudge counter.
638
- teamState.commitNudgeAttempts = 0;
639
- teamState.noProgressCycles = updatedNoProgressCycles;
640
- teamState.lastProgressSnapshot = snapshot;
641
- console.warn(`[focus-executor] Scope-coverage guard tripped for "${focusName}" -- ` +
642
- `${teamState.noProgressCycles} consecutive cycles with no progress on ${postNonTerminal.length} ` +
643
- `non-terminal delivery(ies); escalating and terminating`);
644
- await escalateNoProgress({
645
- organizationId: teamState.organizationId,
646
- sessionId: teamState.leadSessionId ?? '',
647
- focusId,
648
- focusName,
649
- agentRoleId: teamState.roleId,
650
- cycles: teamState.noProgressCycles,
651
- remaining: postNonTerminal,
652
- }, deps);
653
- teamState.shutdownReason = 'work_complete';
654
- deps.terminateTeam(focusId);
655
- return;
656
- }
657
- case 'handoff_new_work':
658
- case 'reengage': {
659
- teamState.noProgressCycles = updatedNoProgressCycles;
660
- teamState.lastProgressSnapshot = snapshot;
661
- // Actionable deliveries with open issues -> wake message with the new
662
- // work. Otherwise (planning deliveries remain) -> re-engagement nudge to
663
- // scope them.
664
- let message;
665
- if (action.kind === 'handoff_new_work') {
666
- console.log(`[focus-executor] Mid-focus handoff for "${focusName}" -- sending new work`);
667
- message = buildWakeMessage(effectiveDeliveries, currentIssues, teamState.knownDeliveryIds);
668
- }
669
- else {
670
- console.log(`[focus-executor] Re-engaging "${focusName}" -- ${postNonTerminal.length} non-terminal ` +
671
- `delivery(ies) remain (cycle ${teamState.noProgressCycles}/${SCOPE_COVERAGE_NO_PROGRESS_LIMIT})`);
672
- message = buildReEngagementMessage(postNonTerminal, currentIssues);
673
- }
674
- // Update tracking state
675
- for (const d of currentDeliveries) {
676
- teamState.knownDeliveryIds.add(d.id);
677
- teamState.deliveryStageIds.set(d.id, d.currentWorkflowStageId);
678
- }
679
- completionDetector.reset();
680
- sendMessage(proc.stdin, message);
681
- return;
682
- }
683
- }
126
+ // 7. Execute the chosen action's side effects.
127
+ await executeCompletionAction(ctx, deps, facts, action, SCOPE_COVERAGE_NO_PROGRESS_LIMIT);
684
128
  }
685
129
  // ── Progress snapshot helpers ───────────────────────────────────────────
686
130
  // Extracted to ./progress-snapshot.ts (keeps this file under the structural