@sema-agent/server 7.43.0 → 7.44.0

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 (345) hide show
  1. package/USAGE.md +18 -0
  2. package/dist/adoption/plan.js +31 -192
  3. package/dist/adoption/quiesce.js +3 -86
  4. package/dist/adoption/runner.js +3 -140
  5. package/dist/adoption/sql.js +0 -74
  6. package/dist/adoption/wire.js +0 -80
  7. package/dist/approval-ask-machine.js +0 -75
  8. package/dist/approval-card.js +0 -323
  9. package/dist/approval-deny-reasons.js +0 -42
  10. package/dist/approval-hmac.js +0 -35
  11. package/dist/approval-reconciler.js +8 -166
  12. package/dist/approval.js +4 -67
  13. package/dist/audit.js +1 -44
  14. package/dist/auth-bridge.js +4 -47
  15. package/dist/auth-keys.js +0 -23
  16. package/dist/bake-runner/main.js +4 -65
  17. package/dist/bake-runner/protocol.js +3 -58
  18. package/dist/bake-runner/runner.js +5 -91
  19. package/dist/bench/l8/artifact.js +2 -61
  20. package/dist/bench/l8/escape.js +0 -25
  21. package/dist/bench/l8/index.js +0 -14
  22. package/dist/bench/l8/probes.js +0 -60
  23. package/dist/bench/l8/run-probes.js +2 -45
  24. package/dist/bench/s1/arms.js +14 -189
  25. package/dist/bench/s1/live-deps.js +11 -253
  26. package/dist/bench/s1/oracle.js +1 -36
  27. package/dist/bench/s1/repair-oracle-adapter.js +0 -30
  28. package/dist/bench/s1/reviewer.js +0 -34
  29. package/dist/bench/s1/row.js +0 -62
  30. package/dist/bench/s1/run-firm.js +4 -70
  31. package/dist/bench/s1/runner-ctx.js +0 -40
  32. package/dist/bench/s1/tasks.js +0 -115
  33. package/dist/boot/adoption.js +0 -20
  34. package/dist/boot/budget-tracing.js +3 -55
  35. package/dist/boot/config-center.js +43 -551
  36. package/dist/boot/coordinators.js +1 -69
  37. package/dist/boot/crash-last.js +0 -18
  38. package/dist/boot/deferred-sandbox-path-env.js +3 -119
  39. package/dist/boot/execution-env.js +5 -171
  40. package/dist/boot/governance-seams.js +0 -92
  41. package/dist/boot/leader.js +0 -69
  42. package/dist/boot/limit-sync.js +0 -2
  43. package/dist/boot/memory-boundary.js +3 -91
  44. package/dist/boot/org-memory.js +1 -25
  45. package/dist/boot/parked-revive-gate.js +0 -150
  46. package/dist/boot/permission-rules-audit.js +1 -56
  47. package/dist/boot/reapers.js +15 -273
  48. package/dist/boot/resolve-spec.js +9 -768
  49. package/dist/boot/retention-lane.js +4 -87
  50. package/dist/boot/runner-deps.js +3 -297
  51. package/dist/boot/runtime-caps.js +0 -40
  52. package/dist/boot/session-faces.js +2 -133
  53. package/dist/boot/shutdown.js +9 -83
  54. package/dist/boot/side-query-lane.js +2 -137
  55. package/dist/boot/stores.js +15 -215
  56. package/dist/boot/task-list-lane.js +0 -18
  57. package/dist/boot/webfetch-summarize-lane.js +0 -47
  58. package/dist/boot/workflow-orchestration.js +3 -99
  59. package/dist/boot-reclaim.js +0 -20
  60. package/dist/bounded-session-map.js +0 -19
  61. package/dist/brain.js +2 -139
  62. package/dist/budget.js +9 -244
  63. package/dist/capabilities/builtin-tools.js +0 -3
  64. package/dist/capabilities/center-plugins.js +6 -51
  65. package/dist/capabilities/center-prompts.js +6 -71
  66. package/dist/capabilities/code-review-council.js +7 -40
  67. package/dist/capabilities/collab-workflows.js +1 -44
  68. package/dist/capabilities/hands-lane.js +0 -65
  69. package/dist/capabilities/memory-notice.js +0 -70
  70. package/dist/capabilities/prompt.js +0 -14
  71. package/dist/capabilities/prompts/code-review.js +0 -14
  72. package/dist/capabilities/prompts/identity.js +0 -6
  73. package/dist/capabilities/prompts/team.js +0 -4
  74. package/dist/capabilities/repo-tools.js +0 -34
  75. package/dist/capabilities/sandbox-file-send.js +3 -92
  76. package/dist/capabilities/scenarios.d.ts +0 -1
  77. package/dist/capabilities/scenarios.js +18 -279
  78. package/dist/capabilities/select-environment-tool.js +0 -32
  79. package/dist/capabilities/send-user-file-tool.js +2 -66
  80. package/dist/capabilities/skills.d.ts +1 -2
  81. package/dist/capabilities/skills.js +3 -21
  82. package/dist/capabilities/team.d.ts +3 -11
  83. package/dist/capabilities/team.js +3 -51
  84. package/dist/capabilities/tool-defer.js +0 -3
  85. package/dist/config-center/apply-effective.js +15 -330
  86. package/dist/config-center/apply-ledger.js +5 -38
  87. package/dist/config-center/facade.js +0 -41
  88. package/dist/config-center/hot-keys-registry.js +0 -20
  89. package/dist/config-center/http-client.js +2 -125
  90. package/dist/config-center/mcp-revocation.js +2 -34
  91. package/dist/config-center/read-face.js +0 -59
  92. package/dist/config-center/restart-signal.js +1 -85
  93. package/dist/config-center/skills-mcp.d.ts +1 -1
  94. package/dist/config-center/skills-mcp.js +9 -76
  95. package/dist/config-center/stage-limits.js +8 -35
  96. package/dist/config-invariants.js +0 -16
  97. package/dist/config-lkg.js +0 -42
  98. package/dist/config-provider.js +3 -186
  99. package/dist/config-types.js +0 -5
  100. package/dist/config.js +107 -1145
  101. package/dist/degenerate-instrument.js +3 -67
  102. package/dist/deployment-governance.js +0 -124
  103. package/dist/digest-form.js +0 -12
  104. package/dist/elicitation.js +3 -86
  105. package/dist/env-facts.js +7 -75
  106. package/dist/fleet/fleet-bus.js +34 -507
  107. package/dist/fleet/fleet-reconciler.js +9 -149
  108. package/dist/fleet/fleet-terminal-window.js +10 -178
  109. package/dist/fleet/subagent-tail-bus.js +3 -72
  110. package/dist/fleet-client.js +10 -70
  111. package/dist/fleet-lease.js +5 -79
  112. package/dist/git-api-kind.js +0 -3
  113. package/dist/governance-ask-marks.js +2 -78
  114. package/dist/hooks/branch-transcript.js +0 -74
  115. package/dist/hooks/cc-agent-hook-prompt.js +0 -29
  116. package/dist/hooks/cc-stop-prompt.js +1 -46
  117. package/dist/hooks/hook-llm.js +1 -53
  118. package/dist/hooks/hook-runner.js +20 -414
  119. package/dist/http/active-run-conflict.js +4 -129
  120. package/dist/http/cursor-fingerprint.d.ts +5 -0
  121. package/dist/http/cursor-fingerprint.js +5 -0
  122. package/dist/http/idempotency.js +0 -37
  123. package/dist/http/principal-gate.js +3 -40
  124. package/dist/http/route-ctx.js +0 -9
  125. package/dist/http/routes/a2a-serve.js +7 -319
  126. package/dist/http/routes/admin-config-refresh.js +0 -4
  127. package/dist/http/routes/admin-drain.js +0 -10
  128. package/dist/http/routes/adoption.js +1 -29
  129. package/dist/http/routes/agents-roster.js +1 -47
  130. package/dist/http/routes/approvals-assistant.js +32 -395
  131. package/dist/http/routes/attachments.js +4 -22
  132. package/dist/http/routes/capabilities.js +3 -420
  133. package/dist/http/routes/diagnostics.js +1 -81
  134. package/dist/http/routes/fleet.js +9 -185
  135. package/dist/http/routes/images.js +14 -239
  136. package/dist/http/routes/leader.js +0 -13
  137. package/dist/http/routes/memory-bundle.js +1 -61
  138. package/dist/http/routes/memory-policy.js +9 -99
  139. package/dist/http/routes/notify-wake.js +3 -37
  140. package/dist/http/routes/observability.js +2 -19
  141. package/dist/http/routes/retention-ops.js +2 -34
  142. package/dist/http/routes/rules.js +1 -93
  143. package/dist/http/routes/runs.js +64 -873
  144. package/dist/http/routes/session-sync.js +19 -258
  145. package/dist/http/routes/sessions-list.js +17 -43
  146. package/dist/http/routes/sessions.js +34 -226
  147. package/dist/http/routes/shared-memory.js +5 -36
  148. package/dist/http/routes/side-query.js +1 -87
  149. package/dist/http/routes/tasks.js +52 -722
  150. package/dist/http/routes/trace-usage.js +32 -195
  151. package/dist/http/routes/workflows.js +19 -195
  152. package/dist/http/run-meta.js +0 -6
  153. package/dist/http/send.js +0 -32
  154. package/dist/http/server.js +92 -1597
  155. package/dist/http/sse-lifecycle.js +2 -13
  156. package/dist/http/sse-log.js +3 -48
  157. package/dist/http/tar.js +5 -21
  158. package/dist/http/verify-rounds.js +0 -5
  159. package/dist/http/wire-gate.js +0 -9
  160. package/dist/http/workspace-content.js +0 -10
  161. package/dist/images/bake-validate.js +1 -70
  162. package/dist/images/manifest.js +1 -6
  163. package/dist/index.js +0 -21
  164. package/dist/key-resolver.js +2 -17
  165. package/dist/leader/diffout.js +1 -20
  166. package/dist/leader/diffup.js +0 -47
  167. package/dist/leader/endpoint.js +2 -57
  168. package/dist/leader/fanout.js +3 -45
  169. package/dist/leader/grader-env-factory.js +3 -72
  170. package/dist/leader/leader.js +5 -156
  171. package/dist/leader/merge.js +8 -115
  172. package/dist/leader/planner.js +3 -54
  173. package/dist/leader/repair-oracle.js +1 -60
  174. package/dist/leader/repair-wire.js +2 -79
  175. package/dist/leader/wire.js +8 -307
  176. package/dist/lsp/e2b-bridge.js +4 -64
  177. package/dist/lsp/e2b-manager.js +6 -94
  178. package/dist/lsp/lsp-frames.js +0 -12
  179. package/dist/lsp/manager.js +4 -96
  180. package/dist/lsp/ws-transport.js +5 -55
  181. package/dist/lsp-evict.js +1 -15
  182. package/dist/main.js +57 -765
  183. package/dist/memory-bundle-engine.js +0 -55
  184. package/dist/memory-export.js +0 -4
  185. package/dist/memory-posture.js +1 -15
  186. package/dist/memory-scope.js +11 -148
  187. package/dist/memory-sync-client.js +2 -44
  188. package/dist/memory-sync.js +1 -80
  189. package/dist/model-select.js +3 -80
  190. package/dist/observability/cost-quota.js +1 -17
  191. package/dist/observability/cost-taxonomy.js +0 -34
  192. package/dist/observability/fail-open.js +7 -86
  193. package/dist/observability/logger.js +0 -6
  194. package/dist/observability/metrics.js +0 -94
  195. package/dist/observability/otel-exporter.js +3 -13
  196. package/dist/observability/principal-context.js +0 -9
  197. package/dist/observability/prompt-manifest.js +1 -37
  198. package/dist/observability/rate-limit.js +0 -4
  199. package/dist/observability/secret-env-scrub.js +2 -56
  200. package/dist/observability/tool-trace.js +1 -70
  201. package/dist/orchestration/hardened-vm-runner.js +4 -118
  202. package/dist/orchestration/hardened-vm-worker-runner.js +1 -26
  203. package/dist/orchestration/hardened-vm-worker.js +0 -27
  204. package/dist/orchestration/subagent-steer.js +1 -45
  205. package/dist/orchestration/workflow-agent-steer.js +1 -80
  206. package/dist/orchestration/workflow-completion-inbox.js +32 -285
  207. package/dist/orchestration/workflow-notify-journal.js +16 -259
  208. package/dist/org-memory-admission.js +3 -47
  209. package/dist/parent-watch.js +2 -48
  210. package/dist/parked-decide.js +1 -109
  211. package/dist/per-task-image.js +0 -57
  212. package/dist/plan-cache-probe.js +3 -27
  213. package/dist/plugins/adoption-log-sql.js +2 -119
  214. package/dist/plugins/approval-ask-store-memory.js +3 -38
  215. package/dist/plugins/approval-ask-store-sql.js +6 -188
  216. package/dist/plugins/approval-exemption-store.js +2 -28
  217. package/dist/plugins/background-agent-store-sql.js +4 -105
  218. package/dist/plugins/background-shell-support.js +14 -122
  219. package/dist/plugins/blob-backend.js +6 -169
  220. package/dist/plugins/breaker-state-sql.js +8 -46
  221. package/dist/plugins/caching-session-store.js +4 -106
  222. package/dist/plugins/checkpoint-store-sql.js +18 -505
  223. package/dist/plugins/e2b-orphan-reclaim.js +0 -45
  224. package/dist/plugins/file-outcome-sink.js +0 -9
  225. package/dist/plugins/file-resume-anchor-store.js +4 -43
  226. package/dist/plugins/file-run-store.js +26 -364
  227. package/dist/plugins/file-snapshot-store-sql.js +10 -181
  228. package/dist/plugins/fork-routing-session-store.js +8 -111
  229. package/dist/plugins/host-platform.js +2 -91
  230. package/dist/plugins/image-bake-store-sql.js +7 -250
  231. package/dist/plugins/image-index-sql.js +4 -123
  232. package/dist/plugins/k8s-bg-scripts.js +4 -88
  233. package/dist/plugins/k8s-exec-protocol.js +0 -41
  234. package/dist/plugins/leader-run-store-sql.js +0 -101
  235. package/dist/plugins/local-checkpoint-store.js +8 -128
  236. package/dist/plugins/local-session-store.js +34 -296
  237. package/dist/plugins/local-task-attachment-store.js +2 -16
  238. package/dist/plugins/mailbox-store-sql.js +8 -65
  239. package/dist/plugins/memory-embedder-fingerprint.js +5 -166
  240. package/dist/plugins/memory-embedder.js +4 -70
  241. package/dist/plugins/memory-engine-pg.js +6 -170
  242. package/dist/plugins/memory-engine-tidb.js +7 -155
  243. package/dist/plugins/memory-engine-vector-util.js +0 -10
  244. package/dist/plugins/memory-key-guards.js +0 -34
  245. package/dist/plugins/memory-origin-law.js +0 -187
  246. package/dist/plugins/memory-resume-anchor-store.js +0 -17
  247. package/dist/plugins/memory-run-store.js +12 -89
  248. package/dist/plugins/memory-session-policy-store.js +0 -17
  249. package/dist/plugins/memory-sync-store-pg.js +4 -49
  250. package/dist/plugins/memory-sync-store-tidb.js +3 -35
  251. package/dist/plugins/outcome-ledger-sql.js +3 -97
  252. package/dist/plugins/permission-rule-store-file.js +5 -133
  253. package/dist/plugins/permission-rule-store-sql.d.ts +3 -0
  254. package/dist/plugins/permission-rule-store-sql.js +50 -307
  255. package/dist/plugins/pg-cost-quota.js +0 -7
  256. package/dist/plugins/pg-pool.js +0 -92
  257. package/dist/plugins/pg-rate-limiter.js +2 -13
  258. package/dist/plugins/pg-safe-json.js +4 -40
  259. package/dist/plugins/pg-session-storage.js +25 -189
  260. package/dist/plugins/posix-shell-fs.js +1 -31
  261. package/dist/plugins/remote-env-adb.js +12 -101
  262. package/dist/plugins/remote-env-e2b.js +40 -371
  263. package/dist/plugins/remote-env-file-error.js +0 -37
  264. package/dist/plugins/remote-env-host.js +63 -483
  265. package/dist/plugins/remote-env-k8s.js +32 -326
  266. package/dist/plugins/remote-env-local-docker.js +24 -157
  267. package/dist/plugins/remote-env-ssh.js +20 -128
  268. package/dist/plugins/remote-scratchpad.js +2 -32
  269. package/dist/plugins/remote-shell.js +1 -32
  270. package/dist/plugins/resume-anchor-store-sql.js +0 -11
  271. package/dist/plugins/retention-lane-store-sql.js +0 -108
  272. package/dist/plugins/retention-store-sql.js +4 -383
  273. package/dist/plugins/roster-store-sql.js +0 -55
  274. package/dist/plugins/run-store-sql.js +14 -251
  275. package/dist/plugins/s3-presign.js +2 -49
  276. package/dist/plugins/scheduler-support.js +3 -80
  277. package/dist/plugins/send-file-ledger.js +4 -53
  278. package/dist/plugins/send-user-file.js +4 -94
  279. package/dist/plugins/session-placement.js +1 -89
  280. package/dist/plugins/session-policy-store-sql.js +3 -81
  281. package/dist/plugins/session-store.js +0 -57
  282. package/dist/plugins/shared-memory-store-sql.js +4 -167
  283. package/dist/plugins/sql-driver.js +0 -17
  284. package/dist/plugins/sql-errors.js +0 -7
  285. package/dist/plugins/sql-escape.js +0 -8
  286. package/dist/plugins/sql-row-helpers.js +0 -25
  287. package/dist/plugins/store-backend.js +42 -222
  288. package/dist/plugins/store-contracts.js +2 -46
  289. package/dist/plugins/task-attachment-store.js +3 -49
  290. package/dist/plugins/task-list-store-sql.js +0 -79
  291. package/dist/plugins/tidb-cost-quota.js +1 -4
  292. package/dist/plugins/tidb-pool.js +1 -207
  293. package/dist/plugins/tidb-rate-limiter.js +3 -9
  294. package/dist/plugins/tidb-session-storage.js +4 -70
  295. package/dist/plugins/tidb-session-store.js +19 -341
  296. package/dist/plugins/tool-result-store-sql.js +4 -179
  297. package/dist/plugins/usage-window-store-sql.js +0 -8
  298. package/dist/plugins/web-search.js +10 -132
  299. package/dist/plugins/workflow-journal-store-sql.js +2 -58
  300. package/dist/plugins/workflow-run-store-sql.js +6 -91
  301. package/dist/plugins/worktree-isolation.js +6 -126
  302. package/dist/plugins/write-behind-counter.js +16 -75
  303. package/dist/principal-jwt.js +5 -60
  304. package/dist/project-memory.js +15 -146
  305. package/dist/prompts-domain-validate.js +1 -51
  306. package/dist/question.js +2 -111
  307. package/dist/resource-suspend.js +0 -18
  308. package/dist/router/route-orchestration.js +0 -77
  309. package/dist/rules-consent.d.ts +44 -5
  310. package/dist/rules-consent.js +75 -215
  311. package/dist/run-local.js +14 -381
  312. package/dist/runs.js +29 -617
  313. package/dist/runtime-caps-resolver.js +6 -132
  314. package/dist/runtime-governance.js +1 -232
  315. package/dist/sandbox-pkg-source.js +0 -37
  316. package/dist/sealed-key.js +3 -68
  317. package/dist/security.js +6 -291
  318. package/dist/session-leaf-bus.js +0 -32
  319. package/dist/session-sync-content.js +1 -69
  320. package/dist/session-sync-kernel.js +3 -60
  321. package/dist/session-sync.js +3 -66
  322. package/dist/session-titler.js +7 -41
  323. package/dist/session-watch.js +9 -73
  324. package/dist/shared-memory-scope-authorizer.js +0 -18
  325. package/dist/sighup-idle.js +1 -12
  326. package/dist/spec-fields.js +4 -143
  327. package/dist/store-live-probe.js +3 -49
  328. package/dist/task-a2a.js +1 -126
  329. package/dist/task-cwd.js +1 -103
  330. package/dist/task-mcp.js +1 -89
  331. package/dist/task-settings.js +10 -300
  332. package/dist/task-workflow.js +8 -75
  333. package/dist/tool-approval.d.ts +28 -1
  334. package/dist/tool-approval.js +70 -1430
  335. package/dist/trace/artifacts.js +5 -20
  336. package/dist/trace/engine-notice-wire.js +6 -138
  337. package/dist/trace/ledger-sink.js +7 -96
  338. package/dist/trace/project.js +4 -448
  339. package/dist/trace/redact.js +9 -77
  340. package/dist/turn-activity.js +1 -27
  341. package/dist/usage-analytics.js +4 -34
  342. package/dist/wall-clock-jump-guard.js +1 -68
  343. package/package.json +3 -3
  344. package/dist/capabilities/scenario-alias.d.ts +0 -27
  345. package/dist/capabilities/scenario-alias.js +0 -61
