harness-engineering-agent 0.1.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 (327) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +191 -0
  3. package/dist/config/config-defaults.d.ts +32 -0
  4. package/dist/config/config-defaults.d.ts.map +1 -0
  5. package/dist/config/config-defaults.js +32 -0
  6. package/dist/config/config-defaults.js.map +1 -0
  7. package/dist/config/config-errors.d.ts +7 -0
  8. package/dist/config/config-errors.d.ts.map +1 -0
  9. package/dist/config/config-errors.js +11 -0
  10. package/dist/config/config-errors.js.map +1 -0
  11. package/dist/config/config-file.d.ts +3 -0
  12. package/dist/config/config-file.d.ts.map +1 -0
  13. package/dist/config/config-file.js +31 -0
  14. package/dist/config/config-file.js.map +1 -0
  15. package/dist/config/config-merge.d.ts +8 -0
  16. package/dist/config/config-merge.d.ts.map +1 -0
  17. package/dist/config/config-merge.js +50 -0
  18. package/dist/config/config-merge.js.map +1 -0
  19. package/dist/config/config-schema.d.ts +135 -0
  20. package/dist/config/config-schema.d.ts.map +1 -0
  21. package/dist/config/config-schema.js +94 -0
  22. package/dist/config/config-schema.js.map +1 -0
  23. package/dist/config/config-types.d.ts +21 -0
  24. package/dist/config/config-types.d.ts.map +1 -0
  25. package/dist/config/config-types.js +2 -0
  26. package/dist/config/config-types.js.map +1 -0
  27. package/dist/config/harness-config.d.ts +6 -0
  28. package/dist/config/harness-config.d.ts.map +1 -0
  29. package/dist/config/harness-config.js +4 -0
  30. package/dist/config/harness-config.js.map +1 -0
  31. package/dist/config/index.d.ts +2 -0
  32. package/dist/config/index.d.ts.map +1 -0
  33. package/dist/config/index.js +2 -0
  34. package/dist/config/index.js.map +1 -0
  35. package/dist/config/load-harness-config.d.ts +9 -0
  36. package/dist/config/load-harness-config.d.ts.map +1 -0
  37. package/dist/config/load-harness-config.js +25 -0
  38. package/dist/config/load-harness-config.js.map +1 -0
  39. package/dist/controller/agent-output.d.ts +21 -0
  40. package/dist/controller/agent-output.d.ts.map +1 -0
  41. package/dist/controller/agent-output.js +75 -0
  42. package/dist/controller/agent-output.js.map +1 -0
  43. package/dist/controller/agent-session.d.ts +49 -0
  44. package/dist/controller/agent-session.d.ts.map +1 -0
  45. package/dist/controller/agent-session.js +220 -0
  46. package/dist/controller/agent-session.js.map +1 -0
  47. package/dist/controller/contracts.d.ts +65 -0
  48. package/dist/controller/contracts.d.ts.map +1 -0
  49. package/dist/controller/contracts.js +2 -0
  50. package/dist/controller/contracts.js.map +1 -0
  51. package/dist/controller/errors.d.ts +23 -0
  52. package/dist/controller/errors.d.ts.map +1 -0
  53. package/dist/controller/errors.js +37 -0
  54. package/dist/controller/errors.js.map +1 -0
  55. package/dist/controller/evidence-factory.d.ts +15 -0
  56. package/dist/controller/evidence-factory.d.ts.map +1 -0
  57. package/dist/controller/evidence-factory.js +38 -0
  58. package/dist/controller/evidence-factory.js.map +1 -0
  59. package/dist/controller/failure-analysis.d.ts +36 -0
  60. package/dist/controller/failure-analysis.d.ts.map +1 -0
  61. package/dist/controller/failure-analysis.js +175 -0
  62. package/dist/controller/failure-analysis.js.map +1 -0
  63. package/dist/controller/failure-factory.d.ts +85 -0
  64. package/dist/controller/failure-factory.d.ts.map +1 -0
  65. package/dist/controller/failure-factory.js +124 -0
  66. package/dist/controller/failure-factory.js.map +1 -0
  67. package/dist/controller/graph-validator.d.ts +4 -0
  68. package/dist/controller/graph-validator.d.ts.map +1 -0
  69. package/dist/controller/graph-validator.js +102 -0
  70. package/dist/controller/graph-validator.js.map +1 -0
  71. package/dist/controller/index.d.ts +15 -0
  72. package/dist/controller/index.d.ts.map +1 -0
  73. package/dist/controller/index.js +11 -0
  74. package/dist/controller/index.js.map +1 -0
  75. package/dist/controller/repair-loop.d.ts +42 -0
  76. package/dist/controller/repair-loop.d.ts.map +1 -0
  77. package/dist/controller/repair-loop.js +385 -0
  78. package/dist/controller/repair-loop.js.map +1 -0
  79. package/dist/controller/report-factory.d.ts +4 -0
  80. package/dist/controller/report-factory.d.ts.map +1 -0
  81. package/dist/controller/report-factory.js +107 -0
  82. package/dist/controller/report-factory.js.map +1 -0
  83. package/dist/controller/role-prompts.d.ts +8 -0
  84. package/dist/controller/role-prompts.d.ts.map +1 -0
  85. package/dist/controller/role-prompts.js +35 -0
  86. package/dist/controller/role-prompts.js.map +1 -0
  87. package/dist/controller/run-controller.d.ts +3 -0
  88. package/dist/controller/run-controller.d.ts.map +1 -0
  89. package/dist/controller/run-controller.js +291 -0
  90. package/dist/controller/run-controller.js.map +1 -0
  91. package/dist/controller/run-persistence.d.ts +15 -0
  92. package/dist/controller/run-persistence.d.ts.map +1 -0
  93. package/dist/controller/run-persistence.js +83 -0
  94. package/dist/controller/run-persistence.js.map +1 -0
  95. package/dist/controller/run-state.d.ts +61 -0
  96. package/dist/controller/run-state.d.ts.map +1 -0
  97. package/dist/controller/run-state.js +151 -0
  98. package/dist/controller/run-state.js.map +1 -0
  99. package/dist/controller/task-agent-output.d.ts +70 -0
  100. package/dist/controller/task-agent-output.d.ts.map +1 -0
  101. package/dist/controller/task-agent-output.js +256 -0
  102. package/dist/controller/task-agent-output.js.map +1 -0
  103. package/dist/controller/task-agent-schemas.d.ts +217 -0
  104. package/dist/controller/task-agent-schemas.d.ts.map +1 -0
  105. package/dist/controller/task-agent-schemas.js +171 -0
  106. package/dist/controller/task-agent-schemas.js.map +1 -0
  107. package/dist/controller/task-executor.d.ts +15 -0
  108. package/dist/controller/task-executor.d.ts.map +1 -0
  109. package/dist/controller/task-executor.js +751 -0
  110. package/dist/controller/task-executor.js.map +1 -0
  111. package/dist/controller/task-paths.d.ts +3 -0
  112. package/dist/controller/task-paths.d.ts.map +1 -0
  113. package/dist/controller/task-paths.js +20 -0
  114. package/dist/controller/task-paths.js.map +1 -0
  115. package/dist/controller/task-retry.d.ts +24 -0
  116. package/dist/controller/task-retry.d.ts.map +1 -0
  117. package/dist/controller/task-retry.js +42 -0
  118. package/dist/controller/task-retry.js.map +1 -0
  119. package/dist/controller/worktree-eligibility.d.ts +15 -0
  120. package/dist/controller/worktree-eligibility.d.ts.map +1 -0
  121. package/dist/controller/worktree-eligibility.js +38 -0
  122. package/dist/controller/worktree-eligibility.js.map +1 -0
  123. package/dist/controller/worktree-scheduler.d.ts +72 -0
  124. package/dist/controller/worktree-scheduler.d.ts.map +1 -0
  125. package/dist/controller/worktree-scheduler.js +142 -0
  126. package/dist/controller/worktree-scheduler.js.map +1 -0
  127. package/dist/domain/agent-result.d.ts +83 -0
  128. package/dist/domain/agent-result.d.ts.map +1 -0
  129. package/dist/domain/agent-result.js +44 -0
  130. package/dist/domain/agent-result.js.map +1 -0
  131. package/dist/domain/evidence.d.ts +97 -0
  132. package/dist/domain/evidence.d.ts.map +1 -0
  133. package/dist/domain/evidence.js +85 -0
  134. package/dist/domain/evidence.js.map +1 -0
  135. package/dist/domain/failure.d.ts +121 -0
  136. package/dist/domain/failure.d.ts.map +1 -0
  137. package/dist/domain/failure.js +72 -0
  138. package/dist/domain/failure.js.map +1 -0
  139. package/dist/domain/index.d.ts +19 -0
  140. package/dist/domain/index.d.ts.map +1 -0
  141. package/dist/domain/index.js +10 -0
  142. package/dist/domain/index.js.map +1 -0
  143. package/dist/domain/report.d.ts +904 -0
  144. package/dist/domain/report.d.ts.map +1 -0
  145. package/dist/domain/report.js +90 -0
  146. package/dist/domain/report.js.map +1 -0
  147. package/dist/domain/requirement.d.ts +10 -0
  148. package/dist/domain/requirement.d.ts.map +1 -0
  149. package/dist/domain/requirement.js +12 -0
  150. package/dist/domain/requirement.js.map +1 -0
  151. package/dist/domain/run.d.ts +857 -0
  152. package/dist/domain/run.d.ts.map +1 -0
  153. package/dist/domain/run.js +129 -0
  154. package/dist/domain/run.js.map +1 -0
  155. package/dist/domain/shared.d.ts +60 -0
  156. package/dist/domain/shared.d.ts.map +1 -0
  157. package/dist/domain/shared.js +66 -0
  158. package/dist/domain/shared.js.map +1 -0
  159. package/dist/domain/task.d.ts +316 -0
  160. package/dist/domain/task.d.ts.map +1 -0
  161. package/dist/domain/task.js +116 -0
  162. package/dist/domain/task.js.map +1 -0
  163. package/dist/index.d.ts +15 -0
  164. package/dist/index.d.ts.map +1 -0
  165. package/dist/index.js +15 -0
  166. package/dist/index.js.map +1 -0
  167. package/dist/path/workspace-path.d.ts +6 -0
  168. package/dist/path/workspace-path.d.ts.map +1 -0
  169. package/dist/path/workspace-path.js +36 -0
  170. package/dist/path/workspace-path.js.map +1 -0
  171. package/dist/plugin/create-harness-plugin.d.ts +40 -0
  172. package/dist/plugin/create-harness-plugin.d.ts.map +1 -0
  173. package/dist/plugin/create-harness-plugin.js +169 -0
  174. package/dist/plugin/create-harness-plugin.js.map +1 -0
  175. package/dist/plugin/harness-agent-config.d.ts +6 -0
  176. package/dist/plugin/harness-agent-config.d.ts.map +1 -0
  177. package/dist/plugin/harness-agent-config.js +130 -0
  178. package/dist/plugin/harness-agent-config.js.map +1 -0
  179. package/dist/plugin/harness-chat-hook.d.ts +27 -0
  180. package/dist/plugin/harness-chat-hook.d.ts.map +1 -0
  181. package/dist/plugin/harness-chat-hook.js +72 -0
  182. package/dist/plugin/harness-chat-hook.js.map +1 -0
  183. package/dist/plugin/harness-chat-parts.d.ts +10 -0
  184. package/dist/plugin/harness-chat-parts.d.ts.map +1 -0
  185. package/dist/plugin/harness-chat-parts.js +36 -0
  186. package/dist/plugin/harness-chat-parts.js.map +1 -0
  187. package/dist/plugin/harness-chat-protocol.d.ts +9 -0
  188. package/dist/plugin/harness-chat-protocol.d.ts.map +1 -0
  189. package/dist/plugin/harness-chat-protocol.js +23 -0
  190. package/dist/plugin/harness-chat-protocol.js.map +1 -0
  191. package/dist/plugin/harness-coordinator.d.ts +33 -0
  192. package/dist/plugin/harness-coordinator.d.ts.map +1 -0
  193. package/dist/plugin/harness-coordinator.js +188 -0
  194. package/dist/plugin/harness-coordinator.js.map +1 -0
  195. package/dist/plugin/harness-intake-prompt.d.ts +2 -0
  196. package/dist/plugin/harness-intake-prompt.d.ts.map +1 -0
  197. package/dist/plugin/harness-intake-prompt.js +52 -0
  198. package/dist/plugin/harness-intake-prompt.js.map +1 -0
  199. package/dist/plugin/harness-intent.d.ts +72 -0
  200. package/dist/plugin/harness-intent.d.ts.map +1 -0
  201. package/dist/plugin/harness-intent.js +82 -0
  202. package/dist/plugin/harness-intent.js.map +1 -0
  203. package/dist/plugin/harness-parent-session-dispatcher.d.ts +46 -0
  204. package/dist/plugin/harness-parent-session-dispatcher.d.ts.map +1 -0
  205. package/dist/plugin/harness-parent-session-dispatcher.js +33 -0
  206. package/dist/plugin/harness-parent-session-dispatcher.js.map +1 -0
  207. package/dist/plugin/harness-requirement.d.ts +4 -0
  208. package/dist/plugin/harness-requirement.d.ts.map +1 -0
  209. package/dist/plugin/harness-requirement.js +37 -0
  210. package/dist/plugin/harness-requirement.js.map +1 -0
  211. package/dist/session/contracts.d.ts +178 -0
  212. package/dist/session/contracts.d.ts.map +1 -0
  213. package/dist/session/contracts.js +28 -0
  214. package/dist/session/contracts.js.map +1 -0
  215. package/dist/session/fake-session-adapter.d.ts +22 -0
  216. package/dist/session/fake-session-adapter.d.ts.map +1 -0
  217. package/dist/session/fake-session-adapter.js +238 -0
  218. package/dist/session/fake-session-adapter.js.map +1 -0
  219. package/dist/session/fake-session-errors.d.ts +23 -0
  220. package/dist/session/fake-session-errors.d.ts.map +1 -0
  221. package/dist/session/fake-session-errors.js +37 -0
  222. package/dist/session/fake-session-errors.js.map +1 -0
  223. package/dist/session/harness-agent-names.d.ts +51 -0
  224. package/dist/session/harness-agent-names.d.ts.map +1 -0
  225. package/dist/session/harness-agent-names.js +98 -0
  226. package/dist/session/harness-agent-names.js.map +1 -0
  227. package/dist/session/index.d.ts +9 -0
  228. package/dist/session/index.d.ts.map +1 -0
  229. package/dist/session/index.js +9 -0
  230. package/dist/session/index.js.map +1 -0
  231. package/dist/session/opencode-session-adapter.d.ts +9 -0
  232. package/dist/session/opencode-session-adapter.d.ts.map +1 -0
  233. package/dist/session/opencode-session-adapter.js +243 -0
  234. package/dist/session/opencode-session-adapter.js.map +1 -0
  235. package/dist/session/opencode-session-client.d.ts +58 -0
  236. package/dist/session/opencode-session-client.d.ts.map +1 -0
  237. package/dist/session/opencode-session-client.js +17 -0
  238. package/dist/session/opencode-session-client.js.map +1 -0
  239. package/dist/session/opencode-session-errors.d.ts +15 -0
  240. package/dist/session/opencode-session-errors.d.ts.map +1 -0
  241. package/dist/session/opencode-session-errors.js +19 -0
  242. package/dist/session/opencode-session-errors.js.map +1 -0
  243. package/dist/session/opencode-session-prompts.d.ts +12 -0
  244. package/dist/session/opencode-session-prompts.d.ts.map +1 -0
  245. package/dist/session/opencode-session-prompts.js +16 -0
  246. package/dist/session/opencode-session-prompts.js.map +1 -0
  247. package/dist/store/filesystem-run-store-io.d.ts +9 -0
  248. package/dist/store/filesystem-run-store-io.d.ts.map +1 -0
  249. package/dist/store/filesystem-run-store-io.js +85 -0
  250. package/dist/store/filesystem-run-store-io.js.map +1 -0
  251. package/dist/store/filesystem-run-store.d.ts +16 -0
  252. package/dist/store/filesystem-run-store.d.ts.map +1 -0
  253. package/dist/store/filesystem-run-store.js +286 -0
  254. package/dist/store/filesystem-run-store.js.map +1 -0
  255. package/dist/store/index.d.ts +5 -0
  256. package/dist/store/index.d.ts.map +1 -0
  257. package/dist/store/index.js +4 -0
  258. package/dist/store/index.js.map +1 -0
  259. package/dist/store/run-event.d.ts +786 -0
  260. package/dist/store/run-event.d.ts.map +1 -0
  261. package/dist/store/run-event.js +24 -0
  262. package/dist/store/run-event.js.map +1 -0
  263. package/dist/store/run-store-errors.d.ts +38 -0
  264. package/dist/store/run-store-errors.d.ts.map +1 -0
  265. package/dist/store/run-store-errors.js +62 -0
  266. package/dist/store/run-store-errors.js.map +1 -0
  267. package/dist/tools/context7-client.d.ts +22 -0
  268. package/dist/tools/context7-client.d.ts.map +1 -0
  269. package/dist/tools/context7-client.js +108 -0
  270. package/dist/tools/context7-client.js.map +1 -0
  271. package/dist/tools/grep-app-client.d.ts +21 -0
  272. package/dist/tools/grep-app-client.d.ts.map +1 -0
  273. package/dist/tools/grep-app-client.js +136 -0
  274. package/dist/tools/grep-app-client.js.map +1 -0
  275. package/dist/tools/harness-research-tools.d.ts +21 -0
  276. package/dist/tools/harness-research-tools.d.ts.map +1 -0
  277. package/dist/tools/harness-research-tools.js +101 -0
  278. package/dist/tools/harness-research-tools.js.map +1 -0
  279. package/dist/tools/lsp-client.d.ts +32 -0
  280. package/dist/tools/lsp-client.d.ts.map +1 -0
  281. package/dist/tools/lsp-client.js +234 -0
  282. package/dist/tools/lsp-client.js.map +1 -0
  283. package/dist/tools/lsp-json-rpc.d.ts +13 -0
  284. package/dist/tools/lsp-json-rpc.d.ts.map +1 -0
  285. package/dist/tools/lsp-json-rpc.js +98 -0
  286. package/dist/tools/lsp-json-rpc.js.map +1 -0
  287. package/dist/verification/command-policy.d.ts +11 -0
  288. package/dist/verification/command-policy.d.ts.map +1 -0
  289. package/dist/verification/command-policy.js +262 -0
  290. package/dist/verification/command-policy.js.map +1 -0
  291. package/dist/verification/contracts.d.ts +82 -0
  292. package/dist/verification/contracts.d.ts.map +1 -0
  293. package/dist/verification/contracts.js +6 -0
  294. package/dist/verification/contracts.js.map +1 -0
  295. package/dist/verification/fake-process-runner.d.ts +16 -0
  296. package/dist/verification/fake-process-runner.d.ts.map +1 -0
  297. package/dist/verification/fake-process-runner.js +28 -0
  298. package/dist/verification/fake-process-runner.js.map +1 -0
  299. package/dist/verification/index.d.ts +6 -0
  300. package/dist/verification/index.d.ts.map +1 -0
  301. package/dist/verification/index.js +5 -0
  302. package/dist/verification/index.js.map +1 -0
  303. package/dist/verification/node-process-runner.d.ts +12 -0
  304. package/dist/verification/node-process-runner.d.ts.map +1 -0
  305. package/dist/verification/node-process-runner.js +103 -0
  306. package/dist/verification/node-process-runner.js.map +1 -0
  307. package/dist/verification/verification-runner.d.ts +3 -0
  308. package/dist/verification/verification-runner.d.ts.map +1 -0
  309. package/dist/verification/verification-runner.js +101 -0
  310. package/dist/verification/verification-runner.js.map +1 -0
  311. package/dist/workspace/git-baseline.d.ts +63 -0
  312. package/dist/workspace/git-baseline.d.ts.map +1 -0
  313. package/dist/workspace/git-baseline.js +53 -0
  314. package/dist/workspace/git-baseline.js.map +1 -0
  315. package/dist/workspace/git-workspace-adapter.d.ts +43 -0
  316. package/dist/workspace/git-workspace-adapter.d.ts.map +1 -0
  317. package/dist/workspace/git-workspace-adapter.js +279 -0
  318. package/dist/workspace/git-workspace-adapter.js.map +1 -0
  319. package/dist/workspace/git-worktree-adapter.d.ts +45 -0
  320. package/dist/workspace/git-worktree-adapter.d.ts.map +1 -0
  321. package/dist/workspace/git-worktree-adapter.js +104 -0
  322. package/dist/workspace/git-worktree-adapter.js.map +1 -0
  323. package/dist/workspace/index.d.ts +7 -0
  324. package/dist/workspace/index.d.ts.map +1 -0
  325. package/dist/workspace/index.js +4 -0
  326. package/dist/workspace/index.js.map +1 -0
  327. package/package.json +45 -0
