@telora/daemon 0.22.132 → 0.22.136

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 (549) hide show
  1. package/build-info.json +6 -3
  2. package/dist/audit/audit-hooks.d.ts +6 -2
  3. package/dist/audit/audit-hooks.d.ts.map +1 -1
  4. package/dist/audit/audit-hooks.js +12 -5
  5. package/dist/audit/audit-hooks.js.map +1 -1
  6. package/dist/audit/audit-phase.js +1 -1
  7. package/dist/audit/audit-phase.js.map +1 -1
  8. package/dist/backends/index.d.ts.map +1 -1
  9. package/dist/backends/index.js +1 -0
  10. package/dist/backends/index.js.map +1 -1
  11. package/dist/completion/completion-phase-registry.d.ts +4 -0
  12. package/dist/completion/completion-phase-registry.d.ts.map +1 -1
  13. package/dist/completion/completion-phase-registry.js +63 -18
  14. package/dist/completion/completion-phase-registry.js.map +1 -1
  15. package/dist/completion/exit-classification.d.ts.map +1 -1
  16. package/dist/completion/exit-classification.js +1 -0
  17. package/dist/completion/exit-classification.js.map +1 -1
  18. package/dist/completion/review-approved-head-gate.d.ts +53 -0
  19. package/dist/completion/review-approved-head-gate.d.ts.map +1 -0
  20. package/dist/completion/review-approved-head-gate.js +124 -0
  21. package/dist/completion/review-approved-head-gate.js.map +1 -0
  22. package/dist/completion/review-census-record.d.ts +52 -0
  23. package/dist/completion/review-census-record.d.ts.map +1 -0
  24. package/dist/completion/review-census-record.js +145 -0
  25. package/dist/completion/review-census-record.js.map +1 -0
  26. package/dist/completion/review-exit-deps.d.ts +5 -1
  27. package/dist/completion/review-exit-deps.d.ts.map +1 -1
  28. package/dist/completion/review-exit-deps.js +7 -2
  29. package/dist/completion/review-exit-deps.js.map +1 -1
  30. package/dist/completion/review-exit-phase.d.ts.map +1 -1
  31. package/dist/completion/review-exit-phase.js +29 -16
  32. package/dist/completion/review-exit-phase.js.map +1 -1
  33. package/dist/completion/review-exit-progress.d.ts +8 -0
  34. package/dist/completion/review-exit-progress.d.ts.map +1 -1
  35. package/dist/completion/review-exit-progress.js +14 -0
  36. package/dist/completion/review-exit-progress.js.map +1 -1
  37. package/dist/completion/review-exit-shortfall-park.d.ts +14 -0
  38. package/dist/completion/review-exit-shortfall-park.d.ts.map +1 -0
  39. package/dist/completion/review-exit-shortfall-park.js +52 -0
  40. package/dist/completion/review-exit-shortfall-park.js.map +1 -0
  41. package/dist/completion/review-exit-shortfall.js +1 -1
  42. package/dist/completion/review-exit-shortfall.js.map +1 -1
  43. package/dist/completion/review-exit-verdict-gate.d.ts +58 -0
  44. package/dist/completion/review-exit-verdict-gate.d.ts.map +1 -1
  45. package/dist/completion/review-exit-verdict-gate.js +68 -8
  46. package/dist/completion/review-exit-verdict-gate.js.map +1 -1
  47. package/dist/completion/review-next-step.d.ts +15 -3
  48. package/dist/completion/review-next-step.d.ts.map +1 -1
  49. package/dist/completion/review-next-step.js +7 -2
  50. package/dist/completion/review-next-step.js.map +1 -1
  51. package/dist/completion/standing-approval-convergence.d.ts +68 -0
  52. package/dist/completion/standing-approval-convergence.d.ts.map +1 -0
  53. package/dist/completion/standing-approval-convergence.js +230 -0
  54. package/dist/completion/standing-approval-convergence.js.map +1 -0
  55. package/dist/completion/standing-approval-deps.d.ts +5 -0
  56. package/dist/completion/standing-approval-deps.d.ts.map +1 -0
  57. package/dist/completion/standing-approval-deps.js +37 -0
  58. package/dist/completion/standing-approval-deps.js.map +1 -0
  59. package/dist/completion/state-cascade.d.ts +11 -0
  60. package/dist/completion/state-cascade.d.ts.map +1 -1
  61. package/dist/completion/state-cascade.js +40 -1
  62. package/dist/completion/state-cascade.js.map +1 -1
  63. package/dist/completion/team-lifecycle.d.ts +14 -2
  64. package/dist/completion/team-lifecycle.d.ts.map +1 -1
  65. package/dist/completion/team-lifecycle.js +36 -36
  66. package/dist/completion/team-lifecycle.js.map +1 -1
  67. package/dist/completion/terminated-work-reconcile.d.ts +3 -1
  68. package/dist/completion/terminated-work-reconcile.d.ts.map +1 -1
  69. package/dist/completion/terminated-work-reconcile.js +24 -11
  70. package/dist/completion/terminated-work-reconcile.js.map +1 -1
  71. package/dist/delivery/delivery-advance-block.d.ts +6 -0
  72. package/dist/delivery/delivery-advance-block.d.ts.map +1 -1
  73. package/dist/delivery/delivery-advance-block.js +29 -3
  74. package/dist/delivery/delivery-advance-block.js.map +1 -1
  75. package/dist/delivery/delivery-reconciler.d.ts.map +1 -1
  76. package/dist/delivery/delivery-reconciler.js +4 -1
  77. package/dist/delivery/delivery-reconciler.js.map +1 -1
  78. package/dist/delivery/operator-hold.d.ts.map +1 -1
  79. package/dist/delivery/operator-hold.js +3 -1
  80. package/dist/delivery/operator-hold.js.map +1 -1
  81. package/dist/directive/directive-executor.d.ts.map +1 -1
  82. package/dist/directive/directive-executor.js +6 -0
  83. package/dist/directive/directive-executor.js.map +1 -1
  84. package/dist/directive/directive-queue.d.ts +8 -0
  85. package/dist/directive/directive-queue.d.ts.map +1 -1
  86. package/dist/directive/directive-queue.js +20 -0
  87. package/dist/directive/directive-queue.js.map +1 -1
  88. package/dist/directive/phase-sync.d.ts +5 -0
  89. package/dist/directive/phase-sync.d.ts.map +1 -1
  90. package/dist/directive/phase-sync.js +11 -1
  91. package/dist/directive/phase-sync.js.map +1 -1
  92. package/dist/directive/stranded-review-reroute.d.ts +37 -0
  93. package/dist/directive/stranded-review-reroute.d.ts.map +1 -0
  94. package/dist/directive/stranded-review-reroute.js +150 -0
  95. package/dist/directive/stranded-review-reroute.js.map +1 -0
  96. package/dist/focus/focus-engine.d.ts.map +1 -1
  97. package/dist/focus/focus-engine.js +15 -3
  98. package/dist/focus/focus-engine.js.map +1 -1
  99. package/dist/focus/focus-executor.d.ts.map +1 -1
  100. package/dist/focus/focus-executor.js +9 -65
  101. package/dist/focus/focus-executor.js.map +1 -1
  102. package/dist/focus/focus-lifecycle.d.ts +12 -0
  103. package/dist/focus/focus-lifecycle.d.ts.map +1 -1
  104. package/dist/focus/focus-lifecycle.js +66 -37
  105. package/dist/focus/focus-lifecycle.js.map +1 -1
  106. package/dist/focus/focus-phase.d.ts.map +1 -1
  107. package/dist/focus/focus-phase.js +6 -0
  108. package/dist/focus/focus-phase.js.map +1 -1
  109. package/dist/focus/focus-spawn-decision.d.ts.map +1 -1
  110. package/dist/focus/focus-spawn-decision.js +1 -0
  111. package/dist/focus/focus-spawn-decision.js.map +1 -1
  112. package/dist/focus/focus-team-state.d.ts +10 -0
  113. package/dist/focus/focus-team-state.d.ts.map +1 -1
  114. package/dist/focus/focus-team-state.js +19 -1
  115. package/dist/focus/focus-team-state.js.map +1 -1
  116. package/dist/focus/listener-auto-advance.d.ts.map +1 -1
  117. package/dist/focus/listener-auto-advance.js +1 -0
  118. package/dist/focus/listener-auto-advance.js.map +1 -1
  119. package/dist/focus/listener-spawn.d.ts +10 -0
  120. package/dist/focus/listener-spawn.d.ts.map +1 -1
  121. package/dist/focus/listener-spawn.js +46 -4
  122. package/dist/focus/listener-spawn.js.map +1 -1
  123. package/dist/focus/listener-state.d.ts.map +1 -1
  124. package/dist/focus/listener-state.js +11 -1
  125. package/dist/focus/listener-state.js.map +1 -1
  126. package/dist/focus/listener.d.ts +42 -0
  127. package/dist/focus/listener.d.ts.map +1 -1
  128. package/dist/focus/listener.js +62 -33
  129. package/dist/focus/listener.js.map +1 -1
  130. package/dist/focus/pending-review-approved-head.d.ts +41 -0
  131. package/dist/focus/pending-review-approved-head.d.ts.map +1 -0
  132. package/dist/focus/pending-review-approved-head.js +198 -0
  133. package/dist/focus/pending-review-approved-head.js.map +1 -0
  134. package/dist/focus/pending-spawn-pass-deps.d.ts +5 -0
  135. package/dist/focus/pending-spawn-pass-deps.d.ts.map +1 -1
  136. package/dist/focus/pending-spawn-pass-deps.js +2 -1
  137. package/dist/focus/pending-spawn-pass-deps.js.map +1 -1
  138. package/dist/focus/pending-spawn-review-next-step.d.ts.map +1 -1
  139. package/dist/focus/pending-spawn-review-next-step.js +26 -4
  140. package/dist/focus/pending-spawn-review-next-step.js.map +1 -1
  141. package/dist/focus/review-spawn-coverage.d.ts +87 -0
  142. package/dist/focus/review-spawn-coverage.d.ts.map +1 -0
  143. package/dist/focus/review-spawn-coverage.js +104 -0
  144. package/dist/focus/review-spawn-coverage.js.map +1 -0
  145. package/dist/focus/session-health.d.ts +5 -2
  146. package/dist/focus/session-health.d.ts.map +1 -1
  147. package/dist/focus/session-health.js +2 -1
  148. package/dist/focus/session-health.js.map +1 -1
  149. package/dist/focus/session-process-reap.d.ts +50 -0
  150. package/dist/focus/session-process-reap.d.ts.map +1 -0
  151. package/dist/focus/session-process-reap.js +77 -0
  152. package/dist/focus/session-process-reap.js.map +1 -0
  153. package/dist/focus/unchanged-reviewed-head.d.ts +40 -0
  154. package/dist/focus/unchanged-reviewed-head.d.ts.map +1 -0
  155. package/dist/focus/unchanged-reviewed-head.js +84 -0
  156. package/dist/focus/unchanged-reviewed-head.js.map +1 -0
  157. package/dist/focus/verify-open-issues.d.ts +4 -19
  158. package/dist/focus/verify-open-issues.d.ts.map +1 -1
  159. package/dist/focus/verify-open-issues.js +83 -41
  160. package/dist/focus/verify-open-issues.js.map +1 -1
  161. package/dist/git/focus-integration-sync.d.ts +61 -0
  162. package/dist/git/focus-integration-sync.d.ts.map +1 -0
  163. package/dist/git/focus-integration-sync.js +73 -0
  164. package/dist/git/focus-integration-sync.js.map +1 -0
  165. package/dist/git/focus-own-delta.d.ts +38 -0
  166. package/dist/git/focus-own-delta.d.ts.map +1 -0
  167. package/dist/git/focus-own-delta.js +193 -0
  168. package/dist/git/focus-own-delta.js.map +1 -0
  169. package/dist/git/index.d.ts +2 -0
  170. package/dist/git/index.d.ts.map +1 -1
  171. package/dist/git/index.js +4 -0
  172. package/dist/git/index.js.map +1 -1
  173. package/dist/git/merge.d.ts.map +1 -1
  174. package/dist/git/merge.js +11 -1
  175. package/dist/git/merge.js.map +1 -1
  176. package/dist/git/resolution-restore.d.ts +66 -0
  177. package/dist/git/resolution-restore.d.ts.map +1 -0
  178. package/dist/git/resolution-restore.js +331 -0
  179. package/dist/git/resolution-restore.js.map +1 -0
  180. package/dist/loops/engine.d.ts.map +1 -1
  181. package/dist/loops/engine.js.map +1 -1
  182. package/dist/merge/abandoned-merge-recovery.d.ts +72 -0
  183. package/dist/merge/abandoned-merge-recovery.d.ts.map +1 -0
  184. package/dist/merge/abandoned-merge-recovery.js +117 -0
  185. package/dist/merge/abandoned-merge-recovery.js.map +1 -0
  186. package/dist/merge/focus-merge.d.ts +19 -0
  187. package/dist/merge/focus-merge.d.ts.map +1 -1
  188. package/dist/merge/focus-merge.js +136 -99
  189. package/dist/merge/focus-merge.js.map +1 -1
  190. package/dist/merge/merge-back-activity.d.ts +11 -0
  191. package/dist/merge/merge-back-activity.d.ts.map +1 -0
  192. package/dist/merge/merge-back-activity.js +57 -0
  193. package/dist/merge/merge-back-activity.js.map +1 -0
  194. package/dist/merge/merge-back-conflict.d.ts +119 -0
  195. package/dist/merge/merge-back-conflict.d.ts.map +1 -0
  196. package/dist/merge/merge-back-conflict.js +178 -0
  197. package/dist/merge/merge-back-conflict.js.map +1 -0
  198. package/dist/merge/merge-back-followup.d.ts +109 -0
  199. package/dist/merge/merge-back-followup.d.ts.map +1 -0
  200. package/dist/merge/merge-back-followup.js +316 -0
  201. package/dist/merge/merge-back-followup.js.map +1 -0
  202. package/dist/merge/merge-back-loop.d.ts +2 -0
  203. package/dist/merge/merge-back-loop.d.ts.map +1 -1
  204. package/dist/merge/merge-back-loop.js +14 -15
  205. package/dist/merge/merge-back-loop.js.map +1 -1
  206. package/dist/merge/merge-conflict-escalation.d.ts +12 -0
  207. package/dist/merge/merge-conflict-escalation.d.ts.map +1 -1
  208. package/dist/merge/merge-conflict-escalation.js +37 -2
  209. package/dist/merge/merge-conflict-escalation.js.map +1 -1
  210. package/dist/merge/merge-gate-coverage.d.ts.map +1 -1
  211. package/dist/merge/merge-gate-coverage.js +1 -0
  212. package/dist/merge/merge-gate-coverage.js.map +1 -1
  213. package/dist/prompt/prompt-listing.d.ts +5 -0
  214. package/dist/prompt/prompt-listing.d.ts.map +1 -1
  215. package/dist/prompt/prompt-listing.js +35 -0
  216. package/dist/prompt/prompt-listing.js.map +1 -1
  217. package/dist/prompt-sections/routing-decider-sections.d.ts.map +1 -1
  218. package/dist/prompt-sections/routing-decider-sections.js +2 -0
  219. package/dist/prompt-sections/routing-decider-sections.js.map +1 -1
  220. package/dist/prompt-sections/shortfall-park-section.d.ts +43 -0
  221. package/dist/prompt-sections/shortfall-park-section.d.ts.map +1 -0
  222. package/dist/prompt-sections/shortfall-park-section.js +36 -0
  223. package/dist/prompt-sections/shortfall-park-section.js.map +1 -0
  224. package/dist/queries/agent-resolvable-promotion.d.ts +16 -2
  225. package/dist/queries/agent-resolvable-promotion.d.ts.map +1 -1
  226. package/dist/queries/agent-resolvable-promotion.js +30 -3
  227. package/dist/queries/agent-resolvable-promotion.js.map +1 -1
  228. package/dist/queries/cause-key.d.ts +17 -22
  229. package/dist/queries/cause-key.d.ts.map +1 -1
  230. package/dist/queries/cause-key.js +50 -1
  231. package/dist/queries/cause-key.js.map +1 -1
  232. package/dist/queries/commit-attributions.d.ts +18 -0
  233. package/dist/queries/commit-attributions.d.ts.map +1 -0
  234. package/dist/queries/commit-attributions.js +39 -0
  235. package/dist/queries/commit-attributions.js.map +1 -0
  236. package/dist/queries/deliveries.d.ts.map +1 -1
  237. package/dist/queries/deliveries.js +3 -1
  238. package/dist/queries/deliveries.js.map +1 -1
  239. package/dist/queries/delivery-gate-state.d.ts +21 -5
  240. package/dist/queries/delivery-gate-state.d.ts.map +1 -1
  241. package/dist/queries/delivery-gate-state.js +35 -9
  242. package/dist/queries/delivery-gate-state.js.map +1 -1
  243. package/dist/queries/escalation-consumption.d.ts.map +1 -1
  244. package/dist/queries/escalation-consumption.js +2 -1
  245. package/dist/queries/escalation-consumption.js.map +1 -1
  246. package/dist/queries/escalation-field-bounds.d.ts +48 -0
  247. package/dist/queries/escalation-field-bounds.d.ts.map +1 -0
  248. package/dist/queries/escalation-field-bounds.js +83 -0
  249. package/dist/queries/escalation-field-bounds.js.map +1 -0
  250. package/dist/queries/escalation-refusal.d.ts +88 -0
  251. package/dist/queries/escalation-refusal.d.ts.map +1 -0
  252. package/dist/queries/escalation-refusal.js +217 -0
  253. package/dist/queries/escalation-refusal.js.map +1 -0
  254. package/dist/queries/escalation-suppression.d.ts +6 -0
  255. package/dist/queries/escalation-suppression.d.ts.map +1 -1
  256. package/dist/queries/escalation-suppression.js +4 -13
  257. package/dist/queries/escalation-suppression.js.map +1 -1
  258. package/dist/queries/escalations.d.ts +5 -13
  259. package/dist/queries/escalations.d.ts.map +1 -1
  260. package/dist/queries/escalations.js +25 -30
  261. package/dist/queries/escalations.js.map +1 -1
  262. package/dist/queries/filing-observation.d.ts.map +1 -1
  263. package/dist/queries/filing-observation.js +3 -1
  264. package/dist/queries/filing-observation.js.map +1 -1
  265. package/dist/queries/focus-review-approval.d.ts +2 -0
  266. package/dist/queries/focus-review-approval.d.ts.map +1 -1
  267. package/dist/queries/focus-review-approval.js +7 -2
  268. package/dist/queries/focus-review-approval.js.map +1 -1
  269. package/dist/queries/focuses.d.ts.map +1 -1
  270. package/dist/queries/focuses.js +2 -1
  271. package/dist/queries/focuses.js.map +1 -1
  272. package/dist/queries/incident-correlation.d.ts.map +1 -1
  273. package/dist/queries/incident-correlation.js +4 -1
  274. package/dist/queries/incident-correlation.js.map +1 -1
  275. package/dist/queries/misattributed-commits-record.d.ts +26 -0
  276. package/dist/queries/misattributed-commits-record.d.ts.map +1 -0
  277. package/dist/queries/misattributed-commits-record.js +5 -0
  278. package/dist/queries/misattributed-commits-record.js.map +1 -0
  279. package/dist/queries/reality-tree.d.ts.map +1 -1
  280. package/dist/queries/reality-tree.js +2 -1
  281. package/dist/queries/reality-tree.js.map +1 -1
  282. package/dist/queries/schemas.d.ts +3 -0
  283. package/dist/queries/schemas.d.ts.map +1 -1
  284. package/dist/queries/schemas.js +1 -0
  285. package/dist/queries/schemas.js.map +1 -1
  286. package/dist/queries/shared.js +1 -1
  287. package/dist/queries/shortfall-park-record.d.ts +48 -0
  288. package/dist/queries/shortfall-park-record.d.ts.map +1 -0
  289. package/dist/queries/shortfall-park-record.js +31 -0
  290. package/dist/queries/shortfall-park-record.js.map +1 -0
  291. package/dist/queries/shortfall-rulings.d.ts +71 -0
  292. package/dist/queries/shortfall-rulings.d.ts.map +1 -0
  293. package/dist/queries/shortfall-rulings.js +53 -0
  294. package/dist/queries/shortfall-rulings.js.map +1 -0
  295. package/dist/queries/verification.d.ts +20 -1
  296. package/dist/queries/verification.d.ts.map +1 -1
  297. package/dist/queries/verification.js +48 -8
  298. package/dist/queries/verification.js.map +1 -1
  299. package/dist/resolvers/index.d.ts +2 -2
  300. package/dist/resolvers/index.d.ts.map +1 -1
  301. package/dist/resolvers/index.js +1 -1
  302. package/dist/resolvers/index.js.map +1 -1
  303. package/dist/resolvers/shared/delivery-trailer.d.ts +99 -0
  304. package/dist/resolvers/shared/delivery-trailer.d.ts.map +1 -0
  305. package/dist/resolvers/shared/delivery-trailer.js +103 -0
  306. package/dist/resolvers/shared/delivery-trailer.js.map +1 -0
  307. package/dist/resolvers/shared/git-diff.d.ts +11 -84
  308. package/dist/resolvers/shared/git-diff.d.ts.map +1 -1
  309. package/dist/resolvers/shared/git-diff.js +17 -84
  310. package/dist/resolvers/shared/git-diff.js.map +1 -1
  311. package/dist/resolvers/shared/review-window-commits.d.ts.map +1 -1
  312. package/dist/resolvers/shared/review-window-commits.js +1 -0
  313. package/dist/resolvers/shared/review-window-commits.js.map +1 -1
  314. package/dist/session/session-lineage.d.ts.map +1 -1
  315. package/dist/session/session-lineage.js +1 -0
  316. package/dist/session/session-lineage.js.map +1 -1
  317. package/dist/shell/config.d.ts +2 -1
  318. package/dist/shell/config.d.ts.map +1 -1
  319. package/dist/shell/config.js +21 -4
  320. package/dist/shell/config.js.map +1 -1
  321. package/dist/shell/crash-recovery.d.ts +3 -7
  322. package/dist/shell/crash-recovery.d.ts.map +1 -1
  323. package/dist/shell/crash-recovery.js +28 -31
  324. package/dist/shell/crash-recovery.js.map +1 -1
  325. package/dist/shell/orphan-capture.d.ts +42 -0
  326. package/dist/shell/orphan-capture.d.ts.map +1 -0
  327. package/dist/shell/orphan-capture.js +53 -0
  328. package/dist/shell/orphan-capture.js.map +1 -0
  329. package/dist/shell/worker-pidfile.d.ts +22 -4
  330. package/dist/shell/worker-pidfile.d.ts.map +1 -1
  331. package/dist/shell/worker-pidfile.js +51 -13
  332. package/dist/shell/worker-pidfile.js.map +1 -1
  333. package/dist/spawn/helpers.d.ts +3 -0
  334. package/dist/spawn/helpers.d.ts.map +1 -1
  335. package/dist/spawn/helpers.js +28 -9
  336. package/dist/spawn/helpers.js.map +1 -1
  337. package/dist/spawn/lead-launch.d.ts +34 -0
  338. package/dist/spawn/lead-launch.d.ts.map +1 -0
  339. package/dist/spawn/lead-launch.js +34 -0
  340. package/dist/spawn/lead-launch.js.map +1 -0
  341. package/dist/spawn/lead-process-ownership.d.ts +91 -0
  342. package/dist/spawn/lead-process-ownership.d.ts.map +1 -0
  343. package/dist/spawn/lead-process-ownership.js +120 -0
  344. package/dist/spawn/lead-process-ownership.js.map +1 -0
  345. package/dist/spawn/lifecycle.d.ts +43 -3
  346. package/dist/spawn/lifecycle.d.ts.map +1 -1
  347. package/dist/spawn/lifecycle.js +75 -44
  348. package/dist/spawn/lifecycle.js.map +1 -1
  349. package/dist/spawn/liveness.d.ts.map +1 -1
  350. package/dist/spawn/liveness.js +5 -1
  351. package/dist/spawn/liveness.js.map +1 -1
  352. package/dist/spawn/one-shot-process-ownership.d.ts +48 -0
  353. package/dist/spawn/one-shot-process-ownership.d.ts.map +1 -0
  354. package/dist/spawn/one-shot-process-ownership.js +44 -0
  355. package/dist/spawn/one-shot-process-ownership.js.map +1 -0
  356. package/dist/spawn/process-group-kill.d.ts +79 -0
  357. package/dist/spawn/process-group-kill.d.ts.map +1 -0
  358. package/dist/spawn/process-group-kill.js +210 -0
  359. package/dist/spawn/process-group-kill.js.map +1 -0
  360. package/dist/spawn/process-spawn.d.ts +18 -1
  361. package/dist/spawn/process-spawn.d.ts.map +1 -1
  362. package/dist/spawn/process-spawn.js +51 -20
  363. package/dist/spawn/process-spawn.js.map +1 -1
  364. package/dist/spawn/resolution-budget.d.ts +38 -0
  365. package/dist/spawn/resolution-budget.d.ts.map +1 -0
  366. package/dist/spawn/resolution-budget.js +87 -0
  367. package/dist/spawn/resolution-budget.js.map +1 -0
  368. package/dist/spawn/review-hold-park.d.ts.map +1 -1
  369. package/dist/spawn/review-hold-park.js +37 -5
  370. package/dist/spawn/review-hold-park.js.map +1 -1
  371. package/dist/spawn/spawn-cooldown.d.ts.map +1 -1
  372. package/dist/spawn/spawn-cooldown.js +9 -0
  373. package/dist/spawn/spawn-cooldown.js.map +1 -1
  374. package/dist/spawn/untracked-lead-sweep.d.ts +29 -0
  375. package/dist/spawn/untracked-lead-sweep.d.ts.map +1 -0
  376. package/dist/spawn/untracked-lead-sweep.js +100 -0
  377. package/dist/spawn/untracked-lead-sweep.js.map +1 -0
  378. package/dist/spawn/worker-registry.d.ts.map +1 -1
  379. package/dist/spawn/worker-registry.js +4 -1
  380. package/dist/spawn/worker-registry.js.map +1 -1
  381. package/dist/spawn/worktree-session.d.ts.map +1 -1
  382. package/dist/spawn/worktree-session.js +8 -6
  383. package/dist/spawn/worktree-session.js.map +1 -1
  384. package/dist/testing/fixtures/exit-capture-verify.d.ts +41 -0
  385. package/dist/testing/fixtures/exit-capture-verify.d.ts.map +1 -0
  386. package/dist/testing/fixtures/exit-capture-verify.js +152 -0
  387. package/dist/testing/fixtures/exit-capture-verify.js.map +1 -0
  388. package/dist/testing/fixtures/index.js +1 -1
  389. package/dist/testing/fixtures/pending-spawn-world.d.ts +7 -0
  390. package/dist/testing/fixtures/pending-spawn-world.d.ts.map +1 -1
  391. package/dist/testing/fixtures/pending-spawn-world.js +21 -1
  392. package/dist/testing/fixtures/pending-spawn-world.js.map +1 -1
  393. package/dist/testing/fixtures/stubborn-lead-process.d.ts +22 -0
  394. package/dist/testing/fixtures/stubborn-lead-process.d.ts.map +1 -0
  395. package/dist/testing/fixtures/stubborn-lead-process.js +68 -0
  396. package/dist/testing/fixtures/stubborn-lead-process.js.map +1 -0
  397. package/dist/types/config.d.ts +13 -0
  398. package/dist/types/config.d.ts.map +1 -1
  399. package/dist/types/focus.d.ts +7 -0
  400. package/dist/types/focus.d.ts.map +1 -1
  401. package/dist/types/index.js +1 -1
  402. package/dist/verify/commit-verdict-record.d.ts +13 -3
  403. package/dist/verify/commit-verdict-record.d.ts.map +1 -1
  404. package/dist/verify/commit-verdict-record.js +45 -25
  405. package/dist/verify/commit-verdict-record.js.map +1 -1
  406. package/dist/verify/credit-rearm.d.ts +18 -0
  407. package/dist/verify/credit-rearm.d.ts.map +1 -0
  408. package/dist/verify/credit-rearm.js +69 -0
  409. package/dist/verify/credit-rearm.js.map +1 -0
  410. package/dist/verify/delivery-commit-set.d.ts +10 -0
  411. package/dist/verify/delivery-commit-set.d.ts.map +1 -0
  412. package/dist/verify/delivery-commit-set.js +82 -0
  413. package/dist/verify/delivery-commit-set.js.map +1 -0
  414. package/dist/verify/explicit-rearm.d.ts.map +1 -1
  415. package/dist/verify/explicit-rearm.js +2 -0
  416. package/dist/verify/explicit-rearm.js.map +1 -1
  417. package/dist/verify/failure-signature.d.ts.map +1 -1
  418. package/dist/verify/failure-signature.js +1 -0
  419. package/dist/verify/failure-signature.js.map +1 -1
  420. package/dist/verify/gate-run-registry.d.ts.map +1 -1
  421. package/dist/verify/gate-run-registry.js +3 -0
  422. package/dist/verify/gate-run-registry.js.map +1 -1
  423. package/dist/verify/injection-stamp.d.ts +29 -0
  424. package/dist/verify/injection-stamp.d.ts.map +1 -0
  425. package/dist/verify/injection-stamp.js +139 -0
  426. package/dist/verify/injection-stamp.js.map +1 -0
  427. package/dist/verify/misattributed-commits-read.d.ts +31 -0
  428. package/dist/verify/misattributed-commits-read.d.ts.map +1 -0
  429. package/dist/verify/misattributed-commits-read.js +47 -0
  430. package/dist/verify/misattributed-commits-read.js.map +1 -0
  431. package/dist/verify/misattributed-commits.d.ts +52 -0
  432. package/dist/verify/misattributed-commits.d.ts.map +1 -0
  433. package/dist/verify/misattributed-commits.js +148 -0
  434. package/dist/verify/misattributed-commits.js.map +1 -0
  435. package/dist/verify/no-own-delta-verdict.d.ts +47 -0
  436. package/dist/verify/no-own-delta-verdict.d.ts.map +1 -0
  437. package/dist/verify/no-own-delta-verdict.js +74 -0
  438. package/dist/verify/no-own-delta-verdict.js.map +1 -0
  439. package/dist/verify/shortfall-park-escalation.d.ts +41 -0
  440. package/dist/verify/shortfall-park-escalation.d.ts.map +1 -0
  441. package/dist/verify/shortfall-park-escalation.js +177 -0
  442. package/dist/verify/shortfall-park-escalation.js.map +1 -0
  443. package/dist/verify/shortfall-park.d.ts +109 -0
  444. package/dist/verify/shortfall-park.d.ts.map +1 -0
  445. package/dist/verify/shortfall-park.js +208 -0
  446. package/dist/verify/shortfall-park.js.map +1 -0
  447. package/dist/verify/shortfall-repeat.d.ts +47 -16
  448. package/dist/verify/shortfall-repeat.d.ts.map +1 -1
  449. package/dist/verify/shortfall-repeat.js +77 -23
  450. package/dist/verify/shortfall-repeat.js.map +1 -1
  451. package/dist/verify/shortfall-rulings.d.ts +106 -0
  452. package/dist/verify/shortfall-rulings.d.ts.map +1 -0
  453. package/dist/verify/shortfall-rulings.js +118 -0
  454. package/dist/verify/shortfall-rulings.js.map +1 -0
  455. package/dist/verify/stall-detectors.d.ts.map +1 -1
  456. package/dist/verify/stall-detectors.js +10 -4
  457. package/dist/verify/stall-detectors.js.map +1 -1
  458. package/dist/verify/verification-attribution-base.d.ts +26 -0
  459. package/dist/verify/verification-attribution-base.d.ts.map +1 -0
  460. package/dist/verify/verification-attribution-base.js +127 -0
  461. package/dist/verify/verification-attribution-base.js.map +1 -0
  462. package/dist/verify/verification-attribution-deps.d.ts +32 -0
  463. package/dist/verify/verification-attribution-deps.d.ts.map +1 -0
  464. package/dist/verify/verification-attribution-deps.js +209 -0
  465. package/dist/verify/verification-attribution-deps.js.map +1 -0
  466. package/dist/verify/verification-baseline.d.ts +6 -0
  467. package/dist/verify/verification-baseline.d.ts.map +1 -1
  468. package/dist/verify/verification-baseline.js +27 -11
  469. package/dist/verify/verification-baseline.js.map +1 -1
  470. package/dist/verify/verification-commit-sharing.d.ts.map +1 -1
  471. package/dist/verify/verification-commit-sharing.js +30 -8
  472. package/dist/verify/verification-commit-sharing.js.map +1 -1
  473. package/dist/verify/verification-config.d.ts.map +1 -1
  474. package/dist/verify/verification-config.js +2 -0
  475. package/dist/verify/verification-config.js.map +1 -1
  476. package/dist/verify/verification-default-on.d.ts +3 -0
  477. package/dist/verify/verification-default-on.d.ts.map +1 -1
  478. package/dist/verify/verification-default-on.js +5 -1
  479. package/dist/verify/verification-default-on.js.map +1 -1
  480. package/dist/verify/verification-deps-default.d.ts.map +1 -1
  481. package/dist/verify/verification-deps-default.js +61 -120
  482. package/dist/verify/verification-deps-default.js.map +1 -1
  483. package/dist/verify/verification-deps.d.ts +26 -4
  484. package/dist/verify/verification-deps.d.ts.map +1 -1
  485. package/dist/verify/verification-deterministic.d.ts +15 -0
  486. package/dist/verify/verification-deterministic.d.ts.map +1 -1
  487. package/dist/verify/verification-deterministic.js +1 -12
  488. package/dist/verify/verification-deterministic.js.map +1 -1
  489. package/dist/verify/verification-engine.d.ts +9 -6
  490. package/dist/verify/verification-engine.d.ts.map +1 -1
  491. package/dist/verify/verification-engine.js +43 -62
  492. package/dist/verify/verification-engine.js.map +1 -1
  493. package/dist/verify/verification-evidence-unavailable-feedback.d.ts +2 -1
  494. package/dist/verify/verification-evidence-unavailable-feedback.d.ts.map +1 -1
  495. package/dist/verify/verification-evidence-unavailable-feedback.js +19 -1
  496. package/dist/verify/verification-evidence-unavailable-feedback.js.map +1 -1
  497. package/dist/verify/verification-failure-parser.d.ts +57 -1
  498. package/dist/verify/verification-failure-parser.d.ts.map +1 -1
  499. package/dist/verify/verification-failure-parser.js +116 -10
  500. package/dist/verify/verification-failure-parser.js.map +1 -1
  501. package/dist/verify/verification-feedback.d.ts +60 -43
  502. package/dist/verify/verification-feedback.d.ts.map +1 -1
  503. package/dist/verify/verification-feedback.js +115 -120
  504. package/dist/verify/verification-feedback.js.map +1 -1
  505. package/dist/verify/verification-history-reset.d.ts +2 -1
  506. package/dist/verify/verification-history-reset.d.ts.map +1 -1
  507. package/dist/verify/verification-history-reset.js +4 -2
  508. package/dist/verify/verification-history-reset.js.map +1 -1
  509. package/dist/verify/verification-rework-issue.d.ts +87 -0
  510. package/dist/verify/verification-rework-issue.d.ts.map +1 -0
  511. package/dist/verify/verification-rework-issue.js +212 -0
  512. package/dist/verify/verification-rework-issue.js.map +1 -0
  513. package/dist/verify/verification-rework-messages.d.ts.map +1 -1
  514. package/dist/verify/verification-rework-messages.js +10 -0
  515. package/dist/verify/verification-rework-messages.js.map +1 -1
  516. package/dist/verify/verify-gate-stall.d.ts +2 -0
  517. package/dist/verify/verify-gate-stall.d.ts.map +1 -1
  518. package/dist/verify/verify-gate-stall.js +35 -9
  519. package/dist/verify/verify-gate-stall.js.map +1 -1
  520. package/dist/verify/verify-restatement.d.ts.map +1 -1
  521. package/dist/verify/verify-restatement.js +9 -2
  522. package/dist/verify/verify-restatement.js.map +1 -1
  523. package/dist/worktree/capture-attribution-read.d.ts +47 -0
  524. package/dist/worktree/capture-attribution-read.d.ts.map +1 -0
  525. package/dist/worktree/capture-attribution-read.js +98 -0
  526. package/dist/worktree/capture-attribution-read.js.map +1 -0
  527. package/dist/worktree/capture-attribution.d.ts +26 -0
  528. package/dist/worktree/capture-attribution.d.ts.map +1 -0
  529. package/dist/worktree/capture-attribution.js +111 -0
  530. package/dist/worktree/capture-attribution.js.map +1 -0
  531. package/dist/worktree/index.d.ts.map +1 -1
  532. package/dist/worktree/index.js +2 -1
  533. package/dist/worktree/index.js.map +1 -1
  534. package/dist/worktree/provision.d.ts.map +1 -1
  535. package/dist/worktree/provision.js +23 -6
  536. package/dist/worktree/provision.js.map +1 -1
  537. package/dist/worktree/safety.d.ts +14 -4
  538. package/dist/worktree/safety.d.ts.map +1 -1
  539. package/dist/worktree/safety.js +37 -7
  540. package/dist/worktree/safety.js.map +1 -1
  541. package/dist/worktree/scratch-guard.d.ts +53 -2
  542. package/dist/worktree/scratch-guard.d.ts.map +1 -1
  543. package/dist/worktree/scratch-guard.js +115 -30
  544. package/dist/worktree/scratch-guard.js.map +1 -1
  545. package/dist/worktree/unmerged-capture-escalation.d.ts +52 -0
  546. package/dist/worktree/unmerged-capture-escalation.d.ts.map +1 -0
  547. package/dist/worktree/unmerged-capture-escalation.js +64 -0
  548. package/dist/worktree/unmerged-capture-escalation.js.map +1 -0
  549. package/package.json +2 -2
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lead-launch.d.ts","sourceRoot":"","sources":["../../src/spawn/lead-launch.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,oBAAoB,CAAC;AACzE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAqB,KAAK,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAOxF,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,cAAc,CAAC;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,6FAA6F;IAC7F,KAAK,EAAE,MAAM,8BAA8B,CAAC;CAC7C;AAED;;;;GAIG;AACH,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,qBAAqB,EAC3B,SAAS,GAAE,iBAAsB,GAChC,OAAO,CAAC,8BAA8B,GAAG,IAAI,CAAC,CAsBhD"}
@@ -0,0 +1,34 @@
1
+ // PINNED-BY-BEGIN (generated by gen:pinned-by -- do not hand-edit; checked by lint:pinned-by)
2
+ // Pinned by: packages/daemon/src/spawn/lead-launch.test.ts
3
+ // PINNED-BY-END
4
+ import { recordLeadProcess } from './lead-process-ownership.js';
5
+ import { endSupersededLeadProcesses } from './untracked-lead-sweep.js';
6
+ import { processGroupEnded } from './process-group-kill.js';
7
+ import { createLogger } from '../shell/log.js';
8
+ const log = createLogger({ module: 'lead-launch' });
9
+ /**
10
+ * End the focus's untracked previous leads, then spawn and record the new lead.
11
+ * Returns the lead, or null when the spawn was refused because a previous lead
12
+ * could not be ended (the caller fails the session and releases the team).
13
+ */
14
+ export async function launchLeadProcess(args, ownership = {}) {
15
+ const { focusId, focusName, teamState, sessionId } = args;
16
+ const superseded = await endSupersededLeadProcesses(focusId, focusName, ownership);
17
+ const unended = superseded.filter((r) => !processGroupEnded(r));
18
+ if (unended.length > 0) {
19
+ log.error(`Refusing spawn for "${focusName}": previous lead process group(s) ` +
20
+ `${unended.map((r) => `${r.pgid} (${r.outcome}${r.detail ? `: ${r.detail}` : ''})`).join(', ')} could not be ended -- ` +
21
+ 'a new lead must not supersede a live one; their ownership records are kept for the untracked-process sweep', { focusId });
22
+ return null;
23
+ }
24
+ const proc = args.spawn();
25
+ teamState.leadPid = proc.pid ?? null;
26
+ teamState.leadStdin = proc.stdin;
27
+ teamState.phase = 'executing';
28
+ // Record the detached lead (pid, start identity, focus, session) on the team
29
+ // state and in the pidfile: a hard daemon kill group-reaps it from there, and
30
+ // the running daemon's ownership passes read it.
31
+ recordLeadProcess(proc, teamState, sessionId, ownership);
32
+ return proc;
33
+ }
34
+ //# sourceMappingURL=lead-launch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lead-launch.js","sourceRoot":"","sources":["../../src/spawn/lead-launch.ts"],"names":[],"mappings":"AAAA,8FAA8F;AAC9F,2DAA2D;AAC3D,gBAAgB;AAqBhB,OAAO,EAAE,iBAAiB,EAA0B,MAAM,6BAA6B,CAAC;AACxF,OAAO,EAAE,0BAA0B,EAAE,MAAM,2BAA2B,CAAC;AACvE,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAE/C,MAAM,GAAG,GAAG,YAAY,CAAC,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC,CAAC;AAWpD;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,IAA2B,EAC3B,YAA+B,EAAE;IAEjC,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,GAAG,IAAI,CAAC;IAC1D,MAAM,UAAU,GAAG,MAAM,0BAA0B,CAAC,OAAO,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC;IACnF,MAAM,OAAO,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,CAAC;IAChE,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,GAAG,CAAC,KAAK,CACP,uBAAuB,SAAS,oCAAoC;YACpE,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,OAAO,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,yBAAyB;YACvH,4GAA4G,EAC5G,EAAE,OAAO,EAAE,CACZ,CAAC;QACF,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC;IAC1B,SAAS,CAAC,OAAO,GAAG,IAAI,CAAC,GAAG,IAAI,IAAI,CAAC;IACrC,SAAS,CAAC,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC;IACjC,SAAS,CAAC,KAAK,GAAG,WAAW,CAAC;IAC9B,6EAA6E;IAC7E,8EAA8E;IAC9E,iDAAiD;IACjD,iBAAiB,CAAC,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC;IACzD,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Which agent processes this daemon owns, and ending the ones no team does.
3
+ *
4
+ * Every team lead the daemon spawns is recorded -- pid, process group (== pid,
5
+ * the lead is spawned detached), start identity, focus and session -- both on
6
+ * its FocusTeamState and in the persisted worker pidfile (shell/worker-pidfile).
7
+ * The running daemon reads that record, not only the boot reap and
8
+ * `stop --force`, so it can:
9
+ *
10
+ * - end a team's whole process group at cleanup (`ensureTeamProcessGroupGone`),
11
+ * - verify a recorded pid still names the process it spawned, and
12
+ * - (spawn/untracked-lead-sweep.ts) end a focus's previous lead before a new
13
+ * spawn supersedes it, and any live lead it spawned that no team tracks.
14
+ *
15
+ * A recorded pid is signalled only when its identity is verified: the live
16
+ * process's start identity equals the one recorded at spawn. A pid that was
17
+ * reused by an unrelated process, or an entry with no recorded identity, is
18
+ * never signalled. A lead that has already exited is safe to group-signal: the
19
+ * kernel does not reuse a pid while a process group of that id still has
20
+ * members, so a live group with that id is the lead's own stragglers.
21
+ */
22
+ import type { ChildProcess } from 'node:child_process';
23
+ import type { FocusTeamState } from '../types/index.js';
24
+ import { type WorkerPidEntry } from '../shell/worker-pidfile.js';
25
+ import { type ProcessGroupKillOptions, type ProcessGroupKillResult } from './process-group-kill.js';
26
+ /** Seams shared by the ownership passes; defaults touch the real OS and state. */
27
+ export interface LeadOwnershipDeps {
28
+ readEntries?: () => WorkerPidEntry[];
29
+ /** Persist one ownership record. Defaults to the worker pidfile. */
30
+ recordEntry?: (entry: WorkerPidEntry) => void;
31
+ removeEntry?: (pid: number) => void;
32
+ /** Pids a live team or one-shot worker currently owns. */
33
+ trackedPids?: () => Set<number>;
34
+ readStartIdentity?: (pid: number) => string | null;
35
+ /** True while any non-zombie member of the group is alive. */
36
+ isGroupAlive?: (pgid: number) => boolean;
37
+ terminate?: (pgid: number, opts: ProcessGroupKillOptions) => Promise<ProcessGroupKillResult>;
38
+ now?: () => number;
39
+ /** SIGTERM-to-SIGKILL grace for the team-cleanup kill. Defaults to PROCESS_GROUP_GRACE_MS. */
40
+ graceMs?: number;
41
+ }
42
+ export interface ResolvedLeadOwnershipDeps {
43
+ readEntries: () => WorkerPidEntry[];
44
+ removeEntry: (pid: number) => void;
45
+ trackedPids: () => Set<number>;
46
+ readStartIdentity: (pid: number) => string | null;
47
+ isGroupAlive: (pgid: number) => boolean;
48
+ terminate: (pgid: number, opts: ProcessGroupKillOptions) => Promise<ProcessGroupKillResult>;
49
+ now: () => number;
50
+ }
51
+ /** Every pid a live team (lead) or registered one-shot worker owns right now. */
52
+ export declare function currentlyTrackedPids(): Set<number>;
53
+ export declare function resolveLeadOwnershipDeps(deps: LeadOwnershipDeps): ResolvedLeadOwnershipDeps;
54
+ /**
55
+ * Record a freshly spawned lead: its start identity on the team state, and its
56
+ * ownership record in the pidfile. The record is NOT dropped when the lead
57
+ * closes -- the lead's children can outlive it -- only once its whole process
58
+ * group is confirmed gone (`ensureTeamProcessGroupGone`, or the untracked-process
59
+ * sweep, which prunes a record whose group is already gone).
60
+ */
61
+ export declare function recordLeadProcess(proc: ChildProcess, teamState: FocusTeamState, sessionId: string, deps?: Pick<LeadOwnershipDeps, 'recordEntry' | 'readStartIdentity'>): void;
62
+ /**
63
+ * True when the team's lead pid now names a DIFFERENT live process (pid reuse):
64
+ * a start identity was recorded at spawn and the live one differs. A gone lead
65
+ * (null live identity) or an unrecorded identity is not a mismatch.
66
+ */
67
+ export declare function leadPidReused(team: Pick<FocusTeamState, 'leadPid' | 'leadStartIdentity'>, readStartIdentity?: (pid: number) => string | null): boolean;
68
+ /** Whether a team's process tree is confirmed gone, and why (or why not). */
69
+ export interface TeamProcessTreeVerdict {
70
+ /** True only when no process of this team's group is observed alive. */
71
+ gone: boolean;
72
+ detail: string;
73
+ /** The termination result, when the group was signalled. */
74
+ result: ProcessGroupKillResult | null;
75
+ }
76
+ /**
77
+ * The team-cleanup guarantee: after a team's lead has closed, end anything left
78
+ * in its process group (subagents, MCP children, or a lead that closed its
79
+ * stdio but did not exit) within the grace, SIGKILL stragglers, and CONFIRM the
80
+ * group is gone.
81
+ *
82
+ * Signals only a group whose lead was really spawned (a start identity was
83
+ * recorded), whose pid no other live team now owns, and which was not reused.
84
+ * Absence is confirmed only by observation: a group that survived SIGKILL, could
85
+ * not be signalled, or could not be verified is reported `gone: false` and its
86
+ * ownership record is KEPT -- so the untracked-process sweep keeps acting on it --
87
+ * with the failure logged. Only a confirmed-gone tree discharges the record.
88
+ * Never throws.
89
+ */
90
+ export declare function ensureTeamProcessGroupGone(teamState: FocusTeamState, context: string, deps?: LeadOwnershipDeps): Promise<TeamProcessTreeVerdict>;
91
+ //# sourceMappingURL=lead-process-ownership.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lead-process-ownership.d.ts","sourceRoot":"","sources":["../../src/spawn/lead-process-ownership.ts"],"names":[],"mappings":"AAQA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAGxD,OAAO,EAIL,KAAK,cAAc,EACpB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAML,KAAK,uBAAuB,EAC5B,KAAK,sBAAsB,EAC5B,MAAM,yBAAyB,CAAC;AAKjC,kFAAkF;AAClF,MAAM,WAAW,iBAAiB;IAChC,WAAW,CAAC,EAAE,MAAM,cAAc,EAAE,CAAC;IACrC,oEAAoE;IACpE,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAC;IAC9C,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,0DAA0D;IAC1D,WAAW,CAAC,EAAE,MAAM,GAAG,CAAC,MAAM,CAAC,CAAC;IAChC,iBAAiB,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;IACnD,8DAA8D;IAC9D,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;IACzC,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,uBAAuB,KAAK,OAAO,CAAC,sBAAsB,CAAC,CAAC;IAC7F,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,8FAA8F;IAC9F,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,yBAAyB;IACxC,WAAW,EAAE,MAAM,cAAc,EAAE,CAAC;IACpC,WAAW,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,WAAW,EAAE,MAAM,GAAG,CAAC,MAAM,CAAC,CAAC;IAC/B,iBAAiB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;IAClD,YAAY,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;IACxC,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,uBAAuB,KAAK,OAAO,CAAC,sBAAsB,CAAC,CAAC;IAC5F,GAAG,EAAE,MAAM,MAAM,CAAC;CACnB;AAED,iFAAiF;AACjF,wBAAgB,oBAAoB,IAAI,GAAG,CAAC,MAAM,CAAC,CAOlD;AAED,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,iBAAiB,GAAG,yBAAyB,CAU3F;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,YAAY,EAClB,SAAS,EAAE,cAAc,EACzB,SAAS,EAAE,MAAM,EACjB,IAAI,GAAE,IAAI,CAAC,iBAAiB,EAAE,aAAa,GAAG,mBAAmB,CAAM,GACtE,IAAI,CAUN;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,IAAI,CAAC,cAAc,EAAE,SAAS,GAAG,mBAAmB,CAAC,EAC3D,iBAAiB,GAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,GAAG,IAA+B,GAC3E,OAAO,CAIT;AAED,6EAA6E;AAC7E,MAAM,WAAW,sBAAsB;IACrC,wEAAwE;IACxE,IAAI,EAAE,OAAO,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,4DAA4D;IAC5D,MAAM,EAAE,sBAAsB,GAAG,IAAI,CAAC;CACvC;AA4BD;;;;;;;;;;;;;GAaG;AACH,wBAAsB,0BAA0B,CAC9C,SAAS,EAAE,cAAc,EACzB,OAAO,EAAE,MAAM,EACf,IAAI,GAAE,iBAAsB,GAC3B,OAAO,CAAC,sBAAsB,CAAC,CAcjC"}
@@ -0,0 +1,120 @@
1
+ // PINNED-BY-BEGIN (generated by gen:pinned-by -- do not hand-edit; checked by lint:pinned-by)
2
+ // Pinned by: packages/daemon/src/completion/team-teardown-kills-process-group.test.ts,
3
+ // packages/daemon/src/focus/session-process-reap.test.ts, packages/daemon/src/spawn/lead-launch.test.ts,
4
+ // packages/daemon/src/spawn/lead-process-ownership.test.ts,
5
+ // packages/daemon/src/spawn/spawn-team-process-ownership.test.ts,
6
+ // packages/daemon/src/spawn/untracked-lead-sweep.test.ts
7
+ // PINNED-BY-END
8
+ import { getActiveTeams } from '../focus/focus-team-state.js';
9
+ import { listTrackedWorkers } from './worker-registry.js';
10
+ import { readWorkerPidfile, recordWorkerProcess, removeWorkerPid, } from '../shell/worker-pidfile.js';
11
+ import { PROCESS_GROUP_GRACE_MS, isProcessGroupAlive, processGroupEnded, readProcessStartIdentity, terminateProcessGroup, } from './process-group-kill.js';
12
+ import { createLogger } from '../shell/log.js';
13
+ const log = createLogger({ module: 'lead-process-ownership' });
14
+ /** Every pid a live team (lead) or registered one-shot worker owns right now. */
15
+ export function currentlyTrackedPids() {
16
+ const pids = new Set();
17
+ for (const team of getActiveTeams().values()) {
18
+ if (typeof team.leadPid === 'number')
19
+ pids.add(team.leadPid);
20
+ }
21
+ for (const worker of listTrackedWorkers())
22
+ pids.add(worker.pid);
23
+ return pids;
24
+ }
25
+ export function resolveLeadOwnershipDeps(deps) {
26
+ return {
27
+ readEntries: deps.readEntries ?? (() => readWorkerPidfile()),
28
+ removeEntry: deps.removeEntry ?? ((pid) => removeWorkerPid(pid)),
29
+ trackedPids: deps.trackedPids ?? currentlyTrackedPids,
30
+ readStartIdentity: deps.readStartIdentity ?? readProcessStartIdentity,
31
+ isGroupAlive: deps.isGroupAlive ?? isProcessGroupAlive,
32
+ terminate: deps.terminate ?? ((pgid, opts) => terminateProcessGroup(pgid, opts)),
33
+ now: deps.now ?? Date.now,
34
+ };
35
+ }
36
+ /**
37
+ * Record a freshly spawned lead: its start identity on the team state, and its
38
+ * ownership record in the pidfile. The record is NOT dropped when the lead
39
+ * closes -- the lead's children can outlive it -- only once its whole process
40
+ * group is confirmed gone (`ensureTeamProcessGroupGone`, or the untracked-process
41
+ * sweep, which prunes a record whose group is already gone).
42
+ */
43
+ export function recordLeadProcess(proc, teamState, sessionId, deps = {}) {
44
+ if (typeof proc.pid !== 'number')
45
+ return;
46
+ const leadPid = proc.pid;
47
+ const startIdentity = (deps.readStartIdentity ?? readProcessStartIdentity)(leadPid);
48
+ teamState.leadStartIdentity = startIdentity;
49
+ const entry = {
50
+ pid: leadPid, label: teamState.focusName, focusId: teamState.focusId, sessionId, recordedAt: Date.now(),
51
+ };
52
+ if (startIdentity !== null)
53
+ entry.startIdentity = startIdentity;
54
+ (deps.recordEntry ?? ((e) => recordWorkerProcess(e)))(entry);
55
+ }
56
+ /**
57
+ * True when the team's lead pid now names a DIFFERENT live process (pid reuse):
58
+ * a start identity was recorded at spawn and the live one differs. A gone lead
59
+ * (null live identity) or an unrecorded identity is not a mismatch.
60
+ */
61
+ export function leadPidReused(team, readStartIdentity = readProcessStartIdentity) {
62
+ if (team.leadPid == null || team.leadStartIdentity == null)
63
+ return false;
64
+ const live = readStartIdentity(team.leadPid);
65
+ return live !== null && live !== team.leadStartIdentity;
66
+ }
67
+ async function resolveTeamProcessTree(teamState, context, d, graceMs) {
68
+ const pid = teamState.leadPid;
69
+ const verdict = (gone, detail, result = null) => ({ gone, detail, result });
70
+ if (pid == null)
71
+ return verdict(true, 'no lead process was spawned');
72
+ if (!Number.isInteger(pid) || pid <= 1)
73
+ return verdict(false, `lead pid ${pid} is not a signallable process group`);
74
+ // The kernel does not reuse an id a process group still holds, so a pid now
75
+ // owned by another team's lead, or reused by an unrelated process, means
76
+ // nothing of THIS team's group remains.
77
+ const ownedByAnotherTeam = [...getActiveTeams().values()].some((t) => t !== teamState && t.leadPid === pid);
78
+ if (ownedByAnotherTeam || leadPidReused(teamState, d.readStartIdentity)) {
79
+ return verdict(true, `pid ${pid} now belongs to another process, so no process of this team's group remains`);
80
+ }
81
+ if (teamState.leadStartIdentity == null) {
82
+ if (!d.isGroupAlive(pid))
83
+ return verdict(true, `process group ${pid} is already gone`);
84
+ return verdict(false, `no spawn identity was recorded for pid ${pid}, so its live process group cannot be verified as this team's and was not signalled`);
85
+ }
86
+ const result = await d.terminate(pid, { label: teamState.focusName, context, graceMs });
87
+ if (processGroupEnded(result))
88
+ return verdict(true, `process group ${pid} ${result.outcome}`, result);
89
+ return verdict(false, `process group ${pid} ${result.outcome}${result.detail ? ` (${result.detail})` : ''}`, result);
90
+ }
91
+ /**
92
+ * The team-cleanup guarantee: after a team's lead has closed, end anything left
93
+ * in its process group (subagents, MCP children, or a lead that closed its
94
+ * stdio but did not exit) within the grace, SIGKILL stragglers, and CONFIRM the
95
+ * group is gone.
96
+ *
97
+ * Signals only a group whose lead was really spawned (a start identity was
98
+ * recorded), whose pid no other live team now owns, and which was not reused.
99
+ * Absence is confirmed only by observation: a group that survived SIGKILL, could
100
+ * not be signalled, or could not be verified is reported `gone: false` and its
101
+ * ownership record is KEPT -- so the untracked-process sweep keeps acting on it --
102
+ * with the failure logged. Only a confirmed-gone tree discharges the record.
103
+ * Never throws.
104
+ */
105
+ export async function ensureTeamProcessGroupGone(teamState, context, deps = {}) {
106
+ const d = resolveLeadOwnershipDeps(deps);
107
+ const verdict = await resolveTeamProcessTree(teamState, context, d, deps.graceMs ?? PROCESS_GROUP_GRACE_MS);
108
+ const pid = teamState.leadPid;
109
+ if (verdict.gone) {
110
+ if (pid != null)
111
+ d.removeEntry(pid);
112
+ log.debug(`[process-kill] ${context}: "${teamState.focusName}" process tree confirmed gone -- ${verdict.detail}`);
113
+ }
114
+ else {
115
+ log.error(`[process-kill] ${context}: "${teamState.focusName}" process tree NOT confirmed gone -- ${verdict.detail}; ` +
116
+ 'its ownership record is kept for the untracked-process sweep');
117
+ }
118
+ return verdict;
119
+ }
120
+ //# sourceMappingURL=lead-process-ownership.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lead-process-ownership.js","sourceRoot":"","sources":["../../src/spawn/lead-process-ownership.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,16 @@
1
+ /**
2
+ * Agent lifecycle management — terminate, query, and resolution spawning.
3
+ *
4
+ * Extracted from the former spawner.ts monolith (now ./state.ts) — manages the lifecycle of running agent processes
5
+ * including termination, status queries, and merge conflict resolution agents.
6
+ */
7
+ import { type ChildProcess, type SpawnOptions } from 'node:child_process';
1
8
  import type { DaemonConfig } from '../types/index.js';