@@ -1,34 +1,8 @@
1
- /**
2
- * v2 MVP M4 — the leader orchestrator (design/50 §3, V2-MVP-PLAN.md). Composes the proven pieces into one
3
- * callable API: Planner → fanOut (M1) → diff-out (M2) → merge(b) (M3) → Coordinator push.
4
- *
5
- * Worker-env model (design/68 §8 F2(a)): preferred = FACTORY env (runner-owned lifecycle; durable suspend only
6
- * yields a complete ownedEnv on this path) + diff-out via the worker's mandatory SELF-UPLOAD last step
7
- * (`fetchDiff`, diffup.ts). Compat = the original static env the leader owns: provision up-front, pull each
8
- * branch's diff OUT of the still-alive env (pullDiff), destroy every env in a `finally`.
9
- * fanOut's runner just routes a sessionId to that worker's env-bound runner. This is the leaderless-leader
10
- * pattern (service[19]): authority = owning the integration+verification point + disjoint file-ownership.
11
- *
12
- * Deps injected → mock-tested; the real-infra wiring (E2B envs + deepseek runners + git push) lives in the
13
- * joint dogfood, which this generalizes.
14
- */
15
1
  import { fanOut, isDurablePauseStatus } from "./fanout.js";
16
2
  import { pullDiff } from "./diffout.js";