@@ -0,0 +1,751 @@
1
+ import { AgentResultSchema } from "../domain/index.js";
2
+ import { VerificationExecutionStatus } from "../verification/index.js";
3
+ import { createGitWorkspaceAdapter, createGitWorktreeAdapter } from "../workspace/index.js";
4
+ import { createEvidence } from "./evidence-factory.js";
5
+ import { resolveFailureAnalysis } from "./failure-analysis.js";
6
+ import { createAcceptanceFailure, createContextGapFailure, createImplementationFailure, createMergeConflictFailure, createModelFailure, createScopeDriftFailure, createVerificationEnvironmentFailure, } from "./failure-factory.js";
7
+ import { orderTasks } from "./graph-validator.js";
8
+ import { runRepairLoop } from "./repair-loop.js";
9
+ import { consumeBudget, parsePlannedRun, RunBudgetExceededError, withParts, withPlannedStatus, withTask, } from "./run-state.js";
10
+ import { readAcceptanceOutput, readFailureAnalyzerOutput, readVerifierOutput, readWorkerOutput, } from "./task-agent-output.js";
11
+ import { isAllowedPath } from "./task-paths.js";
12
+ import { persistTaskFailed, persistTaskRetry } from "./task-retry.js";
13
+ import { createWorktreeScheduler } from "./worktree-scheduler.js";
14
+ export const createTaskExecutor = (input) => {
15
+ const workspaceAdapter = input.options.workspaceAdapter ?? createGitWorkspaceAdapter(input.options.workingDirectory);
16
+ const workspaceAdapterFactory = input.options.workspaceAdapterFactory ?? createGitWorkspaceAdapter;
17
+ const analyzeFailure = async (params) => {
18
+ const analysisResult = await readFailureAnalyzerOutput({
19
+ input,
20
+ run: params.run,
21
+ task: params.task,
22
+ attempt: params.attempt,
23
+ failure: params.failure,
24
+ evidence: params.evidence,
25
+ });
26
+ if (analysisResult.kind === "failed") {
27
+ const failedRun = withParts(withTask(params.run, { ...params.task, status: "failed" }), {
28
+ evidence: params.run.evidence,
29
+ failures: [...params.run.failures, params.failure, analysisResult.failure],
30
+ agentResults: params.run.agentResults,
31
+ workspace: params.run.workspace,
32
+ });
33
+ await input.writer.persist(withPlannedStatus(failedRun, "failed"), input.writer.createEvent(params.run.runId, "task.failed", {
34
+ taskId: params.task.taskId,
35
+ attempt: params.attempt,
36
+ failureId: analysisResult.failure.failureId,
37
+ }));
38
+ return {
39
+ run: withPlannedStatus(failedRun, "failed"),
40
+ repairRequested: false,
41
+ failure: analysisResult.failure,
42
+ };
43
+ }
44
+ const resolution = resolveFailureAnalysis({
45
+ options: input.options,
46
+ run: params.run,
47
+ task: params.task,
48
+ attempt: params.attempt,
49
+ sourceFailure: params.failure,
50
+ evidence: params.evidence,
51
+ analysis: analysisResult.output,
52
+ });
53
+ const nextTask = { ...params.task, status: resolution.taskStatus };
54
+ const nextRun = withParts(withTask(params.run, nextTask), {
55
+ evidence: params.run.evidence,
56
+ failures: [...params.run.failures, params.failure, resolution.failure],
57
+ agentResults: [...params.run.agentResults, resolution.result],
58
+ workspace: params.run.workspace,
59
+ });
60
+ await input.writer.persist(withPlannedStatus(nextRun, resolution.nextStatus), input.writer.createEvent(params.run.runId, resolution.eventType, resolution.eventPayload));
61
+ return {
62
+ run: withPlannedStatus(nextRun, resolution.nextStatus),
63
+ repairRequested: resolution.eventType === "task.repair.requested",
64
+ failure: resolution.failure,
65
+ };
66
+ };
67
+ const verifyAndAccept = async (params) => {
68
+ const verifierOutput = await readVerifierOutput({
69
+ input,
70
+ run: params.run,
71
+ task: params.task,
72
+ attempt: params.attempt,
73
+ evidence: params.evidence,
74
+ });
75
+ if (verifierOutput.kind === "failed") {
76
+ const resolution = await analyzeFailure({
77
+ run: params.run,
78
+ task: params.task,
79
+ attempt: params.attempt,
80
+ failure: verifierOutput.failure,
81
+ evidence: params.evidence,
82
+ });
83
+ return resolution.run;
84
+ }
85
+ if (verifierOutput.output.status !== "passed") {
86
+ const verifierFailure = verifierOutput.output.status === "evidence_gap"
87
+ ? createContextGapFailure({
88
+ failureId: input.options.ids.createFailureId(`${params.task.taskId}-${params.attempt}-verifier-evidence-gap`),
89
+ runId: params.run.runId,
90
+ taskId: params.task.taskId,
91
+ relatedEvidenceIds: params.evidence.map((item) => item.evidenceId),
92
+ summary: verifierOutput.output.summary,
93
+ detail: verifierOutput.output.missingEvidenceKinds.join(", "),
94
+ recoveryPlan: "Collect the missing verification evidence before retrying.",
95
+ missingContext: verifierOutput.output.missingEvidenceKinds,
96
+ })
97
+ : createImplementationFailure({
98
+ failureId: input.options.ids.createFailureId(`${params.task.taskId}-${params.attempt}-verifier`),
99
+ runId: params.run.runId,
100
+ taskId: params.task.taskId,
101
+ ...(params.evidence[0] === undefined
102
+ ? {}
103
+ : { failingEvidenceId: params.evidence[0].evidenceId }),
104
+ summary: verifierOutput.output.summary,
105
+ detail: verifierOutput.output.status,
106
+ recoveryPlan: "Retry the task with fresh evidence.",
107
+ });
108
+ const resolution = await analyzeFailure({
109
+ run: params.run,
110
+ task: params.task,
111
+ attempt: params.attempt,
112
+ failure: verifierFailure,
113
+ evidence: params.evidence,
114
+ });
115
+ return resolution.run;
116
+ }
117
+ const workerResult = AgentResultSchema.parse({
118
+ schemaVersion: "1.0.0",
119
+ resultId: input.options.ids.createAgentResultId(`worker-${params.task.taskId}-${params.attempt}`),
120
+ runId: params.run.runId,
121
+ taskId: params.task.taskId,
122
+ role: "worker",
123
+ status: "completed",
124
+ summary: `Task ${params.task.taskId} completed`,
125
+ evidenceIds: params.evidence.map((item) => item.evidenceId),
126
+ changedPaths: params.changedPaths,
127
+ });
128
+ const verifierResult = AgentResultSchema.parse({
129
+ schemaVersion: "1.0.0",
130
+ resultId: input.options.ids.createAgentResultId(`verifier-${params.task.taskId}-${params.attempt}`),
131
+ runId: params.run.runId,
132
+ taskId: params.task.taskId,
133
+ role: "verifier",
134
+ status: "completed",
135
+ summary: verifierOutput.output.summary,
136
+ evidenceIds: params.evidence.map((item) => item.evidenceId),
137
+ changedPaths: [],
138
+ });
139
+ const acceptedRun = withParts(params.run, {
140
+ evidence: params.run.evidence,
141
+ failures: params.run.failures,
142
+ agentResults: [...params.run.agentResults, workerResult, verifierResult],
143
+ workspace: params.run.workspace,
144
+ });
145
+ const accepting = withPlannedStatus(acceptedRun, "verifying");
146
+ await input.writer.persist(accepting, input.writer.createEvent(params.run.runId, "task.acceptance.started", {
147
+ taskId: params.task.taskId,
148
+ attempt: params.attempt,
149
+ }));
150
+ const acceptanceOutput = await readAcceptanceOutput({
151
+ input,
152
+ run: accepting,
153
+ task: params.task,
154
+ attempt: params.attempt,
155
+ evidence: params.evidence,
156
+ });
157
+ if (acceptanceOutput.kind === "failed") {
158
+ const resolution = await analyzeFailure({
159
+ run: accepting,
160
+ task: params.task,
161
+ attempt: params.attempt,
162
+ failure: acceptanceOutput.failure,
163
+ evidence: params.evidence,
164
+ });
165
+ return resolution.run;
166
+ }
167
+ if (acceptanceOutput.output.status !== "accepted") {
168
+ const acceptanceFailure = acceptanceOutput.output.status === "evidence_gap"
169
+ ? createContextGapFailure({
170
+ failureId: input.options.ids.createFailureId(`${params.task.taskId}-${params.attempt}-acceptance-evidence-gap`),
171
+ runId: params.run.runId,
172
+ taskId: params.task.taskId,
173
+ relatedEvidenceIds: params.evidence.map((item) => item.evidenceId),
174
+ summary: acceptanceOutput.output.summary,
175
+ detail: acceptanceOutput.output.missingEvidenceKinds.join(", "),
176
+ recoveryPlan: "Collect the missing acceptance evidence before completing.",
177
+ missingContext: acceptanceOutput.output.missingEvidenceKinds,
178
+ })
179
+ : params.evidence[0] === undefined
180
+ ? createContextGapFailure({
181
+ failureId: input.options.ids.createFailureId(`${params.task.taskId}-${params.attempt}-acceptance-missing-evidence`),
182
+ runId: params.run.runId,
183
+ taskId: params.task.taskId,
184
+ relatedEvidenceIds: [],
185
+ summary: acceptanceOutput.output.summary,
186
+ detail: "Acceptance reported failed but no deterministic evidence exists.",
187
+ recoveryPlan: "Capture real deterministic evidence before processing acceptance failure.",
188
+ missingContext: ["acceptance-evidence"],
189
+ })
190
+ : createAcceptanceFailure({
191
+ failureId: input.options.ids.createFailureId(`${params.task.taskId}-${params.attempt}-acceptance`),
192
+ runId: params.run.runId,
193
+ taskId: params.task.taskId,
194
+ failingEvidenceId: params.evidence[0].evidenceId,
195
+ summary: acceptanceOutput.output.summary,
196
+ detail: acceptanceOutput.output.status,
197
+ recoveryPlan: "Address the acceptance findings before completing the task.",
198
+ });
199
+ const resolution = await analyzeFailure({
200
+ run: accepting,
201
+ task: params.task,
202
+ attempt: params.attempt,
203
+ failure: acceptanceFailure,
204
+ evidence: params.evidence,
205
+ });
206
+ return resolution.run;
207
+ }
208
+ const completedTask = {
209
+ ...params.task,
210
+ status: "completed",
211
+ evidenceRefs: [
212
+ ...params.task.evidenceRefs,
213
+ ...params.evidence.map((item) => item.evidenceId),
214
+ ],
215
+ };
216
+ const acceptanceResult = AgentResultSchema.parse({
217
+ schemaVersion: "1.0.0",
218
+ resultId: input.options.ids.createAgentResultId(`acceptance-${params.task.taskId}-${params.attempt}`),
219
+ runId: params.run.runId,
220
+ taskId: params.task.taskId,
221
+ role: "acceptance",
222
+ status: "completed",
223
+ summary: acceptanceOutput.output.summary,
224
+ evidenceIds: params.evidence.map((item) => item.evidenceId),
225
+ changedPaths: [],
226
+ });
227
+ const currentRun = withPlannedStatus(withParts(withTask(accepting, completedTask), {
228
+ evidence: accepting.evidence,
229
+ failures: accepting.failures,
230
+ agentResults: [...accepting.agentResults, acceptanceResult],
231
+ workspace: accepting.workspace,
232
+ }), "executing");
233
+ await input.writer.persist(currentRun, input.writer.createEvent(params.run.runId, "task.completed", {
234
+ taskId: params.task.taskId,
235
+ attempt: params.attempt,
236
+ }));
237
+ return currentRun;
238
+ };
239
+ const handleRepairRequest = async (params) => {
240
+ if (params.task.retryBudget.maxRepairAttempts <= 0) {
241
+ return persistTaskFailed({
242
+ run: params.run,
243
+ failure: createImplementationFailure({
244
+ failureId: input.options.ids.createFailureId(`${params.task.taskId}-${params.attempt}-repair-budget`),
245
+ runId: params.run.runId,
246
+ taskId: params.task.taskId,
247
+ summary: "Repair budget exhausted before automatic repair could begin.",
248
+ detail: `task ${params.task.taskId} has maxRepairAttempts=${params.task.retryBudget.maxRepairAttempts}`,
249
+ recoveryPlan: "Increase the repair budget or resolve the failure manually.",
250
+ }),
251
+ task: params.task,
252
+ writer: input.writer,
253
+ });
254
+ }
255
+ const repaired = await runRepairLoop({
256
+ executor: input,
257
+ run: params.run,
258
+ task: params.task,
259
+ attempt: params.attempt,
260
+ failure: params.failure,
261
+ evidence: params.evidence,
262
+ analyzeFailure,
263
+ });
264
+ if (repaired.kind === "terminal") {
265
+ return repaired.run;
266
+ }
267
+ return verifyAndAccept({
268
+ run: repaired.run,
269
+ task: params.task,
270
+ attempt: params.attempt,
271
+ evidence: repaired.evidence,
272
+ changedPaths: repaired.changedPaths,
273
+ });
274
+ };
275
+ const runTask = async (run, task, executionCwd = input.options.workingDirectory) => {
276
+ const taskWorkspaceAdapter = executionCwd === input.options.workingDirectory
277
+ ? workspaceAdapter
278
+ : workspaceAdapterFactory(executionCwd);
279
+ const maxAttempts = Math.max(task.retryBudget.maxAttempts, 1);
280
+ let currentRun = run;
281
+ let lastFailure;
282
+ for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
283
+ try {
284
+ currentRun = claimTaskAttemptBudget(currentRun, attempt);
285
+ }
286
+ catch (error) {
287
+ if (!(error instanceof RunBudgetExceededError)) {
288
+ throw error;
289
+ }
290
+ lastFailure = createImplementationFailure({
291
+ failureId: input.options.ids.createFailureId(`${task.taskId}-${attempt}-budget`),
292
+ runId: run.runId,
293
+ taskId: task.taskId,
294
+ summary: `${error.budget} budget exhausted.`,
295
+ detail: error.message,
296
+ recoveryPlan: "Request a manual decision or expand the relevant budget.",
297
+ });
298
+ break;
299
+ }
300
+ currentRun = withTask(currentRun, { ...task, status: "in_progress" });
301
+ await input.writer.persist(withPlannedStatus(currentRun, "executing"), input.writer.createEvent(run.runId, "task.started", { taskId: task.taskId, attempt }));
302
+ const workspaceBeforeTask = await taskWorkspaceAdapter.captureCurrentState();
303
+ const workerOutput = await readWorkerOutput({
304
+ input,
305
+ run,
306
+ task,
307
+ attempt,
308
+ workingDirectory: executionCwd,
309
+ });
310
+ const workspaceAfterWorker = await taskWorkspaceAdapter.captureCurrentState();
311
+ if (workerOutput.kind === "failed") {
312
+ lastFailure = workerOutput.failure;
313
+ }
314
+ else if (!workerOutput.output.changedPaths.every((path) => isAllowedPath(path, task.allowedPaths))) {
315
+ lastFailure = createModelFailure({
316
+ failureId: input.options.ids.createFailureId(`${task.taskId}-${attempt}-worker-scope`),
317
+ runId: run.runId,
318
+ taskId: task.taskId,
319
+ provider: "worker",
320
+ summary: "Worker reported paths outside the allowed task scope.",
321
+ detail: workerOutput.output.changedPaths.join(", "),
322
+ recoveryPlan: "Retry with a constrained task scope and bounded output.",
323
+ });
324
+ }
325
+ else {
326
+ const verifying = withPlannedStatus(currentRun, "verifying");
327
+ await input.writer.persist(verifying, input.writer.createEvent(run.runId, "task.verification.started", {
328
+ taskId: task.taskId,
329
+ attempt,
330
+ }));
331
+ let verificationResult;
332
+ try {
333
+ verificationResult = await input.options.verificationRunner.run({
334
+ verificationCommands: task.verificationPlan,
335
+ defaultWorkingDirectory: executionCwd,
336
+ });
337
+ }
338
+ catch (error) {
339
+ if (!(error instanceof Error)) {
340
+ throw error;
341
+ }
342
+ lastFailure = createVerificationEnvironmentFailure({
343
+ failureId: input.options.ids.createFailureId(`${task.taskId}-${attempt}-verification-environment`),
344
+ runId: run.runId,
345
+ taskId: task.taskId,
346
+ commandName: task.verificationPlan[0]?.name ?? "verification",
347
+ detail: error.message,
348
+ });
349
+ }
350
+ const workspaceAfterVerification = await taskWorkspaceAdapter.captureCurrentState();
351
+ if (currentRun.workspace.git.mode === "git") {
352
+ const workspaceAssessment = await taskWorkspaceAdapter.assessTaskChanges({
353
+ baseline: currentRun.workspace.git,
354
+ before: workspaceBeforeTask,
355
+ afterWorker: workspaceAfterWorker,
356
+ afterVerification: workspaceAfterVerification,
357
+ claimedChangedPaths: workerOutput.output.changedPaths,
358
+ allowedPaths: task.allowedPaths,
359
+ });
360
+ if (workspaceAssessment.status === "paused") {
361
+ const workspaceFailure = createScopeDriftFailure({
362
+ failureId: input.options.ids.createFailureId(`${task.taskId}-${attempt}-workspace-drift`),
363
+ runId: run.runId,
364
+ taskId: task.taskId,
365
+ driftedPaths: workspaceAssessment.driftedPaths,
366
+ summary: "Workspace protection paused the task after git baseline drift.",
367
+ detail: workspaceAssessment.detail,
368
+ recoveryPlan: "Inspect external or out-of-scope changes before resuming the task.",
369
+ });
370
+ const pausedRun = withPlannedStatus(withParts(withTask(currentRun, { ...task, status: "paused" }), {
371
+ evidence: currentRun.evidence,
372
+ failures: [...currentRun.failures, workspaceFailure],
373
+ agentResults: currentRun.agentResults,
374
+ workspace: currentRun.workspace,
375
+ }), "paused");
376
+ await input.writer.persist(pausedRun, input.writer.createEvent(run.runId, "task.paused.external_change", {
377
+ taskId: task.taskId,
378
+ attempt,
379
+ changedPaths: workspaceAssessment.actualChangedPaths,
380
+ driftedPaths: workspaceAssessment.driftedPaths,
381
+ }));
382
+ return pausedRun;
383
+ }
384
+ currentRun = withParts(currentRun, {
385
+ evidence: currentRun.evidence,
386
+ failures: currentRun.failures,
387
+ agentResults: currentRun.agentResults,
388
+ workspace: { git: workspaceAssessment.nextWorkspace },
389
+ });
390
+ }
391
+ if (verificationResult === undefined) {
392
+ const resolution = await analyzeFailure({
393
+ run: currentRun,
394
+ task,
395
+ attempt,
396
+ failure: lastFailure ??
397
+ createImplementationFailure({
398
+ failureId: input.options.ids.createFailureId(`${task.taskId}-${attempt}-verification`),
399
+ runId: run.runId,
400
+ taskId: task.taskId,
401
+ summary: "Verification did not produce a usable result.",
402
+ detail: "verificationResult was undefined",
403
+ recoveryPlan: "Retry the task with a stable verification environment.",
404
+ }),
405
+ evidence: [],
406
+ });
407
+ currentRun = resolution.run;
408
+ if (resolution.repairRequested &&
409
+ currentRun.status === "executing" &&
410
+ task.repairEnabled) {
411
+ return handleRepairRequest({
412
+ run: currentRun,
413
+ task,
414
+ attempt,
415
+ failure: resolution.failure,
416
+ evidence: [],
417
+ });
418
+ }
419
+ }
420
+ else if (verificationResult.records.length === 0) {
421
+ lastFailure = createContextGapFailure({
422
+ failureId: input.options.ids.createFailureId(`${task.taskId}-${attempt}-verification-empty`),
423
+ runId: run.runId,
424
+ taskId: task.taskId,
425
+ relatedEvidenceIds: [],
426
+ summary: "Deterministic verification produced no evidence records.",
427
+ detail: "At least one deterministic verification record is required before verifier or acceptance can run.",
428
+ recoveryPlan: "Re-run deterministic verification and capture at least one real evidence record.",
429
+ missingContext: ["verification-record"],
430
+ });
431
+ const resolution = await analyzeFailure({
432
+ run: currentRun,
433
+ task,
434
+ attempt,
435
+ failure: lastFailure,
436
+ evidence: [],
437
+ });
438
+ currentRun = resolution.run;
439
+ if (resolution.repairRequested &&
440
+ currentRun.status === "executing" &&
441
+ task.repairEnabled) {
442
+ return handleRepairRequest({
443
+ run: currentRun,
444
+ task,
445
+ attempt,
446
+ failure: resolution.failure,
447
+ evidence: [],
448
+ });
449
+ }
450
+ }
451
+ else {
452
+ const evidence = verificationResult.records.map((record, index) => createEvidence({
453
+ ids: input.options.ids,
454
+ runId: run.runId,
455
+ task,
456
+ record,
457
+ label: `${task.taskId}-${attempt}-${index}`,
458
+ changedPaths: workerOutput.output.changedPaths,
459
+ acceptanceCriterionIds: paramsAcceptanceCriterionIds(run),
460
+ }));
461
+ currentRun = withParts(currentRun, {
462
+ evidence: [...currentRun.evidence, ...evidence],
463
+ failures: currentRun.failures,
464
+ agentResults: currentRun.agentResults,
465
+ workspace: currentRun.workspace,
466
+ });
467
+ const deterministicPassed = verificationResult.records.every((record) => record.status === VerificationExecutionStatus.passed);
468
+ if (!deterministicPassed) {
469
+ lastFailure = createImplementationFailure({
470
+ failureId: input.options.ids.createFailureId(`${task.taskId}-${attempt}-verification`),
471
+ runId: run.runId,
472
+ taskId: task.taskId,
473
+ ...(evidence[0] === undefined ? {} : { failingEvidenceId: evidence[0].evidenceId }),
474
+ summary: "Verification command failed after replay.",
475
+ detail: "A deterministic verification command failed or timed out.",
476
+ recoveryPlan: "Retry the task within its bounded attempt budget.",
477
+ });
478
+ const resolution = await analyzeFailure({
479
+ run: currentRun,
480
+ task,
481
+ attempt,
482
+ failure: lastFailure,
483
+ evidence,
484
+ });
485
+ currentRun = resolution.run;
486
+ if (resolution.repairRequested &&
487
+ currentRun.status === "executing" &&
488
+ task.repairEnabled) {
489
+ return handleRepairRequest({
490
+ run: currentRun,
491
+ task,
492
+ attempt,
493
+ failure: resolution.failure,
494
+ evidence,
495
+ });
496
+ }
497
+ }
498
+ else {
499
+ const finalized = await verifyAndAccept({
500
+ run: currentRun,
501
+ task,
502
+ attempt,
503
+ evidence,
504
+ changedPaths: workerOutput.output.changedPaths,
505
+ });
506
+ if (finalized.status !== "executing") {
507
+ return finalized;
508
+ }
509
+ currentRun = finalized;
510
+ const finalizedTask = currentRun.taskGraph.tasks.find((candidate) => candidate.taskId === task.taskId);
511
+ if (finalizedTask?.status === "completed") {
512
+ return currentRun;
513
+ }
514
+ lastFailure = currentRun.failures.at(-1);
515
+ }
516
+ }
517
+ }
518
+ if (currentRun.status !== "executing") {
519
+ return currentRun;
520
+ }
521
+ if (attempt < maxAttempts && lastFailure !== undefined) {
522
+ currentRun = await persistTaskRetry({
523
+ run: currentRun,
524
+ failure: lastFailure,
525
+ task,
526
+ attempt,
527
+ writer: input.writer,
528
+ });
529
+ }
530
+ }
531
+ const failure = lastFailure ??
532
+ createImplementationFailure({
533
+ failureId: input.options.ids.createFailureId(`${task.taskId}-exhausted`),
534
+ runId: run.runId,
535
+ taskId: task.taskId,
536
+ summary: "Task exhausted its retry budget.",
537
+ detail: "No successful worker/verifier cycle completed before the retry budget expired.",
538
+ recoveryPlan: "Increase retry budget or fix the underlying implementation.",
539
+ });
540
+ return persistTaskFailed({
541
+ run: currentRun,
542
+ failure,
543
+ task,
544
+ writer: input.writer,
545
+ });
546
+ };
547
+ const runTasks = async (run) => {
548
+ let currentRun = run;
549
+ const orderedTasks = orderTasks(currentRun.taskGraph);
550
+ const parallelResult = await runParallelReadyBatch(currentRun, orderedTasks);
551
+ if (parallelResult.kind === "paused") {
552
+ return parallelResult.run;
553
+ }
554
+ if (parallelResult.kind === "completed") {
555
+ currentRun = parallelResult.run;
556
+ }
557
+ for (const task of orderedTasks) {
558
+ const currentTask = currentRun.taskGraph.tasks.find((candidate) => candidate.taskId === task.taskId);
559
+ if (currentTask?.status === "completed") {
560
+ continue;
561
+ }
562
+ currentRun = await runTask(currentRun, task);
563
+ if (currentRun.status !== "executing") {
564
+ return currentRun;
565
+ }
566
+ }
567
+ const completed = withPlannedStatus(currentRun, "completed");
568
+ await input.writer.persist(completed, input.writer.createEvent(completed.runId, "run.completed", { status: "completed" }));
569
+ return completed;
570
+ };
571
+ return { runTasks };
572
+ async function runParallelReadyBatch(run, orderedTasks) {
573
+ if (run.workspace.git.mode !== "git") {
574
+ return { kind: "skipped" };
575
+ }
576
+ const completedTaskIds = new Set(orderedTasks.filter((task) => task.status === "completed").map((task) => task.taskId));
577
+ const readyTasks = orderedTasks.filter((task) => task.status !== "completed" &&
578
+ task.dependencies.every((dependency) => completedTaskIds.has(dependency)));
579
+ if (readyTasks.length < 2) {
580
+ return { kind: "skipped" };
581
+ }
582
+ const childRuns = [];
583
+ const scheduler = createWorktreeScheduler(input.options.worktreeAdapter ??
584
+ createGitWorktreeAdapter({ repositoryRoot: input.options.workingDirectory }));
585
+ const result = await scheduler.runEligibleBatch({
586
+ mainWorkspaceCwd: input.options.workingDirectory,
587
+ tasks: readyTasks,
588
+ completedTaskIds,
589
+ async executeTask({ task, cwd }) {
590
+ const childRun = await runTask(run, task, cwd);
591
+ childRuns.push(childRun);
592
+ const completedTask = childRun.taskGraph.tasks.find((candidate) => candidate.taskId === task.taskId);
593
+ if (childRun.status !== "executing" || completedTask?.status !== "completed") {
594
+ throw new Error(`parallel child task ${task.taskId} did not complete cleanly: run=${childRun.status}, task=${completedTask?.status ?? "missing"}`);
595
+ }
596
+ return {
597
+ changedPaths: latestWorkerChangedPaths(childRun, task),
598
+ evidenceRefs: completedTask?.evidenceRefs ?? [],
599
+ };
600
+ },
601
+ async runIntegrationVerification(cwd) {
602
+ const commands = readyTasks.flatMap((task) => task.verificationPlan.filter((command) => command.scope === "integration"));
603
+ if (commands.length === 0) {
604
+ return { passed: true, detail: "no integration verification configured" };
605
+ }
606
+ const verification = await input.options.verificationRunner.run({
607
+ verificationCommands: commands,
608
+ defaultWorkingDirectory: cwd,
609
+ });
610
+ const passed = verification.records.every((record) => record.status === VerificationExecutionStatus.passed);
611
+ return { passed, detail: passed ? "passed" : "integration verification failed" };
612
+ },
613
+ });
614
+ switch (result.kind) {
615
+ case "serial-fallback":
616
+ return { kind: "skipped" };
617
+ case "parallel-completed":
618
+ return { kind: "completed", run: mergeChildRuns(run, childRuns) };
619
+ case "merge-conflict":
620
+ return { kind: "paused", run: await persistMergeConflict(run, result.conflict.detail) };
621
+ case "execution-failed":
622
+ return { kind: "paused", run: await persistIntegrationFailure(run, result.detail) };
623
+ case "integration-failed":
624
+ return { kind: "paused", run: await persistIntegrationFailure(run, result.detail) };
625
+ case "cleanup-failed":
626
+ return { kind: "paused", run: await persistCleanupFailure(run, result) };
627
+ }
628
+ }
629
+ function latestWorkerChangedPaths(run, task) {
630
+ const result = run.agentResults.findLast((candidate) => candidate.role === "worker" && candidate.taskId === task.taskId);
631
+ return result?.status === "completed" ? result.changedPaths : [];
632
+ }
633
+ function mergeChildRuns(parentRun, childRuns) {
634
+ let merged = parentRun;
635
+ for (const childRun of childRuns) {
636
+ for (const task of childRun.taskGraph.tasks) {
637
+ const parentTask = merged.taskGraph.tasks.find((candidate) => candidate.taskId === task.taskId);
638
+ if (parentTask !== undefined && task.status === "completed") {
639
+ merged = withTask(merged, task);
640
+ }
641
+ }
642
+ merged = withParts(merged, {
643
+ evidence: uniqueById([...merged.evidence, ...childRun.evidence], (item) => item.evidenceId),
644
+ failures: uniqueById([...merged.failures, ...childRun.failures], (item) => item.failureId),
645
+ agentResults: uniqueById([...merged.agentResults, ...childRun.agentResults], (item) => item.resultId),
646
+ workspace: childRun.workspace,
647
+ });
648
+ }
649
+ return merged;
650
+ }
651
+ async function persistMergeConflict(run, detail) {
652
+ const task = firstIncompleteTask(run);
653
+ const failure = createMergeConflictFailure({
654
+ failureId: input.options.ids.createFailureId(`${task.taskId}-worktree-merge-conflict`),
655
+ runId: run.runId,
656
+ taskId: task.taskId,
657
+ conflictingPaths: ["worktree-merge"],
658
+ summary: "Worktree merge conflict paused parallel execution.",
659
+ detail,
660
+ recoveryPlan: "Inspect retained task worktrees and resolve the merge manually.",
661
+ });
662
+ const paused = withPlannedStatus(withParts(withTask(run, { ...task, status: "paused" }), {
663
+ evidence: run.evidence,
664
+ failures: [...run.failures, failure],
665
+ agentResults: run.agentResults,
666
+ workspace: run.workspace,
667
+ }), "paused");
668
+ await input.writer.persist(paused, input.writer.createEvent(run.runId, "task.paused.merge_conflict", {
669
+ taskId: task.taskId,
670
+ failureId: failure.failureId,
671
+ }));
672
+ return paused;
673
+ }
674
+ async function persistIntegrationFailure(run, detail) {
675
+ const task = firstIncompleteTask(run);
676
+ const failure = createVerificationEnvironmentFailure({
677
+ failureId: input.options.ids.createFailureId(`${task.taskId}-worktree-integration`),
678
+ runId: run.runId,
679
+ taskId: task.taskId,
680
+ commandName: "integration verification",
681
+ detail,
682
+ });
683
+ const paused = withPlannedStatus(withParts(withTask(run, { ...task, status: "paused" }), {
684
+ evidence: run.evidence,
685
+ failures: [...run.failures, failure],
686
+ agentResults: run.agentResults,
687
+ workspace: run.workspace,
688
+ }), "paused");
689
+ await input.writer.persist(paused, input.writer.createEvent(run.runId, "task.paused.integration_verification", {
690
+ taskId: task.taskId,
691
+ failureId: failure.failureId,
692
+ }));
693
+ return paused;
694
+ }
695
+ async function persistCleanupFailure(run, result) {
696
+ const task = firstIncompleteTask(run);
697
+ const detail = result.cleanupFailures
698
+ .map((failure) => `${failure.taskId}:${failure.path}:${failure.detail}`)
699
+ .join("\n");
700
+ const failure = createVerificationEnvironmentFailure({
701
+ failureId: input.options.ids.createFailureId(`${task.taskId}-worktree-cleanup`),
702
+ runId: run.runId,
703
+ taskId: task.taskId,
704
+ commandName: "worktree cleanup",
705
+ detail,
706
+ });
707
+ const paused = withPlannedStatus(withParts(withTask(run, { ...task, status: "paused" }), {
708
+ evidence: run.evidence,
709
+ failures: [...run.failures, failure],
710
+ agentResults: run.agentResults,
711
+ workspace: run.workspace,
712
+ }), "paused");
713
+ await input.writer.persist(paused, input.writer.createEvent(run.runId, "task.paused.cleanup_failed", {
714
+ taskId: task.taskId,
715
+ failureId: failure.failureId,
716
+ retainedWorktrees: result.retainedWorktrees.map((lease) => lease.path),
717
+ cleanupReceipts: result.cleanupReceipts,
718
+ }));
719
+ return paused;
720
+ }
721
+ };
722
+ const uniqueById = (items, getId) => {
723
+ const seen = new Set();
724
+ const unique = [];
725
+ for (const item of items) {
726
+ const id = getId(item);
727
+ if (seen.has(id)) {
728
+ continue;
729
+ }
730
+ seen.add(id);
731
+ unique.push(item);
732
+ }
733
+ return unique;
734
+ };
735
+ const firstIncompleteTask = (run) => {
736
+ const task = run.taskGraph.tasks.find((candidate) => candidate.status !== "completed");
737
+ if (task === undefined) {
738
+ throw new TypeError("expected at least one incomplete task");
739
+ }
740
+ return task;
741
+ };
742
+ const claimTaskAttemptBudget = (run, attempt) => {
743
+ const consumedTaskBudget = parsePlannedRun(consumeBudget(run, "task"));
744
+ const consumedOverallBudget = parsePlannedRun(consumeBudget(consumedTaskBudget, "overall"));
745
+ if (attempt === 1) {
746
+ return consumedOverallBudget;
747
+ }
748
+ return parsePlannedRun(consumeBudget(consumedOverallBudget, "repair"));
749
+ };
750
+ const paramsAcceptanceCriterionIds = (run) => run.requirement.acceptanceCriteria.map((_criterion, index) => `${run.requirement.requirementId}-ac-${index + 1}`);
751
+ //# sourceMappingURL=task-executor.js.map