2
9
  import { type TokenUsage } from '@telora/daemon-core';
10
+ import { type OneShotOwnershipDeps } from './one-shot-process-ownership.js';
3
11
  import { type TelemetryMetricRow } from '../otlp/telemetry-writer.js';
12
+ import { openSystemSession, closeSystemSession } from '../queries/system-session.js';
13
+ import { type Logger } from '../shell/log.js';
4
14
  /** A worker the reap path tracks: a focus team lead and its identity. */
5
15
  export interface ReapableWorker {
6
16
  /** PID of the team lead `claude --print` process. */
@@ -54,13 +64,33 @@ export declare function buildResolutionAgentPrompt(p: {
54
64
  integrationBranch: string;
55
65
  conflictFiles: string[];
56
66
  focusDescription: string;
67
+ /** A previous run's preserved partial resolution (refs/telora/resolver-partial/...), or null. */
68
+ priorPartialRef?: string | null;
57
69
  }): string;
70
+ /**
71
+ * The process, timer, log, session and ownership effects of {@link spawnResolutionAgent}.
72
+ * An optional seam so a test observes the real spawn/timeout log lines, the kill
73
+ * timer's budget and the ownership record without mock.module; production uses
74
+ * the defaults (the real session writes and ownership record).
75
+ */
76
+ export interface ResolutionAgentEffects {
77
+ spawn: (command: string, args: string[], options: SpawnOptions) => ChildProcess;
78
+ setTimeout: (fn: () => void, ms: number) => ReturnType<typeof setTimeout>;
79
+ /** Signals the resolver's process GROUP (it is spawned detached) by its leader pid. */
80
+ kill: (pid: number, signal: NodeJS.Signals) => void;
81
+ log: Pick<Logger, 'info' | 'warn' | 'error'>;
82
+ openSession?: typeof openSystemSession;
83
+ closeSession?: typeof closeSystemSession;
84
+ ownership?: OneShotOwnershipDeps;
85
+ }
58
86
  /**
59
87
  * Spawn a short-lived, single-purpose Claude Code agent for merge conflict resolution.
60
88
  *
61
89
  * Used when no active team exists for a focus but merge conflicts need resolution.
62
90
  * The agent receives a focused prompt with conflict details, resolves in the existing
63
- * worktree, and exits. No session DB record, no streaming, no team mode.
91
+ * worktree, and exits. It runs under a role-less 'merge' agent_sessions row whose
92
+ * process ownership is recorded (spawn/one-shot-process-ownership.ts). No
93
+ * streaming, no team mode.
64
94
  */
65
95
  export declare function spawnResolutionAgent(params: {
66
96
  config: DaemonConfig;
@@ -77,7 +107,15 @@ export declare function spawnResolutionAgent(params: {
77
107
  * row is still created, just unscoped).
78
108
  */
79
109
  focusId?: string | null;
80
- }): Promise<{
110
+ /**
111
+ * Scales the conflict-sized budget (an escalated retry). Absent => 1. The
112
+ * budget itself is computed from the conflict set or the product's configured
113
+ * `resolutionAgentTimeoutMs` (spawn/resolution-budget.ts).
114
+ */
115
+ budgetMultiplier?: number;
116
+ /** A previous run's preserved partial resolution, named in the prompt. */
117
+ priorPartialRef?: string | null;
118
+ }, effects?: Partial<ResolutionAgentEffects>): Promise<{
81
119
  success: boolean;
82
120
  error?: string;
83
121
  }>;
@@ -97,6 +135,8 @@ export interface AuditSpawnOptions {
97
135
  * cross-cycle memory of the gap's trajectory. Omitted/null -> fresh session.
98
136
  */
99
137
  resumeSessionId?: string | null;
138
+ /** The role-less 'audit' agent_sessions row this assessor runs under (its process ownership is recorded against it). */
139
+ agentSessionId?: string | null;
100
140
  }
101
141
  /** Result of an audit-assessor spawn. */
102
142
  export interface AuditSpawnResult {
@@ -150,5 +190,5 @@ export declare function buildAuditMetricRow(params: {
150
190
  * reality_tree_* MCP tools so it can mutate the graph but not touch workflow
151
191
  * state.
152
192
  */
153
- export declare function spawnAuditAgent(opts: AuditSpawnOptions): Promise<AuditSpawnResult>;
193
+ export declare function spawnAuditAgent(opts: AuditSpawnOptions, ownership?: OneShotOwnershipDeps): Promise<AuditSpawnResult>;
154
194
  //# sourceMappingURL=lifecycle.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"lifecycle.d.ts","sourceRoot":"","sources":["../../src/spawn/lifecycle.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"lifecycle.d.ts","sourceRoot":"","sources":["../../src/spawn/lifecycle.ts"],"names":[],"mappings":"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"}
@@ -3,7 +3,9 @@
3
3
  // packages/daemon/src/spawn/audit-spawn-reaches-spawn.test.ts,
4
4
  // packages/daemon/src/spawn/audit-spawn-stream-json.test.ts,
5
5
  // packages/daemon/src/spawn/audit-telemetry-capture.test.ts,
6
- // packages/daemon/src/spawn/reap-worker-fleet.test.ts
6
+ // packages/daemon/src/spawn/one-shot-process-ownership.test.ts,
7
+ // packages/daemon/src/spawn/reap-worker-fleet.test.ts,
8
+ // packages/daemon/src/spawn/resolution-agent-budget.test.ts
7
9
  // PINNED-BY-END
8
10
  /**
9
11
  * Agent lifecycle management — terminate, query, and resolution spawning.
@@ -12,11 +14,14 @@
12
14
  * including termination, status queries, and merge conflict resolution agents.
13
15
  */
14
16
  import { spawn } from 'node:child_process';
15
- import { runGitSync } from '../git/index.js';
17
+ import { readResolutionCompletion } from '../git/index.js';
18
+ import { computeResolutionAgentBudget, formatResolutionBudget, statConflictFiles } from './resolution-budget.js';
16
19
  import { stripClaudeCodeEnvVars, StreamJsonParser, } from '@telora/daemon-core';
17
20
  import { getActiveTeams } from '../focus/focus-team-state.js';
18
21
  import { isProcessAlive } from './liveness.js';
19
- import { trackChildProcess, listTrackedWorkers } from './worker-registry.js';
22
+ import { listTrackedWorkers } from './worker-registry.js';
23
+ import { ownOneShotProcess } from './one-shot-process-ownership.js';
24
+ import { signalProcessGroup } from './process-group-kill.js';
20
25
  import { buildLeveredSpawnEnvironment } from './spawn-environment.js';
21
26
  import { bufferMetric } from '../otlp/telemetry-writer.js';
22
27
  import { estimateLoopCostUsd } from '../loops/index.js';
@@ -138,8 +143,6 @@ function defaultListWorkers() {
138
143
  return [...byPid.values()];
139
144
  }
140
145
  // ── Merge Conflict Resolution Agent ──────────────────────────────────
141
- /** Timeout for the resolution agent (10 minutes). */
142
- const RESOLUTION_AGENT_TIMEOUT_MS = 10 * 60 * 1000;
143
146
  /**
144
147
  * The resolution agent's prompt. Its steps come from the shared merge-resolution
145
148
  * wording (the same the team-lead message and the prompt section send). Pure --
@@ -156,6 +159,17 @@ export function buildResolutionAgentPrompt(p) {
156
159
  `**Conflicted files:**`,
157
160
  ...p.conflictFiles.map(f => `- ${f}`),
158
161
  ``,
162
+ ...(p.priorPartialRef
163
+ ? [
164
+ `**Earlier partial resolution:** a previous resolver ran out of time on this conflict. Its partial work is`,
165
+ `preserved at \`${p.priorPartialRef}\` (a commit whose tree holds its staged and unstaged edits). Inspect it`,
166
+ `per file with \`git show ${p.priorPartialRef}:<path>\` and reuse what is correct. It may still contain`,
167
+ `conflict markers, so check each file rather than copying the whole tree. Recover per file only: compare with`,
168
+ `\`git diff ${p.priorPartialRef} -- <path>\` and take what is right into the working copy, then \`git add <path>\`.`,
169
+ `Do NOT \`git checkout\`, \`git reset\` or \`git restore\` the ref (or its whole tree) over the in-progress merge.`,
170
+ ``,
171
+ ]
172
+ : []),
159
173
  `**Focus context:** ${p.focusDescription}`,
160
174
  ``,
161
175
  `**Instructions:**`,
@@ -163,16 +177,32 @@ export function buildResolutionAgentPrompt(p) {
163
177
  `Follow the coding standards in CLAUDE.md.`,
164
178
  ].join('\n');
165
179
  }
180
+ const DEFAULT_RESOLUTION_AGENT_EFFECTS = {
181
+ spawn: (command, args, options) => spawn(command, args, options),
182
+ setTimeout: (fn, ms) => setTimeout(fn, ms),
183
+ kill: (pid, signal) => { signalProcessGroup(pid, signal); },
184
+ log,
185
+ openSession: openSystemSession,
186
+ closeSession: closeSystemSession,
187
+ };
166
188
  /**
167
189
  * Spawn a short-lived, single-purpose Claude Code agent for merge conflict resolution.
168
190
  *
169
191
  * Used when no active team exists for a focus but merge conflicts need resolution.
170
192
  * The agent receives a focused prompt with conflict details, resolves in the existing
171
- * worktree, and exits. No session DB record, no streaming, no team mode.
193
+ * worktree, and exits. It runs under a role-less 'merge' agent_sessions row whose
194
+ * process ownership is recorded (spawn/one-shot-process-ownership.ts). No
195
+ * streaming, no team mode.
172
196
  */
173
- export async function spawnResolutionAgent(params) {
174
- const { config, worktreePath, branchName, integrationBranch, conflictFiles, focusDescription, model, focusId } = params;
175
- const prompt = buildResolutionAgentPrompt({ worktreePath, branchName, integrationBranch, conflictFiles, focusDescription });
197
+ export async function spawnResolutionAgent(params, effects = {}) {
198
+ const fx = { ...DEFAULT_RESOLUTION_AGENT_EFFECTS, ...effects };
199
+ const { config, worktreePath, branchName, integrationBranch, conflictFiles, focusDescription, model, focusId, priorPartialRef } = params;
200
+ const budget = computeResolutionAgentBudget({
201
+ files: statConflictFiles(worktreePath, conflictFiles),
202
+ configuredMs: config.resolutionAgentTimeoutMs ?? null,
203
+ ...(params.budgetMultiplier !== undefined ? { multiplier: params.budgetMultiplier } : {}),
204
+ });
205
+ const prompt = buildResolutionAgentPrompt({ worktreePath, branchName, integrationBranch, conflictFiles, focusDescription, priorPartialRef: priorPartialRef ?? null });
176
206
  const args = [
177
207
  '--print',
178
208
  '--dangerously-skip-permissions',
@@ -182,64 +212,67 @@ export async function spawnResolutionAgent(params) {
182
212
  ];
183
213
  // Build a clean environment (strip Claude Code session vars)
184
214
  const spawnEnv = stripClaudeCodeEnvVars(process.env);
185
- log.info(`Spawning resolution agent for branch ${branchName} (${conflictFiles.length} conflicts, model: ${model ?? 'default'})`);
215
+ fx.log.info(`Spawning resolution agent for branch ${branchName} (${conflictFiles.length} conflicts, model: ${model ?? 'default'}, ${formatResolutionBudget(budget)})`);
186
216
  // Open a role-less 'merge' agent_sessions row so this --print one-shot's
187
217
  // run-time reaches OEE Availability (merge is production work: landing
188
218
  // delivered code). Best-effort -- a null id just means no row for this run.
189
- const mergeSessionId = await openSystemSession({
219
+ const closeSession = fx.closeSession ?? closeSystemSession;
220
+ const mergeSessionId = await (fx.openSession ?? openSystemSession)({
190
221
  organizationId: config.organizationId,
191
222
  focusId: focusId ?? null,
192
223
  branchName,
193
224
  sessionType: 'merge',
194
225
  });
195
226
  return new Promise((resolve) => {
196
- const proc = spawn(config.claudeCodePath, args, {
227
+ const proc = fx.spawn(config.claudeCodePath, args, {
197
228
  cwd: worktreePath,
198
229
  env: spawnEnv,
199
- stdio: ['pipe', 'pipe', 'pipe'],
230
+ stdio: ['pipe', 'pipe', 'pipe'], detached: true, // own group: exit/timeout cleanup ends its MCP children too
200
231
  });
201
- // Register so a graceful stop reaps this one-shot if it is still resolving.
202
- trackChildProcess(proc, `merge-resolution:${branchName}`);
232
+ // Track + record ownership so a graceful stop or the untracked sweep can end its whole tree.
233
+ ownOneShotProcess(proc, { label: `merge-resolution:${branchName}`, focusId: focusId ?? null, sessionId: mergeSessionId }, fx.ownership);
203
234
  // Close stdin immediately -- prompt is passed as CLI arg
204
235
  proc.stdin?.end();
205
236
  // Timeout with escalating shutdown
206
- const timeout = setTimeout(() => {
207
- log.warn(`Resolution agent timed out after ${RESOLUTION_AGENT_TIMEOUT_MS / 1000}s, killing`);
208
- try {
209
- process.kill(proc.pid, 'SIGTERM');
210
- }
211
- catch { /* ignore */ }
212
- setTimeout(() => {
237
+ const timeout = fx.setTimeout(() => {
238
+ fx.log.warn(`Resolution agent for branch ${branchName} timed out, killing (${formatResolutionBudget(budget)})`);
239
+ if (proc.pid) {
213
240
  try {
214
- process.kill(proc.pid, 'SIGKILL');
241
+ fx.kill(proc.pid, 'SIGTERM');
215
242
  }
216
243
  catch { /* ignore */ }
244
+ }
245
+ fx.setTimeout(() => {
246
+ if (proc.pid) {
247
+ try {
248
+ fx.kill(proc.pid, 'SIGKILL');
249
+ }
250
+ catch { /* ignore */ }
251
+ }
217
252
  }, 10000);
218
- }, RESOLUTION_AGENT_TIMEOUT_MS);
253
+ }, budget.budgetMs);
219
254
  proc.on('close', (code) => {
220
255
  clearTimeout(timeout);
221
- // Verify resolution by checking for remaining unmerged files
222
- const unmergedResult = runGitSync(['diff', '--name-only', '--diff-filter=U'], worktreePath);
223
- const unmergedFiles = unmergedResult.success
224
- ? unmergedResult.output.split('\n').filter(Boolean)
225
- : [];
226
- const success = unmergedFiles.length === 0;
227
- const msg = success ? undefined : `Conflicts remain in: ${unmergedFiles.join(', ')}`;
256
+ // Verify resolution: a finished merge (no unmerged paths, no MERGE_HEAD,
257
+ // nothing uncommitted), not merely "no unmerged files".
258
+ const completion = readResolutionCompletion(worktreePath);
259
+ const success = completion.finished;
260
+ const msg = success ? undefined : completion.reason;
228
261
  if (success) {
229
- log.info(`Resolution agent succeeded for branch ${branchName} (exit code: ${code})`);
262
+ fx.log.info(`Resolution agent succeeded for branch ${branchName} (exit code: ${code})`);
230
263
  }
231
264
  else {
232
- log.warn(`Resolution agent failed for branch ${branchName}: ${msg}`);
265
+ fx.log.warn(`Resolution agent failed for branch ${branchName}: ${msg}`);
233
266
  }
234
267
  // Close the 'merge' session (stamps ended_at) before resolving, on either
235
268
  // outcome. closeSystemSession never throws; .finally guarantees resolve.
236
- void closeSystemSession(mergeSessionId, { success, exitReason: msg })
269
+ void closeSession(mergeSessionId, { success, exitReason: msg })
237
270
  .finally(() => resolve(success ? { success: true } : { success: false, error: msg }));
238
271
  });
239
272
  proc.on('error', (err) => {
240
273
  clearTimeout(timeout);
241
- log.error(`Resolution agent spawn error: ${err.message}`);
242
- void closeSystemSession(mergeSessionId, { success: false, exitReason: err.message })
274
+ fx.log.error(`Resolution agent spawn error: ${err.message}`);
275
+ void closeSession(mergeSessionId, { success: false, exitReason: err.message })
243
276
  .finally(() => resolve({ success: false, error: `Spawn error: ${err.message}` }));
244
277
  });
245
278
  });
@@ -309,7 +342,7 @@ export function buildAuditMetricRow(params) {
309
342
  * reality_tree_* MCP tools so it can mutate the graph but not touch workflow
310
343
  * state.
311
344
  */
312
- export async function spawnAuditAgent(opts) {
345
+ export async function spawnAuditAgent(opts, ownership = {}) {
313
346
  const { config, prompt, worktreePath, focusId, cycleNumber, model, timeoutMs, allowedTools, resumeSessionId } = opts;
314
347
  const args = [];
315
348
  // --resume must precede the output-format flags.
@@ -344,15 +377,15 @@ export async function spawnAuditAgent(opts) {
344
377
  child = spawn(config.claudeCodePath, args, {
345
378
  cwd: worktreePath ?? config.repoPath,
346
379
  env: stripClaudeCodeEnvVars(spawnEnv, { preserve: ['CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS'] }),
347
- stdio: ['pipe', 'pipe', 'pipe'],
380
+ stdio: ['pipe', 'pipe', 'pipe'], detached: true, // own group: exit/timeout cleanup ends its MCP children too
348
381
  });
349
382
  }
350
383
  catch (err) {
351
384
  resolve({ exitCode: null, stdout: '', stderr: '', errorMessage: `spawn failed: ${err.message}` });
352
385
  return;
353
386
  }
354
- // Register so a graceful stop reaps this one-shot if the audit is still running.
355
- trackChildProcess(child, `audit:${focusId} cycle ${cycleNumber}`);
387
+ // Track + record ownership so a graceful stop or the untracked sweep can end its whole tree.
388
+ ownOneShotProcess(child, { label: `audit:${focusId} cycle ${cycleNumber}`, focusId, sessionId: opts.agentSessionId ?? null }, ownership);
356
389
  child.stdin?.end();
357
390
  // Raw stdout is retained as a fallback (e.g. a non-JSON stub). In normal
358
391
  // operation stdout is NDJSON stream-json; the parser reconstructs the
@@ -402,10 +435,8 @@ export async function spawnAuditAgent(opts) {
402
435
  child.stdout?.on('data', (chunk) => { rawStdout += chunk.toString('utf8'); });
403
436
  child.stderr?.on('data', (chunk) => { stderr += chunk.toString('utf8'); });
404
437
  const timer = setTimeout(() => {
405
- try {
406
- child.kill('SIGKILL');
407
- }
408
- catch { /* ignore */ }
438
+ if (child.pid)
439
+ signalProcessGroup(child.pid, 'SIGKILL');
409
440
  }, timeoutMs);
410
441
  // Prefer the reconstructed assistant text (so parseAuditSummary finds the
411
442
  // fenced block); fall back to raw stdout when no stream-json was parsed.