17
3
  import { mergeBranches } from "./merge.js";
18
4
  import { Runner, runWithVerification, resumeWithVerification, delimitUntrusted } from "@sema-agent/core";
19
- /** design/74 Slice 6: a belt-and-suspenders bound on the leader's resource auto-resume loop. The PRIMARY bound
20
- * is the resource ledger (core fails an exhausted resume fast) + core's per-task suspend-loop cap; this only
21
- * guards against an unforeseen non-progressing loop. Generous so a legitimate many-slice budget never hits it. */
22
5
  const MAX_RESUME_SLICES = 200;
23
- /** design/74 Slice 6: drive a worker's resource-suspend auto-resume to a terminal (or bound) state. Exported
24
- * for unit tests. Loops `resume(token, slice)` WHILE the result carries a `resource_limit` checkpoint gate —
25
- * NOT by status (the verify wrapper may surface a suspend as failed-with-token). Exits on: a terminal result
26
- * (completed/failed → no gate), an exhausted budget (core fails the resume fast → no token/gate), a
27
- * human-approval gate (kind ≠ "resource_limit" — never auto-resumed), `signal.aborted`, or `maxSlices` (a
28
- * belt; core's ledger fail-fast + per-task suspend-loop cap are the primary bounds). Returns the final result. */
29
- // Generic over the result type so BOTH leader worker paths reuse it: the verify path (VerificationResult via
30
- // runWithVerification/resumeWithVerification) AND the non-verify path (plain TaskResult via runTaskStream/
31
- // runner.resume). The loop only reads `checkpointToken` / `checkpointGate`, both on TaskResult.
32
6
  export async function autoResumeOnResource(initial, signal, resume, maxSlices = MAX_RESUME_SLICES) {
33
7
  let vr = initial;
34
8
  for (let slice = 0; vr.checkpointToken !== undefined && vr.checkpointGate?.kind === "resource_limit" && !signal.aborted && slice < maxSlices; slice++) {
@@ -36,10 +10,6 @@ export async function autoResumeOnResource(initial, signal, resume, maxSlices =
36
10
  }
37
11
  return vr;
38
12
  }
39
- /**
40
- * Run a task to an integrated, pushed result. Never throws — failures (plan, provision, fan-out, merge) are
41
- * captured in LeaderResult. ALWAYS destroys every provisioned worker env (leader owns the lifecycle).
42
- */
43
13
  export async function runLeaderTask(task, durableBaseSha, deps) {
44
14
  let subs;
45
15
  try {
@@ -50,12 +20,10 @@ export async function runLeaderTask(task, durableBaseSha, deps) {
50
20
  }
51
21
  if (subs.length === 0)
52
22
  return { ok: false, reports: [], cancelled: false, error: "planner produced no sub-tasks" };
53
- // difficulty router (search [43]): a 1-subtask plan IS the single route — run through the same N=1 pipeline.
54
23
  const route = subs.length === 1 ? "single" : "fanout";
55
24
  const workers = [];
56
- let finalReports = []; // visible to `finally` — drives the suspended-worker destroy exemption
25
+ let finalReports = [];
57
26
  try {
58
- // Provision every worker env up-front (leader-owned; each outlives its task for diff-out).
59
27
  for (const sub of subs) {
60
28
  try {
61
29
  workers.push(await deps.provisionWorker(sub));
@@ -66,22 +34,15 @@ export async function runLeaderTask(task, durableBaseSha, deps) {
66
34
  }
67
35
  const byId = new Map(workers.map((w) => [w.workerId, w]));
68
36
  const bySession = new Map(workers.map((w) => [w.sessionId, w]));
69
- // fan-out: route each sessionId to its own env-bound runner (M1).
70
37
  const runner = {
71
38
  runTaskStream: (spec) => bySession.get(spec.sessionId).runner.runTaskStream(spec),
72
39
  };
73
40
  const toSubtask = (w) => ({
74
41
  workerId: w.workerId, sessionId: w.sessionId, branch: w.branch, baseSha: w.baseSha, spec: w.spec,
75
42
  });
76
- // cancelOnSuspend = the unconditional C4 reaction: a suspended settle preempts the remaining fan-out.
77
43
  const foBase = { runner, timeoutMs: deps.timeoutMs, maxConcurrency: deps.maxConcurrency, cancelOnSuspend: true, ...(deps.logger ? { logger: deps.logger } : {}) };
78
44
  const runWave = async (sts) => {
79
45
  if (!deps.verifyWorker) {
80
- // design/74 Slice 6 (live-test bug): the NON-verify path ALSO needs the resource-suspend auto-resume.
81
- // The auto-resume previously lived only in the verify path's runWorker, so a leader with NO verifier
82
- // (e.g. the drill) ran via fanOut's plain runTaskStream — a resource-suspended worker was never resumed,
83
- // just surfaced "suspended". Give this path its own runWorker: run, then auto-resume across slices
84
- // (plain runner.resume, no verify), then map + WARN-log any non-completed worker (no silent failures).
85
46
  return fanOut(sts, {
86
47
  ...foBase,
87
48
  runWorker: async (st, signal) => {
@@ -97,22 +58,13 @@ export async function runLeaderTask(task, durableBaseSha, deps) {
97
58
  deps.logger?.warn?.("worker_failed", {
98
59
  workerId: w.workerId, status: r.status, turns: r.stats?.turns ?? 0,
99
60
  ...(r.errorCode ? { errorCode: r.errorCode } : {}),
100
- // A `blocked` worker carries its reason in `blockedReason`, NOT `errorMessage` — surface it too,
101
- // else a report_blocked is a silent "worker blocked" with no WHY (clay no-silent-failures).
102
61
  ...(r.errorMessage ? { error: r.errorMessage.slice(0, 200) } : {}),
103
62
  ...(r.blockedReason ? { blockedReason: r.blockedReason.slice(0, 200) } : {}),
104
63
  });
105
64
  }
106
65
  return {
107
66
  ...base,
108
- // Map the TaskResult status directly (like fanOut's own mapStatus) a durably PARKED result that
109
- // the auto-resume left unresumed (a non-resource gate, e.g. human-approval or a plan review) stays
110
- // `suspended` so the leader's C4 surface/cancel reaction still fires; only completed/timeout pass
111
- // through, else failed. 🔴 park 的判据取自 `fanout.ts` 的 `isDurablePauseStatus`(扫描P2):
112
- // 原地手抄的 `=== "suspended"` 漏掉 `needs_review`(plan_review / dry-run 门),会把一条还
113
- // 拿着 checkpoint、环境还暂停着的 worker 报成 failed —— 与本文件带验证器那条腿早就修过的
114
- // 是同一个缺陷(round-3 review BLOCKER),当时只修了那半场。
115
- status: r.status === "completed" ? "completed" : isDurablePauseStatus(r.status) ? "suspended" : "failed", // core 5.8.0:TaskResult 无 "timeout"(内部词表的 timeout 仍由 fanout 墙钟臂生产)
67
+ status: r.status === "completed" ? "completed" : isDurablePauseStatus(r.status) ? "suspended" : "failed",
116
68
  stats: r.stats,
117
69
  ...(r.status === "completed" ? {} : { error: r.errorMessage ?? r.blockedReason ?? `worker ${r.status}` }),
118
70
  ...(r.checkpointGate ? { checkpointGate: r.checkpointGate } : {}),
@@ -121,13 +73,6 @@ export async function runLeaderTask(task, durableBaseSha, deps) {
121
73
  },
122
74
  });
123
75
  }
124
- // design/54 §4 L3a (core 1.72 NATIVE): each worker = runWithVerification — run the module, then an
125
- // ADVERSARIAL DECORRELATED verifier (a different model) tries to BREAK it (reads diff/workspace, runs
126
- // probes), with a bounded fix-loop = native per-module sub-gate + repair. Replaces the hand-rolled
127
- // moduleGate. verdict FAIL after maxRounds → status:"failed" (merge quarantines). Run cost incl verify+fix.
128
- // Routed THROUGH fanOut (not an unbounded Promise.all — council): reuses the semaphore (maxConcurrency), the
129
- // overall timeout→cancelAll, and the shared abort signal — passed into the spec so a hung/cancelled verify
130
- // actually aborts (so the overall-timeout's `await work` can unblock).
131
76
  const verify = deps.verifyWorker;
132
77
  return fanOut(sts, {
133
78
  ...foBase,
@@ -136,27 +81,14 @@ export async function runLeaderTask(task, durableBaseSha, deps) {
136
81
  const base = { workerId: w.workerId, sessionId: w.sessionId, branch: w.branch, baseSha: w.baseSha };
137
82
  const cfg = w.verifierTools ? { ...verify, verifierTools: w.verifierTools } : verify;
138
83
  let vr = await runWithVerification(w.runner, { ...w.spec, sessionId: w.sessionId, signal }, cfg);
139
- // design/74 Slice 6: auto-resume a RESOURCE-suspended worker (a per-slice budget/turn/walltime overrun)
140
- // until terminal — the leader spends the worker's TOTAL budget across slices instead of failing at the
141
- // first slice. Loop + exit conditions live in `autoResumeOnResource` (unit-tested).
142
84
  vr = await autoResumeOnResource(vr, signal, async (token, slice) => {
143
85
  deps.logger?.info?.("worker_resource_resume", { workerId: w.workerId, slice });
144
86
  return resumeWithVerification(w.runner, token, { gate: "resource_limit", decision: "continue" }, { ...w.spec, sessionId: w.sessionId, signal }, w.spec.objective ?? "", cfg);
145
87
  });
146
88
  deps.logger?.info?.("verify_outcome", { workerId: w.workerId, status: vr.status, verdict: vr.verification.verdict, rounds: vr.verification.rounds, finding: (vr.verification.findings[0] ?? "").slice(0, 220) });
147
89
  const pass = vr.status === "completed" && vr.verification.verdict === "PASS";
148
- // round-3 review BLOCKER: a DURABLE suspend — resource OR human-approval — comes back from
149
- // runWithVerification as failed-WITH-token (core mapNestedSuspend), so gating `stillSuspended` on
150
- // `resource_limit` ONLY mis-routed a human-approval suspend to `failed` → the C4 surface/cancel
151
- // reaction never fired and its paused env could be reaped. Any worker still carrying a checkpoint
152
- // token is suspended (resumable); a genuine failure has no token. The resource auto-resume already
153
- // ran above, so a still-tokened resource worker is one the leader couldn't fully resume (deadline).
154
90
  const stillSuspended = vr.checkpointToken !== undefined;
155
91
  const reportStatus = pass ? "completed" : stillSuspended ? "suspended" : "failed";
156
- // No-silent-failures (clay 2026-06-15): WARN on any non-completed REPORT status — incl. a verify-FAIL
157
- // on an impl that COMPLETED (vr.status "completed" but reportStatus "failed"), which the old
158
- // `vr.status !== completed` guard missed. Carry the impl's reason: errorCode, errorMessage, AND
159
- // blockedReason (a report_blocked worker's reason lives there, not in errorMessage), plus the verdict.
160
92
  if (reportStatus !== "completed") {
161
93
  deps.logger?.warn?.("worker_failed", {
162
94
  workerId: w.workerId, status: reportStatus, turns: vr.stats?.turns ?? 0, verdict: vr.verification.verdict,
@@ -168,8 +100,6 @@ export async function runLeaderTask(task, durableBaseSha, deps) {
168
100
  return {
169
101
  ...base, status: reportStatus, stats: vr.stats,
170
102
  moduleJudge: vr.verification.verdict === "PASS" ? "pass" : "fail", repairs: Math.max(0, vr.verification.rounds - 1),
171
- // A verify FAIL/PARTIAL (the impl completed but the verifier rejected it) reports the verdict;
172
- // an `unverified` (the impl didn't complete — suspend/block/error) reports the impl's own reason.
173
103
  ...(pass
174
104
  ? {}
175
105
  : {
@@ -183,18 +113,8 @@ export async function runLeaderTask(task, durableBaseSha, deps) {
183
113
  },
184
114
  });
185
115
  };
186
- // LEADER-REPAIRLOOP-INTEGRATION §10.6 — the single-agent repair loop's terminal + held candidate, lifted onto
187
- // every terminal LeaderResult. Set ONLY when `deps.repairLoop` ran on a single-agent path (LEADER_REPAIR_LOOP
188
- // on); undefined otherwise → the result is byte-identical to today. The `repairTerminal` ALSO rides on the
189
- // solo worker's report (so the merge push chokepoint, merge.ts, holds an unconfirmed candidate by construction).
190
116
  let repairTerminal;
191
117
  let candidatePatch;
192
- // Route a SINGLE provisioned worker through the opt-in `runRepairLoop` (when `deps.repairLoop` is set) instead
193
- // of the plain fan-out map: GENERATE == one runner.runTask, GRADED by the isolated grader, capped at
194
- // `candidate_only`. Attaches `repairTerminal` to the report (the chokepoint reads it) + lifts the held
195
- // candidate. Falls back to `runWave([toSubtask(solo)])` (today's path) when the repair loop is not attached
196
- // OR the per-worker resolve throws (never break the leader). Returns the same shape as `runWave` so the
197
- // call-sites are a drop-in. §10.6: this covers BOTH single-agent paths (collapse + N=1) through one helper.
198
118
  const runSolo = async (solo, sub) => {
199
119
  if (!deps.repairLoop)
200
120
  return runWave([toSubtask(solo)]);
@@ -207,14 +127,10 @@ export async function runLeaderTask(task, durableBaseSha, deps) {
207
127
  return { reports: [rr.report], cancelled: false };
208
128
  }
209
129
  catch (e) {
210
- // The repair wire already maps a thrown engine error to a failed report; a throw HERE is a wire/resolve
211
- // failure — degrade to the plain wave so the solo still runs (no silent break; warn the WHY).
212
130
  deps.logger?.warn?.("repair_loop_dispatch_failed", { workerId: solo.workerId, err: e instanceof Error ? e.message : String(e) });
213
131
  return runWave([toSubtask(solo)]);
214
132
  }
215
133
  };
216
- // N=1 (route==="single"): the lone worker IS the single-agent path → route it through `runSolo`. With
217
- // `deps.repairLoop` unset, `runSolo` delegates straight to `runWave` → byte-identical to today's `runWave(...)`.
218
134
  const fo1 = route === "single" && workers.length === 1
219
135
  ? await runSolo(workers[0], subs[0])
220
136
  : await runWave(workers.map(toSubtask));
@@ -222,17 +138,11 @@ export async function runLeaderTask(task, durableBaseSha, deps) {
222
138
  let cancelled = fo1.cancelled;
223
139
  finalReports = reports;
224
140
  const workerRepoDir = deps.workerRepoDir ?? deps.repoDir;
225
- // One diff source per worker: self-uploaded (factory env, F2(a)) preferred; else pull from the static env.
226
141
  const workerDiff = (w) => w.fetchDiff
227
142
  ? w.fetchDiff()
228
143
  : w.diffEnv
229
144
  ? pullDiff(w.diffEnv, w.baseSha, { ref: w.branch, ...(workerRepoDir ? { cwd: workerRepoDir } : {}) })
230
145
  : Promise.resolve({ ok: false, error: `worker ${w.workerId} has neither fetchDiff nor diffEnv` });
231
- // design/74 Slice 6 (task#16, round-3 review BLOCKER): salvage the diff (belt auto-commit + diff-out) of
232
- // workers that did NOT merge (failed / still-suspended) so their progress isn't silently dropped. Must run
233
- // on EVERY terminal LeaderResult path — the main merge AND the early returns (surfaceSuspended, collapse) —
234
- // not just the merge, else a suspended-preempted or collapsed run loses the non-completed workers' work.
235
- // Best-effort: a fetch failure or an empty/paused-env diff is skipped; it NEVER blocks the result.
236
146
  const salvageNonCompleted = async (rs) => {
237
147
  const out = [];
238
148
  await Promise.all(rs
@@ -247,19 +157,10 @@ export async function runLeaderTask(task, durableBaseSha, deps) {
247
157
  out.push({ workerId: r.workerId, sessionId: r.sessionId, patch: d.patch });
248
158
  }
249
159
  catch {
250
- /* best-effort — a salvage fetch must not break the leader result */
251
160
  }
252
161
  }));
253
162
  return out;
254
163
  };
255
- // design/52 L3a — per-module sub-gate + bounded repair (between fan-out and merge). Judge each completed
256
- // worker's module diff vs its sub-objective; on fail, re-run that ONE worker with feedback (bounded), then
257
- // re-judge. Still-failing modules → status:"failed" so merge quarantines them. Reaps per-module escapes the
258
- // end-integration test can't see + restores cheap-team correctness via localized repair (search [38]).
259
- // design/158 A13: lifted from an inline `if (deps.moduleGate) { await Promise.all(...) }` block into a named
260
- // stage function (same closure scope/capture/call-site as before — behavior identical), alongside the other
261
- // already-named internal stages of this function (runWave/runSolo/salvageNonCompleted/surfaceSuspended/
262
- // collapseToSingle below).
263
164
  const runModuleGate = async () => {
264
165
  if (!deps.moduleGate)
265
166
  return;
@@ -271,7 +172,7 @@ export async function runLeaderTask(task, durableBaseSha, deps) {
271
172
  for (let attempt = 0; attempt <= maxR; attempt++) {
272
173
  const d = await workerDiff(w);
273
174
  if (!d.ok)
274
- return; // diff-out problem → leave it for merge to quarantine
175
+ return;
275
176
  const verdict = await deps.moduleGate.judge(sub, d.patch);
276
177
  r.moduleJudge = verdict.ok ? "pass" : "fail";
277
178
  r.repairs = attempt;
@@ -286,7 +187,7 @@ export async function runLeaderTask(task, durableBaseSha, deps) {
286
187
  try {
287
188
  const rr = await w.runner.runTaskStream({ ...sub.spec, objective: deps.moduleGate.repairObjective(sub, verdict.reason ?? ""), sessionId: `${w.sessionId}-r${attempt + 1}` }).result();
288
189
  if (r.stats && rr?.stats)
289
- r.stats = { ...r.stats, tokens: (r.stats.tokens ?? 0) + (rr.stats.tokens ?? 0), costMicroUsd: (r.stats.costMicroUsd ?? 0) + (rr.stats.costMicroUsd ?? 0) }; // fold repair cost into the worker's report
190
+ r.stats = { ...r.stats, tokens: (r.stats.tokens ?? 0) + (rr.stats.tokens ?? 0), costMicroUsd: (r.stats.costMicroUsd ?? 0) + (rr.stats.costMicroUsd ?? 0) };
290
191
  }
291
192
  catch (e) {
292
193
  r.status = "failed";
@@ -297,7 +198,6 @@ export async function runLeaderTask(task, durableBaseSha, deps) {
297
198
  }));
298
199
  };
299
200
  await runModuleGate();
300
- // merge(b) deps: diff-out per worker (self-upload or static pull), integrate, Coordinator push.
301
201
  const mergeDeps = {
302
202
  pullWorkerDiff: async (r) => workerDiff(byId.get(r.workerId)),
303
203
  provisionIntegrationSandbox: deps.provisionIntegrationSandbox,
@@ -305,23 +205,12 @@ export async function runLeaderTask(task, durableBaseSha, deps) {
305
205
  testCmd: deps.testCmd,
306
206
  ...(deps.measureCmd ? { measureCmd: deps.measureCmd } : {}),
307
207
  ...(deps.repoDir ? { repoDir: deps.repoDir } : {}),
308
- // §10.3/§10.4 — the push-gate extras (OFF by default; set by the wire from LEADER_MEASURE_GATES).
309
208
  ...(deps.mergeGates?.measureGates ? { measureGates: true } : {}),
310
209
  ...(deps.mergeGates?.strongOracleSeeded ? { strongOracleSeeded: true } : {}),
311
210
  ...(deps.mergeGates?.measureDrivesRepair ? { measureDrivesRepair: deps.mergeGates.measureDrivesRepair } : {}),
312
211
  ...(deps.logger ? { logger: deps.logger } : {}),
313
212
  };
314
- // ── replan-lite (design/68 §6): deterministic reaction table, arbitrated in C6 priority order ──
315
- // Spec priority: suspended > budget > merge-conflict > verify-fail > worker-failed; only the highest-
316
- // priority reaction runs, and the check order below IS the arbitration (each returns; later checks never
317
- // see its trigger). DETECTION order differs for merge-conflict: it is only observable AFTER mergeBranches
318
- // runs, i.e. after the verify/failed reactions — which is spec-equivalent, because all three reactions are
319
- // the same collapse-to-single (the ordering only affects the recorded `trigger` label, and a verify/failed
320
- // collapse removes the conflicting diff before a merge could ever surface its conflict).
321
213
  let redispatched;
322
- // C4 (unconditional): a suspended worker preempted the wave (fan-out cancelOnSuspend already cancelled the
323
- // rest). NO merge, NO redispatch — surface failed + the resume token; its checkpoint + paused env are intact
324
- // (the finally below exempts it from the static-env destroy; factory envs are exempt by construction).
325
214
  const surfaceSuspended = async (rs, wave) => {
326
215
  const sus = wave.filter((r) => r.status === "suspended");
327
216
  if (sus.length === 0)
@@ -336,16 +225,9 @@ export async function runLeaderTask(task, durableBaseSha, deps) {
336
225
  ...(salvaged.length > 0 ? { salvaged } : {}),
337
226
  };
338
227
  };
339
- // Collapse-to-single (at most once): one fresh worker redoes the whole task on a clean branch. The discarded
340
- // workers' envs die via the normal lifecycle (factory finish / the static finally) — E1. The full env-layer
341
- // "adopt N half-done workspaces" seam (design/68 §8) stays future; INSTEAD (search 2026-06-16) we REUSE the
342
- // fan-out work the lightweight, zero-new-seam way — hand the collapse-solo the diffs the parallel workers
343
- // already produced as reference so it adapts their correct output (copy the non-overlapping modules, resolve
344
- // only the overlap) rather than re-deriving the whole task blind.
345
228
  const collapseToSingle = async (trigger, reason, priorMerge) => {
346
229
  deps.logger?.warn?.("replan_collapse_to_single", { trigger, reason: reason.slice(0, 300) });
347
230
  const replan = { trigger, collapsed: reason.slice(0, 300), ...(redispatched ? { redispatched } : {}) };
348
- // Best-effort gather of the fan-out workers' diffs (same source as salvage); an empty/failed diff is omitted.
349
231
  const priorDiffs = [];
350
232
  await Promise.all(reports.map(async (r) => {
351
233
  const w = byId.get(r.workerId);
@@ -357,7 +239,6 @@ export async function runLeaderTask(task, durableBaseSha, deps) {
357
239
  priorDiffs.push({ workerId: r.workerId, status: r.status, patch: d.patch });
358
240
  }
359
241
  catch {
360
- /* best-effort — a salvage fetch must not break the collapse path */
361
242
  }
362
243
  }));
363
244
  if (priorDiffs.length > 0)
@@ -369,11 +250,8 @@ export async function runLeaderTask(task, durableBaseSha, deps) {
369
250
  ``,
370
251
  `--- PRIOR PARALLEL WORK — REUSE IT, do not re-derive from scratch ---`,
371
252
  `${priorDiffs.length} worker(s) attempted this task in parallel; their changes could NOT be integrated cleanly (${reason.slice(0, 150)}). Each worker's diff over the base is below. COPY the correct, non-overlapping parts verbatim and produce ONE coherent change for the WHOLE task, resolving only the overlaps. Treat these as a strong starting point — fix anything wrong, but do not throw the work away.`,
372
- // BL-30/BL-32 — each prior worker's diff is untrusted worker-authored code; delimit it so an injected
373
- // instruction in a diff comment can't steer the collapse-solo agent (design/53 trifecta).
374
253
  ...priorDiffs.map((d) => delimitUntrusted(`PRIOR WORKER '${d.workerId}' (${d.status}) DIFF (untrusted)`, d.patch.slice(0, 8000))),
375
254
  ].join("\n");
376
- // The synthetic collapse-solo sub-task (kept as a value so the §10.6 repair-loop dispatch can pass it).
377
255
  const soloSub = { workerId: "collapse-solo", branch: "worker/collapse-solo", files: ["."], spec: { objective: soloObjective } };
378
256
  let solo;
379
257
  try {
@@ -386,18 +264,12 @@ export async function runLeaderTask(task, durableBaseSha, deps) {
386
264
  workers.push(solo);
387
265
  byId.set(solo.workerId, solo);
388
266
  bySession.set(solo.sessionId, solo);
389
- // §10.6: collapse-to-single IS a single-agent path → route through `runSolo` (the opt-in repair loop when
390
- // `deps.repairLoop` is set; the plain wave otherwise). `runSolo` sets the outer `repairTerminal`/
391
- // `candidatePatch`, which are lifted onto this return so the chokepoint-held candidate reaches the caller.
392
267
  const fo = await runSolo(solo, soloSub);
393
268
  finalReports = [...reports, ...fo.reports];
394
269
  const sus = await surfaceSuspended(finalReports, fo.reports);
395
270
  if (sus)
396
271
  return { ...sus, replan: { ...replan, trigger: "suspended" }, ...(repairTerminal ? { repairTerminal } : {}), ...(candidatePatch ? { candidatePatch } : {}) };
397
272
  const mRaw = await mergeBranches(fo.reports, durableBaseSha, mergeDeps);
398
- // wq64gmm5e: carry the FAILED prior merge's resolver spend across the collapse so captureTeamC1 sees the
399
- // tokens it already burned (a --3way conflict that triggered collapse may have run a conflictResolver). Fold
400
- // it into the collapse-merge's conflictResolverCostUsd; default path (no prior cost) is byte-identical.
401
273
  const priorCost = priorMerge?.conflictResolverCostUsd ?? 0;
402
274
  const m = priorCost > 0 ? { ...mRaw, conflictResolverCostUsd: (mRaw.conflictResolverCostUsd ?? 0) + priorCost } : mRaw;
403
275
  const salvaged = await salvageNonCompleted(finalReports);
@@ -415,22 +287,16 @@ export async function runLeaderTask(task, durableBaseSha, deps) {
415
287
  const sus1 = await surfaceSuspended(reports, reports);
416
288
  if (sus1)
417
289
  return sus1;
418
- // C5 budget: fan-out overspent → stop fanning, single continues (no expire of anything — C7 falls out of
419
- // the suspended check above winning first).
420
290
  if (deps.replan?.budgetUsd !== undefined) {
421
291
  const spent = reports.reduce((s, r) => s + (r.stats?.costMicroUsd ?? 0) / 1e6, 0);
422
292
  if (spent > deps.replan.budgetUsd) {
423
293
  return collapseToSingle("budget", `fan-out spent $${spent.toFixed(4)} > budget $${deps.replan.budgetUsd}`);
424
294
  }
425
295
  }
426
- // C2 verify FAIL: the bounded repair was already spent INSIDE the verify loop (runWithVerification rounds /
427
- // moduleGate repairs) — a worker still failing its module gate collapses, it is not redispatched.
428
296
  if (deps.replan && reports.some((r) => r.moduleJudge === "fail")) {
429
297
  const failed = reports.filter((r) => r.moduleJudge === "fail").map((r) => r.workerId);
430
298
  return collapseToSingle("verify-fail", `module verification failed after bounded repair: ${failed.join(", ")}`);
431
299
  }
432
- // C1 worker failed: redispatch ×1, same spec, FRESH env + clean base (provisionWorker provisions anew; the
433
- // crashed worker's half-written workspace is never reused) → still failed = collapse-remainder-to-single.
434
300
  if (deps.replan) {
435
301
  const failedIds = reports.filter((r) => r.status === "failed").map((r) => r.workerId);
436
302
  if (failedIds.length > 0) {
@@ -441,7 +307,7 @@ export async function runLeaderTask(task, durableBaseSha, deps) {
441
307
  for (const id of failedIds) {
442
308
  const w = await deps.provisionWorker(subById.get(id));
443
309
  fresh.push(w);
444
- workers.push(w); // register IMMEDIATELY — a later provision throw must not orphan this env (council)
310
+ workers.push(w);
445
311
  }
446
312
  }
447
313
  catch (e) {
@@ -465,28 +331,16 @@ export async function runLeaderTask(task, durableBaseSha, deps) {
465
331
  }
466
332
  }
467
333
  }
468
- // merge(b): integrate + Coordinator push.
469
334
  const merge = await mergeBranches(reports, durableBaseSha, mergeDeps);
470
- // C3 merge conflict: merge(b)'s sequential --3way apply IS the serial replay; a --3way failure means an
471
- // architecture-level conflict → collapse to single + RECORD (the design/52 data feed).
472
335
  if (!merge.ok && merge.phase === "apply" && deps.replan) {
473
336
  deps.logger?.warn?.("replan_merge_conflict", { reason: merge.reason, quarantine: merge.quarantine ?? [] });
474
337
  return collapseToSingle("merge-conflict", merge.reason, merge);
475
338
  }
476
- // design/74 Slice 6 (task#16): salvage the progress of workers that did NOT merge (failed / still-
477
- // suspended). The belt auto-commit + diff-out ran on env teardown, so a non-empty patch exists (in S3 for a
478
- // factory worker, or pullable from a still-alive static env, before the `finally` destroys it) — surface it
479
- // so the work isn't silently lost (the migration-run loss: a network-failed worker's commits vanished).
480
- // Best-effort: a fetch failure or an empty/paused-env diff is skipped, and it NEVER blocks the merge result.
481
339
  const salvaged = await salvageNonCompleted(reports);
482
340
  return {
483
341
  ok: merge.ok, reports, merge, cancelled, route,
484
342
  ...(redispatched ? { replan: { trigger: "worker-failed", redispatched } } : {}),
485
343
  ...(salvaged.length > 0 ? { salvaged } : {}),
486
- // §10.6 — lift the single-agent repair loop's terminal (set by `runSolo` on the N=1 path) + the merge
487
- // chokepoint's terminal onto the result so the HTTP status map (`endpoint.ts:75`) surfaces `needs_human`.
488
- // `repairTerminal` (the loop's own) takes precedence over `merge.terminal` (the chokepoint's). Both undefined
489
- // on the default path → byte-identical. The held `candidatePatch` (delimited at the wire) rides alongside.
490
344
  ...(repairTerminal ?? merge.terminal ? { repairTerminal: repairTerminal ?? merge.terminal } : {}),
491
345
  ...(candidatePatch
492
346
  ? { candidatePatch }
@@ -496,11 +350,6 @@ export async function runLeaderTask(task, durableBaseSha, deps) {
496
350
  };
497
351
  }
498
352
  finally {
499
- // Static envs: the leader owns the lifecycle — destroy on EVERY path, EXCEPT a suspended worker's env
500
- // (C4: destroying it kills the resume; its checkpoint + paused env belong to the single-agent resume path,
501
- // orphan protection = the existing deadline reap). Factory envs carry no `destroy` (runner-owned).
502
- // Keyed by sessionId, not workerId: a redispatched worker shares its predecessor's workerId — exempting by
503
- // workerId would also spare the ORIGINAL failed worker's env (leak, council finding).
504
353
  const suspendedSessions = new Set(finalReports.filter((r) => r.status === "suspended").map((r) => r.sessionId));
505
354
  await Promise.allSettled(workers.filter((w) => w.destroy && !suspendedSessions.has(w.sessionId)).map((w) => w.destroy()));
506
355
  }