@fitlab-ai/agent-infra 0.9.14 → 0.10.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 (354) hide show
  1. package/README.md +10 -0
  2. package/README.zh-CN.md +10 -0
  3. package/bin/internal-cli.sh +45 -0
  4. package/dist/bin/internal-cli.js +93 -30
  5. package/dist/lib/agent-clients/adapters/codex-lifecycle/capability-store.js +165 -27
  6. package/dist/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.js +9 -4
  7. package/dist/lib/agent-clients/adapters/codex-lifecycle/store.js +43 -3
  8. package/dist/lib/defaults.json +103 -103
  9. package/dist/lib/fs/durable-write.js +39 -0
  10. package/dist/lib/fs/file-lock.js +111 -0
  11. package/dist/lib/host-control/audit.js +19 -0
  12. package/dist/lib/host-control/client.js +142 -0
  13. package/dist/lib/host-control/command.js +49 -0
  14. package/dist/lib/host-control/path.js +160 -0
  15. package/dist/lib/host-control/secure-fs.js +140 -0
  16. package/dist/lib/host-control/server.js +159 -0
  17. package/dist/lib/host-control/service.js +112 -0
  18. package/dist/lib/init.js +11 -2
  19. package/dist/lib/internal/cli-route-inventory.js +4 -2
  20. package/dist/lib/internal/codex-lifecycle.js +6 -6
  21. package/dist/lib/internal/demo-promotion.js +322 -0
  22. package/dist/lib/internal/demo-transcript.js +454 -0
  23. package/dist/lib/internal/host-control.js +28 -0
  24. package/dist/lib/internal/manual-validation.js +331 -0
  25. package/dist/lib/internal/platform-pr.js +18 -4
  26. package/dist/lib/internal/release-workflow.js +63 -84
  27. package/dist/lib/internal/sandbox-control.js +149 -2
  28. package/dist/lib/internal/task-artifact.js +16 -263
  29. package/dist/lib/internal/task-create.js +6 -5
  30. package/dist/lib/internal/task-event.js +37 -37
  31. package/dist/lib/internal/task-lifecycle.js +2 -2
  32. package/dist/lib/internal/task-operation-registry.js +101 -4
  33. package/dist/lib/internal/task-review.js +11 -56
  34. package/dist/lib/internal/task-validate.js +30 -10
  35. package/dist/lib/owned-path.js +33 -0
  36. package/dist/lib/platform/check-status.js +11 -0
  37. package/dist/lib/platform/comment-safety.js +4 -1
  38. package/dist/lib/platform/completion-backfill.js +1 -1
  39. package/dist/lib/platform/github-data.js +241 -0
  40. package/dist/lib/platform/github-provider.js +15 -20
  41. package/dist/lib/platform/issue-comments.js +18 -12
  42. package/dist/lib/platform/issues.js +2 -180
  43. package/dist/lib/platform/pr-change-report.js +5 -4
  44. package/dist/lib/platform/pr-checks.js +12 -65
  45. package/dist/lib/platform/pr-summary.js +117 -33
  46. package/dist/lib/platform/pull-requests.js +5 -108
  47. package/dist/lib/platform/snapshots.js +2 -0
  48. package/dist/lib/platform/task-identities.js +2 -9
  49. package/dist/lib/platform/verification-sync.js +132 -143
  50. package/dist/lib/pr-review/evidence-grading.js +4 -8
  51. package/dist/lib/release/workflow.js +1 -1
  52. package/dist/lib/sandbox/commands/create.js +7 -2
  53. package/dist/lib/sandbox/commands/rm.js +19 -1511
  54. package/dist/lib/sandbox/control/audit.js +203 -0
  55. package/dist/lib/sandbox/control/client.js +93 -54
  56. package/dist/lib/sandbox/control/executor.js +129 -65
  57. package/dist/lib/sandbox/control/finalization-response.js +27 -0
  58. package/dist/lib/sandbox/control/identity-sentinel.js +107 -0
  59. package/dist/lib/sandbox/control/lifecycle.js +36 -2
  60. package/dist/lib/sandbox/control/native-file-lock.js +6 -98
  61. package/dist/lib/sandbox/control/protocol.js +55 -4
  62. package/dist/lib/sandbox/control/server.js +518 -127
  63. package/dist/lib/sandbox/control/state.js +64 -0
  64. package/dist/lib/sandbox/control/task-workflow.js +74 -0
  65. package/dist/lib/sandbox/control/workflow-executor.js +89 -0
  66. package/dist/lib/sandbox/index.js +1 -2
  67. package/dist/lib/sandbox/recovery.js +28 -4
  68. package/dist/lib/sandbox/removal.js +822 -0
  69. package/dist/lib/sandbox/task-cleanup.js +217 -0
  70. package/dist/lib/sandbox/workspace-identity.js +17 -1
  71. package/dist/lib/sandbox/workspace-view.js +13 -14
  72. package/dist/lib/task/activity-intent.js +2 -1
  73. package/dist/lib/task/activity-log.js +30 -24
  74. package/dist/lib/task/artifact-command.js +79 -0
  75. package/dist/lib/task/artifact-lifecycle.js +30 -28
  76. package/dist/lib/task/artifact-name.js +36 -0
  77. package/dist/lib/task/artifact-operations.js +29 -289
  78. package/dist/lib/task/artifact-receipts.js +3 -15
  79. package/dist/lib/task/artifact-recovery.js +430 -0
  80. package/dist/lib/task/artifact-repair-intent.js +134 -29
  81. package/dist/lib/task/artifact-schema.js +6 -14
  82. package/dist/lib/task/capabilities.js +12 -19
  83. package/dist/lib/task/codex-orchestration.js +71 -25
  84. package/dist/lib/task/commit-identity.js +28 -0
  85. package/dist/lib/task/commit-operation.js +33 -20
  86. package/dist/lib/task/control-authority.js +403 -6
  87. package/dist/lib/task/control-recovery.js +140 -0
  88. package/dist/lib/task/create-service.js +1 -8
  89. package/dist/lib/task/create.js +62 -9
  90. package/dist/lib/task/decision-details.js +2 -46
  91. package/dist/lib/task/delegation-receipts.js +64 -3
  92. package/dist/lib/task/events.js +275 -22
  93. package/dist/lib/task/finalization-state.js +9 -0
  94. package/dist/lib/task/human-override.js +8 -14
  95. package/dist/lib/task/implementation-inputs.js +3 -3
  96. package/dist/lib/task/intermediate-cleanup.js +441 -0
  97. package/dist/lib/task/invalidation.js +18 -65
  98. package/dist/lib/task/ledger-intents.js +7 -22
  99. package/dist/lib/task/ledger.js +1 -3
  100. package/dist/lib/task/lifecycle-recovery.js +549 -0
  101. package/dist/lib/task/lifecycle.js +31 -4
  102. package/dist/lib/task/local-artifact-finalization.js +197 -197
  103. package/dist/lib/task/manual-validation-completion.js +26 -0
  104. package/dist/lib/task/manual-validation-evidence.js +130 -0
  105. package/dist/lib/task/manual-validation-receipt.js +105 -0
  106. package/dist/lib/task/manual-validation-shared.js +40 -0
  107. package/dist/lib/task/manual-validation-transaction.js +243 -0
  108. package/dist/lib/task/markdown.js +47 -0
  109. package/dist/lib/task/orchestration.js +66 -19
  110. package/dist/lib/task/pr-delivery-fact.js +12 -56
  111. package/dist/lib/task/qualification-audit.js +20 -34
  112. package/dist/lib/task/recovery-warning.js +19 -0
  113. package/dist/lib/task/review-artifacts.js +2 -18
  114. package/dist/lib/task/review-command.js +26 -0
  115. package/dist/lib/task/review-finalization.js +175 -216
  116. package/dist/lib/task/review-fingerprint.js +2 -2
  117. package/dist/lib/task/sections.js +34 -56
  118. package/dist/lib/task/short-id.js +15 -7
  119. package/dist/lib/task/task-execution-lock.js +1 -1
  120. package/dist/lib/task/verification-engine.js +119 -337
  121. package/dist/lib/task/verification.js +1 -1
  122. package/dist/lib/task/workflow-command.js +16 -0
  123. package/dist/lib/task/workflow-warning-intents.js +0 -16
  124. package/dist/lib/task/workflow-warnings.js +28 -2
  125. package/dist/lib/update.js +2 -0
  126. package/dist/lib/version.js +1 -11
  127. package/dist/lifecycle-build-manifest.json +3 -3
  128. package/lib/agent-clients/adapters/codex-lifecycle/capability-store.ts +223 -33
  129. package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.ts +10 -4
  130. package/lib/agent-clients/adapters/codex-lifecycle/store.ts +55 -3
  131. package/lib/fs/durable-write.ts +30 -0
  132. package/lib/fs/file-lock.ts +113 -0
  133. package/lib/host-control/audit.ts +22 -0
  134. package/lib/host-control/client.ts +172 -0
  135. package/lib/host-control/command.ts +60 -0
  136. package/lib/host-control/path.ts +167 -0
  137. package/lib/host-control/secure-fs.ts +135 -0
  138. package/lib/host-control/server.ts +180 -0
  139. package/lib/host-control/service.ts +133 -0
  140. package/lib/init.ts +15 -2
  141. package/lib/internal/cli-route-inventory.ts +4 -2
  142. package/lib/internal/codex-lifecycle.ts +6 -6
  143. package/lib/internal/demo-promotion.ts +354 -0
  144. package/lib/internal/demo-transcript.ts +419 -0
  145. package/lib/internal/host-control.ts +28 -0
  146. package/lib/internal/manual-validation.ts +315 -0
  147. package/lib/internal/platform-pr.ts +15 -4
  148. package/lib/internal/release-workflow.ts +74 -82
  149. package/lib/internal/sandbox-control.ts +162 -1
  150. package/lib/internal/task-artifact.ts +14 -217
  151. package/lib/internal/task-create.ts +5 -5
  152. package/lib/internal/task-event.ts +20 -11
  153. package/lib/internal/task-lifecycle.ts +4 -3
  154. package/lib/internal/task-operation-registry.ts +111 -4
  155. package/lib/internal/task-review.ts +9 -54
  156. package/lib/internal/task-validate.ts +26 -10
  157. package/lib/owned-path.ts +26 -0
  158. package/lib/platform/check-status.ts +9 -0
  159. package/lib/platform/comment-safety.ts +5 -0
  160. package/lib/platform/completion-backfill.ts +1 -1
  161. package/lib/platform/github-data.ts +322 -0
  162. package/lib/platform/github-provider.ts +15 -20
  163. package/lib/platform/issue-comments.ts +21 -14
  164. package/lib/platform/issues.ts +5 -246
  165. package/lib/platform/pr-change-report.ts +7 -5
  166. package/lib/platform/pr-checks.ts +13 -77
  167. package/lib/platform/pr-summary.ts +123 -35
  168. package/lib/platform/pull-requests.ts +6 -173
  169. package/lib/platform/snapshots.ts +53 -0
  170. package/lib/platform/task-identities.ts +3 -14
  171. package/lib/platform/verification-sync.ts +150 -156
  172. package/lib/pr-review/evidence-grading.ts +4 -9
  173. package/lib/release/workflow.ts +3 -3
  174. package/lib/sandbox/commands/create.ts +7 -6
  175. package/lib/sandbox/commands/rm.ts +21 -1895
  176. package/lib/sandbox/control/audit.ts +253 -0
  177. package/lib/sandbox/control/client.ts +109 -66
  178. package/lib/sandbox/control/executor.ts +134 -74
  179. package/lib/sandbox/control/finalization-response.ts +29 -0
  180. package/lib/sandbox/control/identity-sentinel.ts +137 -0
  181. package/lib/sandbox/control/lifecycle.ts +45 -3
  182. package/lib/sandbox/control/native-file-lock.ts +6 -107
  183. package/lib/sandbox/control/protocol.ts +80 -6
  184. package/lib/sandbox/control/server.ts +567 -132
  185. package/lib/sandbox/control/state.ts +86 -0
  186. package/lib/sandbox/control/task-workflow.ts +95 -0
  187. package/lib/sandbox/control/workflow-executor.ts +92 -0
  188. package/lib/sandbox/index.ts +1 -2
  189. package/lib/sandbox/recovery.ts +28 -5
  190. package/lib/sandbox/removal.ts +1142 -0
  191. package/lib/sandbox/task-cleanup.ts +209 -0
  192. package/lib/sandbox/workspace-identity.ts +19 -3
  193. package/lib/sandbox/workspace-view.ts +17 -15
  194. package/lib/task/activity-intent.ts +2 -2
  195. package/lib/task/activity-log.ts +27 -19
  196. package/lib/task/artifact-command.ts +85 -0
  197. package/lib/task/artifact-lifecycle.ts +31 -26
  198. package/lib/task/artifact-name.ts +55 -0
  199. package/lib/task/artifact-operations.ts +34 -310
  200. package/lib/task/artifact-receipts.ts +3 -14
  201. package/lib/task/artifact-recovery.ts +519 -0
  202. package/lib/task/artifact-repair-intent.ts +162 -33
  203. package/lib/task/artifact-schema.ts +5 -34
  204. package/lib/task/capabilities.ts +13 -22
  205. package/lib/task/codex-orchestration.ts +85 -24
  206. package/lib/task/commit-identity.ts +31 -0
  207. package/lib/task/commit-operation.ts +37 -20
  208. package/lib/task/control-authority.ts +540 -10
  209. package/lib/task/control-recovery.ts +186 -0
  210. package/lib/task/create-service.ts +1 -10
  211. package/lib/task/create.ts +63 -8
  212. package/lib/task/decision-details.ts +3 -71
  213. package/lib/task/delegation-receipts.ts +73 -2
  214. package/lib/task/events.ts +309 -24
  215. package/lib/task/finalization-state.ts +11 -0
  216. package/lib/task/human-override.ts +8 -16
  217. package/lib/task/implementation-inputs.ts +3 -3
  218. package/lib/task/intermediate-cleanup.ts +493 -0
  219. package/lib/task/invalidation.ts +18 -62
  220. package/lib/task/ledger-intents.ts +7 -24
  221. package/lib/task/ledger.ts +1 -3
  222. package/lib/task/lifecycle-recovery.ts +691 -0
  223. package/lib/task/lifecycle.ts +30 -5
  224. package/lib/task/local-artifact-finalization.ts +244 -234
  225. package/lib/task/manual-validation-completion.ts +40 -0
  226. package/lib/task/manual-validation-evidence.ts +209 -0
  227. package/lib/task/manual-validation-receipt.ts +156 -0
  228. package/lib/task/manual-validation-shared.ts +50 -0
  229. package/lib/task/manual-validation-transaction.ts +304 -0
  230. package/lib/task/markdown.ts +70 -0
  231. package/lib/task/orchestration.ts +84 -19
  232. package/lib/task/pr-delivery-fact.ts +15 -62
  233. package/lib/task/qualification-audit.ts +22 -33
  234. package/lib/task/recovery-warning.ts +21 -0
  235. package/lib/task/review-artifacts.ts +1 -20
  236. package/lib/task/review-command.ts +24 -0
  237. package/lib/task/review-finalization.ts +183 -292
  238. package/lib/task/review-fingerprint.ts +2 -2
  239. package/lib/task/sections.ts +33 -54
  240. package/lib/task/short-id.ts +27 -5
  241. package/lib/task/task-execution-lock.ts +1 -1
  242. package/lib/task/verification-engine.ts +113 -383
  243. package/lib/task/verification-types.ts +15 -0
  244. package/lib/task/verification.ts +1 -1
  245. package/lib/task/workflow-command.ts +17 -0
  246. package/lib/task/workflow-warning-intents.ts +0 -13
  247. package/lib/task/workflow-warnings.ts +21 -2
  248. package/lib/update.ts +6 -0
  249. package/lib/version.ts +1 -15
  250. package/package.json +6 -6
  251. package/templates/.agents/hooks/lifecycle-delegation.js +6 -6
  252. package/templates/.agents/rules/create-issue.en.md +1 -1
  253. package/templates/.agents/rules/create-issue.zh-CN.md +1 -1
  254. package/templates/.agents/rules/issue-sync.en.md +4 -2
  255. package/templates/.agents/rules/issue-sync.zh-CN.md +4 -3
  256. package/templates/.agents/rules/local-artifact-repair.en.md +46 -39
  257. package/templates/.agents/rules/local-artifact-repair.zh-CN.md +43 -36
  258. package/templates/.agents/rules/testing-discipline.en.md +2 -0
  259. package/templates/.agents/rules/testing-discipline.zh-CN.md +2 -0
  260. package/templates/.agents/scripts/package.json +3 -0
  261. package/templates/.agents/skills/analyze-task/SKILL.en.md +6 -6
  262. package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +6 -6
  263. package/templates/.agents/skills/analyze-task/config/verify.en.json +1 -1
  264. package/templates/.agents/skills/analyze-task/config/verify.zh-CN.json +1 -1
  265. package/templates/.agents/skills/block-task/SKILL.en.md +2 -2
  266. package/templates/.agents/skills/block-task/SKILL.zh-CN.md +2 -2
  267. package/templates/.agents/skills/block-task/config/verify.json +1 -1
  268. package/templates/.agents/skills/cancel-task/SKILL.en.md +3 -3
  269. package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +3 -3
  270. package/templates/.agents/skills/cancel-task/config/verify.json +1 -1
  271. package/templates/.agents/skills/code-task/SKILL.en.md +7 -7
  272. package/templates/.agents/skills/code-task/SKILL.zh-CN.md +7 -7
  273. package/templates/.agents/skills/code-task/config/verify.en.json +1 -1
  274. package/templates/.agents/skills/code-task/config/verify.zh-CN.json +1 -1
  275. package/templates/.agents/skills/commit/reference/issue-metadata-sync.en.md +1 -1
  276. package/templates/.agents/skills/commit/reference/issue-metadata-sync.zh-CN.md +1 -1
  277. package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +33 -21
  278. package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +33 -21
  279. package/templates/.agents/skills/complete-manual-validation/config/verify.en.json +1 -1
  280. package/templates/.agents/skills/complete-manual-validation/config/verify.zh-CN.json +1 -1
  281. package/templates/.agents/skills/complete-manual-validation/reference/report-template.en.md +2 -0
  282. package/templates/.agents/skills/complete-manual-validation/reference/report-template.zh-CN.md +2 -0
  283. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.en.md +16 -7
  284. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.zh-CN.md +16 -7
  285. package/templates/.agents/skills/complete-task/SKILL.en.md +3 -3
  286. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +3 -3
  287. package/templates/.agents/skills/complete-task/config/verify.en.json +2 -2
  288. package/templates/.agents/skills/complete-task/config/verify.zh-CN.json +2 -2
  289. package/templates/.agents/skills/complete-task/reference/external-delivery.en.md +1 -1
  290. package/templates/.agents/skills/complete-task/reference/external-delivery.zh-CN.md +1 -1
  291. package/templates/.agents/skills/create-pr/SKILL.en.md +2 -2
  292. package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +2 -2
  293. package/templates/.agents/skills/create-pr/config/verify.json +1 -1
  294. package/templates/.agents/skills/create-pr/reference/pr-body-template.en.md +2 -2
  295. package/templates/.agents/skills/create-pr/reference/pr-body-template.zh-CN.md +2 -2
  296. package/templates/.agents/skills/create-task/SKILL.en.md +5 -5
  297. package/templates/.agents/skills/create-task/SKILL.zh-CN.md +5 -5
  298. package/templates/.agents/skills/create-task/config/verify.json +1 -1
  299. package/templates/.agents/skills/import-issue/SKILL.en.md +8 -8
  300. package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +8 -8
  301. package/templates/.agents/skills/import-issue/config/verify.json +2 -2
  302. package/templates/.agents/skills/plan-task/SKILL.en.md +4 -4
  303. package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +4 -4
  304. package/templates/.agents/skills/plan-task/config/verify.en.json +1 -1
  305. package/templates/.agents/skills/plan-task/config/verify.zh-CN.json +1 -1
  306. package/templates/.agents/skills/release/SKILL.en.md +7 -3
  307. package/templates/.agents/skills/release/SKILL.zh-CN.md +7 -3
  308. package/templates/.agents/skills/restore-task/SKILL.en.md +2 -2
  309. package/templates/.agents/skills/restore-task/SKILL.zh-CN.md +2 -2
  310. package/templates/.agents/skills/restore-task/config/verify.json +1 -1
  311. package/templates/.agents/skills/review-analysis/SKILL.en.md +2 -2
  312. package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +2 -2
  313. package/templates/.agents/skills/review-analysis/config/verify.en.json +1 -1
  314. package/templates/.agents/skills/review-analysis/config/verify.zh-CN.json +1 -1
  315. package/templates/.agents/skills/review-analysis/reference/report-template.en.md +7 -7
  316. package/templates/.agents/skills/review-analysis/reference/report-template.zh-CN.md +6 -6
  317. package/templates/.agents/skills/review-code/SKILL.en.md +2 -2
  318. package/templates/.agents/skills/review-code/SKILL.zh-CN.md +2 -2
  319. package/templates/.agents/skills/review-code/config/verify.en.json +1 -1
  320. package/templates/.agents/skills/review-code/config/verify.zh-CN.json +1 -1
  321. package/templates/.agents/skills/review-code/reference/report-template.en.md +12 -12
  322. package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +12 -12
  323. package/templates/.agents/skills/review-plan/SKILL.en.md +2 -2
  324. package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +2 -2
  325. package/templates/.agents/skills/review-plan/config/verify.en.json +1 -1
  326. package/templates/.agents/skills/review-plan/config/verify.zh-CN.json +1 -1
  327. package/templates/.agents/skills/review-plan/reference/report-template.en.md +7 -7
  328. package/templates/.agents/skills/review-plan/reference/report-template.zh-CN.md +7 -7
  329. package/templates/.agents/skills/review-pr/config/verify.en.json +1 -1
  330. package/templates/.agents/skills/review-pr/config/verify.zh-CN.json +1 -1
  331. package/templates/.agents/skills/review-pr/reference/evidence-grading.en.md +3 -3
  332. package/templates/.agents/skills/review-pr/reference/evidence-grading.zh-CN.md +3 -3
  333. package/templates/.agents/skills/run-manual-validation/SKILL.en.md +20 -3
  334. package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +20 -3
  335. package/templates/.agents/skills/run-manual-validation/config/verify.en.json +1 -1
  336. package/templates/.agents/skills/run-manual-validation/config/verify.zh-CN.json +1 -1
  337. package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.en.md +4 -1
  338. package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.zh-CN.md +4 -1
  339. package/templates/.agents/skills/run-manual-validation/reference/report-template.en.md +2 -0
  340. package/templates/.agents/skills/run-manual-validation/reference/report-template.zh-CN.md +2 -0
  341. package/templates/.agents/skills/run-task/SKILL.en.md +1 -1
  342. package/templates/.agents/skills/run-task/SKILL.zh-CN.md +1 -1
  343. package/templates/.agents/skills/test/SKILL.en.md +17 -6
  344. package/templates/.agents/skills/test/SKILL.zh-CN.md +17 -6
  345. package/templates/.agents/templates/task.en.md +1 -1
  346. package/templates/.agents/templates/task.zh-CN.md +1 -1
  347. package/templates/.claude/commands/import-issue.en.md +1 -1
  348. package/templates/.claude/commands/import-issue.zh-CN.md +1 -1
  349. package/templates/.claude/commands/restore-task.en.md +1 -1
  350. package/templates/.claude/commands/restore-task.zh-CN.md +1 -1
  351. package/templates/.claude/commands/run-manual-validation.en.md +1 -1
  352. package/templates/.claude/commands/run-manual-validation.zh-CN.md +1 -1
  353. package/dist/lib/platform/adapters.js +0 -56
  354. package/lib/platform/adapters.ts +0 -197
@@ -1,3 +1,5 @@
1
+ import { finalizationTerminalResponse } from './finalization-response.js';
2
+ import { isDeepStrictEqual } from 'node:util';
1
3
  import { completedReentryView } from './completed-reentry.js';
2
4
  import fs from 'node:fs';
3
5
  import path from 'node:path';
@@ -6,65 +8,126 @@ import { getProcessStartTime } from '../../server/process-state.js';
6
8
  import { assertGitWorktreeBinding } from '../../git/worktree-identity.js';
7
9
  import { controlError, DEFAULT_SANDBOX_CONTROL_TIMING, SANDBOX_CONTROL_MAX_RESPONSE_BYTES, SANDBOX_CONTROL_MAX_TERMINAL_RECORD_BYTES } from './protocol.js';
8
10
  import { prepareSandboxControlExecution } from './executor.js';
9
- import { appendSandboxControlAudit, atomicWriteJsonNoReplace, createSandboxControlPayload, cleanupStaleSandboxControlLease, executionPath, readJsonFile, readActiveLease, readExecution, readSandboxControlPayload, readSandboxControlResultEvidence, sandboxControlEncodedJsonBytes, sandboxControlGenerationUsage, sanitizeSandboxControlOutput, sanitizeSandboxControlResult, payloadPath, writeSandboxControlPayload, writeSandboxControlReservation, writeSandboxControlResultEvidence, terminateSandboxControlExecution, writeSandboxControlStatus, readSandboxControlStatus } from './state.js';
11
+ import { atomicWriteJsonNoReplace, createSandboxControlPayload, cleanupStaleSandboxControlLease, executionPath, readJsonFile, readActiveLease, readExecution, readSandboxControlPayload, readSandboxControlResultEvidence, readSandboxControlTerminalResult, createSandboxControlTerminalResult, sandboxControlEncodedJsonBytes, sandboxControlGenerationUsage, sanitizeSandboxControlOutput, sanitizeSandboxControlResult, payloadPath, writeSandboxControlPayload, writeSandboxControlReservation, writeSandboxControlResultEvidence, writeSandboxControlTerminalResult, appendSandboxControlAudit, terminateSandboxControlExecution, writeSandboxControlStatus, readSandboxControlStatus } from './state.js';
10
12
  import { validateSandboxControlRequest } from './protocol.js';
13
+ import { requestAuditFields, resultAuditFields, appendCriticalAudit, appendDiagnosticAudit, createSandboxControlAuditContext, writeSandboxControlTransition, readSandboxControlTransition } from './audit.js';
14
+ import { parseTaskControlOperation } from '../../task/control-authority.js';
15
+ import { parseControlOutput, classifySandboxControlRecovery, findSandboxControlRecoveryOperation, operationRecoveryBinding, SANDBOX_CONTROL_REQUIRED_COMPLETION_PHASES } from '../../task/control-recovery.js';
16
+ import { readRun } from '../../task/orchestration.js';
17
+ import { readLifecycleRecoveryDomainEvidence } from '../../task/lifecycle-recovery.js';
18
+ import { captureRepositorySnapshot } from '../../task/workspace-snapshot.js';
19
+ import { parseTypedTaskFrontmatter } from '../../task/frontmatter.js';
20
+ import { readLifecycleJournalEvidence } from '../../task/lifecycle.js';
21
+ import { resolveTaskRef } from '../../task/resolve-ref.js';
22
+ import { loadShortIdByTaskId } from '../../task/short-id.js';
11
23
  import { inspectSandboxControlContainer } from './container-identity.js';
12
24
  import { acquireSandboxControlBrokerStartup, garbageCollectSandboxControlRoot, isSandboxControlRootQuiescing, readSandboxControlManifest } from './lifecycle.js';
13
25
  import { nextSandboxControlBackoff } from './timing.js';
14
26
  import { readTaskFinalizationReceipt } from '../../task/finalization.js';
15
- import { resolveTaskRef } from '../../task/resolve-ref.js';
27
+ import { readCodexControllerRegistration } from './controller-registration.js';
28
+ import { validateSandboxControlIdentity } from './identity-sentinel.js';
16
29
  import { mergeSandboxTaskView, taskViewAfterFinalization, taskViewForManifest } from './task-view.js';
17
30
  import { taskCreateOutputUnavailableResult } from '../../task/create-service.js';
18
- function safeRealpath(value) {
31
+ import { consumeLifecycleRecoveryAttestation, issueLifecycleRecoveryAttestation, recoverLifecycleRecoveryOperation } from '../../task/control-authority.js';
32
+ import { createCodexCapabilityStore } from '../../agent-clients/adapters/codex-lifecycle/capability-store.js';
33
+ import { computeLifecycleBuildIdentity } from '../../agent-clients/adapters/codex-lifecycle/build-identity.js';
34
+ function consumeLifecycleAuthorityForResult(manifest, execution) {
35
+ const authority = execution.lifecycleAuthority?.attestation;
36
+ if (!authority || execution.result?.exitCode !== 0)
37
+ return true;
38
+ try {
39
+ consumeLifecycleRecoveryAttestation(authority);
40
+ return true;
41
+ }
42
+ catch (error) {
43
+ appendBrokerAudit(manifest, 'lifecycle-authority-consume-retry', {
44
+ ...requestAuditFields(manifest, execution.request),
45
+ errorCode: error instanceof Error ? error.name : 'LIFECYCLE_AUTHORITY_CONSUME_FAILED'
46
+ });
47
+ return false;
48
+ }
49
+ }
50
+ function lifecycleAuthorityForRequest(manifest, manifestPath, request) {
51
+ const selector = request.family === 'task-workflow' || request.family === 'task-orchestration'
52
+ ? request.authority
53
+ : undefined;
54
+ if (!selector)
55
+ return undefined;
56
+ let controllerBinding = null;
19
57
  try {
20
- return fs.realpathSync.native(value);
58
+ const registration = readCodexControllerRegistration(manifestPath);
59
+ controllerBinding = {
60
+ instanceDigest: registration.controllerInstanceDigest,
61
+ controlGeneration: registration.controlGeneration
62
+ };
21
63
  }
22
64
  catch {
23
- return null;
65
+ // The issuer reports the stable unavailable result; the broker never trusts
66
+ // a controller binding supplied by the request.
24
67
  }
68
+ return issueLifecycleRecoveryAttestation(selector, {
69
+ controllerBinding,
70
+ capabilityStore: createCodexCapabilityStore(),
71
+ buildIdentity: computeLifecycleBuildIdentity(manifest.repoRoot)
72
+ });
25
73
  }
26
74
  function appendBrokerAudit(manifest, event, fields = {}) {
75
+ appendDiagnosticAudit(manifest, event, { source: 'broker', ...fields });
76
+ }
77
+ function operationKey(request, output) {
78
+ if (request.family === 'task-finalization')
79
+ return request.operation;
80
+ if (request.family === 'task-create')
81
+ return 'create';
82
+ if (request.family === 'codex-controller')
83
+ return request.command;
84
+ if (request.family === 'task-workflow')
85
+ return request.workflow.operation;
86
+ if (request.family !== 'task-lifecycle' && request.family !== 'task-orchestration')
87
+ return null;
88
+ if (request.family === 'task-orchestration' && request.args[1] === 'route') {
89
+ if (!output)
90
+ return 'route';
91
+ const result = parseControlOutput(output);
92
+ return result?.changed === true && result.status === 'completed' ? 'route.clean-completion' : 'route.read';
93
+ }
27
94
  try {
28
- appendSandboxControlAudit(manifest, event, { source: 'broker', ...fields });
95
+ const operation = parseTaskControlOperation(request.family, request.args);
96
+ return operation.family === 'task-orchestration' ? operation.intent : operation.request.intent;
29
97
  }
30
98
  catch {
31
- // Diagnostics must never change the control protocol.
99
+ return null;
32
100
  }
33
101
  }
34
- function requestAuditFields(manifest, manifestPath, request) {
35
- const args = 'args' in request ? request.args : [];
36
- const encodedArgs = JSON.stringify(args);
37
- return {
38
- requestId: request.id,
39
- requestFamily: request.family,
40
- sandboxTaskId: manifest.taskId,
41
- requestGeneration: request.generation,
42
- requestIssuedAt: request.issuedAt,
43
- requestExpiresAt: request.expiresAt,
44
- requestArgCount: args.length,
45
- requestArgsSha256: createHash('sha256').update(encodedArgs, 'utf8').digest('hex'),
46
- requestTaskRef: args[0] ?? null,
47
- requestCommand: args[1] ?? null,
48
- controllerProofPresent: request.controllerProof !== null,
49
- hostCwd: process.cwd(),
50
- manifestPath,
51
- manifestPathRealpath: safeRealpath(manifestPath),
52
- repoRoot: manifest.repoRoot,
53
- repoRootRealpath: safeRealpath(manifest.repoRoot),
54
- worktreeRoot: manifest.worktreeRoot,
55
- worktreeRootRealpath: safeRealpath(manifest.worktreeRoot),
56
- runtimeDir: manifest.runtimeDir,
57
- runtimeDirRealpath: safeRealpath(manifest.runtimeDir)
58
- };
102
+ function recoveryOperationKey(request, terminalResult, output) {
103
+ const operation = operationKey(request, output);
104
+ if (operation !== 'route')
105
+ return operation;
106
+ const digest = terminalResult?.intentDigest;
107
+ for (const candidate of ['route.read', 'route.clean-completion']) {
108
+ const expected = operationRecoveryBinding(request.id, request.generation, null, request.family, candidate).intentDigest;
109
+ if (digest === expected)
110
+ return candidate;
111
+ }
112
+ return null;
59
113
  }
60
- function resultAuditFields(result) {
61
- return {
62
- exitCode: result.exitCode,
63
- stdoutBytes: Buffer.byteLength(result.stdout, 'utf8'),
64
- stderrBytes: Buffer.byteLength(result.stderr, 'utf8'),
65
- stdoutSha256: createHash('sha256').update(result.stdout, 'utf8').digest('hex'),
66
- stderrSha256: createHash('sha256').update(result.stderr, 'utf8').digest('hex')
67
- };
114
+ function terminalResultMatchesRequest(request, terminalResult) {
115
+ const operation = operationKey(request);
116
+ const candidates = operation === 'route'
117
+ ? ['route.read', 'route.clean-completion']
118
+ : operation ? [operation] : [];
119
+ return candidates.some((candidate) => createHash('sha256').update(`${request.family}\0${candidate}`, 'utf8').digest('hex') === terminalResult.intentDigest);
120
+ }
121
+ function criticalRequestPhase(manifest, request, phase, outcome, reference) {
122
+ const context = createSandboxControlAuditContext(manifest, {
123
+ requestId: request.id,
124
+ family: request.family,
125
+ operation: operationKey(request),
126
+ phase,
127
+ outcome
128
+ });
129
+ appendCriticalAudit(manifest, context, { reference: reference ?? null });
130
+ writeSandboxControlTransition(manifest, { requestId: request.id, phase, reference });
68
131
  }
69
132
  function assertRealDirectory(directory, parent) {
70
133
  const stat = fs.lstatSync(directory);
@@ -146,33 +209,7 @@ function finalizationRecoveryResponse(manifest, requestId, exitCode) {
146
209
  catch {
147
210
  return { status: 'deferred' };
148
211
  }
149
- const pendingSteps = [
150
- receipt.taskComment === 'pending' ? 'task-comment' : null,
151
- receipt.verification === 'pending' ? 'verification' : null
152
- ].filter((step) => step !== null);
153
- const completedSteps = ['lifecycle', receipt.taskComment === 'pending' ? null : 'task-comment', receipt.verification === 'pending' ? null : 'verification']
154
- .filter((step) => step !== null);
155
- const warnings = receipt.warnings
156
- .filter((warning) => warning.status === 'open')
157
- .map(({ status: _status, resolvedAt: _resolvedAt, ...warning }) => warning);
158
- const result = {
159
- status: 'completed', changed: false, taskId: manifest.taskId,
160
- lifecycle: { status: 'no-op', changed: false, error: null },
161
- taskComment: receipt.taskComment === 'pending' ? null : { status: 'no-op', changed: false, error: null },
162
- verification: receipt.verification === 'pending' ? null : { status: 'no-op', changed: false, error: null },
163
- completedSteps, pendingSteps,
164
- result: pendingSteps.length > 0 || receipt.warningProjection === 'pending' || warnings.length > 0
165
- ? 'completed_with_warnings' : 'completed',
166
- warnings, error: null
167
- };
168
- return {
169
- status: 'matched',
170
- response: {
171
- version: 2, id: requestId, phase: 'completed', exitCode: 0,
172
- stdout: `${JSON.stringify({ version: 1, status: 'completed', changed: false, accepted: true, result, error: null })}\n`,
173
- stderr: '', error: null
174
- }
175
- };
212
+ return { status: 'matched', response: finalizationTerminalResponse(manifest.taskId, requestId, receipt) };
176
213
  }
177
214
  function publishFinalizationTaskView(manifest, broker, requestId, state, reasonCode, activeRequestId) {
178
215
  let view;
@@ -220,7 +257,7 @@ function outputMatchesEvidence(output, bytes, sha256) {
220
257
  return Buffer.byteLength(output, 'utf8') === bytes
221
258
  && createHash('sha256').update(output, 'utf8').digest('hex') === sha256;
222
259
  }
223
- function genericRecoveryResponse(request, exitCode, payload, cause = 'recovery') {
260
+ export function genericRecoveryResponse(request, exitCode, payload, cause = 'recovery', warning = null) {
224
261
  const outputUnavailable = request.family === 'task-create' && !payload;
225
262
  return {
226
263
  version: 2,
@@ -228,24 +265,271 @@ function genericRecoveryResponse(request, exitCode, payload, cause = 'recovery')
228
265
  phase: 'completed',
229
266
  exitCode,
230
267
  stdout: outputUnavailable ? `${JSON.stringify(taskCreateOutputUnavailableResult(request.id))}\n` : '',
231
- stderr: payload ? '' : cause === 'publish'
232
- ? 'SANDBOX_CONTROL_OUTPUT_UNAVAILABLE: output payload was not retained\n'
233
- : 'SANDBOX_CONTROL_OUTPUT_UNAVAILABLE: broker restarted after executor completion\n',
268
+ stderr: payload ? '' : warning
269
+ ? `${warning.code}: ${warning.message}\nAction: ${warning.action}\n`
270
+ : cause === 'publish'
271
+ ? 'SANDBOX_CONTROL_OUTPUT_UNAVAILABLE: output payload was not retained\n'
272
+ : 'SANDBOX_CONTROL_OUTPUT_UNAVAILABLE: broker restarted after executor completion\n',
234
273
  error: null,
235
274
  outputState: payload ? 'available' : 'unavailable',
236
275
  payload: payload ? payloadReference(payload) : null
237
276
  };
238
277
  }
239
- function recoveryResponse(manifest, request, evidence, payload) {
278
+ function taskCreateDomainEvidence(manifest, output) {
279
+ if (!output || !['applied', 'no-op', 'degraded'].includes(String(output.status))) {
280
+ return { consistent: false };
281
+ }
282
+ const task = output.task && typeof output.task === 'object' && !Array.isArray(output.task)
283
+ ? output.task : null;
284
+ const taskId = typeof task?.id === 'string' ? task.id : null;
285
+ const shortId = typeof task?.shortId === 'string' ? task.shortId : null;
286
+ if (!taskId || !shortId)
287
+ return { consistent: false };
288
+ try {
289
+ const resolved = resolveTaskRef(taskId, { repoRoot: manifest.repoRoot });
290
+ const shortIds = loadShortIdByTaskId(manifest.repoRoot);
291
+ return {
292
+ consistent: resolved.ok && resolved.taskId === taskId && resolved.state === 'active' && shortIds.get(taskId) === shortId,
293
+ taskId,
294
+ shortId
295
+ };
296
+ }
297
+ catch {
298
+ return { consistent: false };
299
+ }
300
+ }
301
+ function controllerDomainEvidence(manifest, manifestPath, request, output) {
302
+ if (!output || output.error !== null || typeof output.status !== 'string')
303
+ return { consistent: false };
304
+ try {
305
+ const registration = readCodexControllerRegistration(manifestPath);
306
+ if (request.family !== 'codex-controller')
307
+ return { consistent: false };
308
+ if (request.command === 'open') {
309
+ const lease = output.lease && typeof output.lease === 'object' && !Array.isArray(output.lease)
310
+ ? output.lease : null;
311
+ return {
312
+ consistent: output.status === 'opened' && output.changed === true
313
+ && lease?.taskId === registration.taskId
314
+ && lease.controlGeneration === registration.controlGeneration
315
+ && lease.controllerInstanceDigest === registration.controllerInstanceDigest
316
+ && registration.taskId === manifest.taskId
317
+ && registration.controlGeneration === manifest.generation
318
+ };
319
+ }
320
+ if (request.command === 'verify') {
321
+ const binding = output.binding && typeof output.binding === 'object' && !Array.isArray(output.binding)
322
+ ? output.binding : null;
323
+ return {
324
+ consistent: output.status === 'verified' && output.changed === false
325
+ && binding?.taskId === registration.taskId
326
+ && binding.controlGeneration === registration.controlGeneration
327
+ && binding.controllerInstanceDigest === registration.controllerInstanceDigest
328
+ };
329
+ }
330
+ return { consistent: false };
331
+ }
332
+ catch (error) {
333
+ return { consistent: request.family === 'codex-controller' && request.command === 'close'
334
+ && error.code === 'CODEX_SANDBOX_CONTROLLER_REGISTRATION_MISSING'
335
+ && output?.status === 'closed' && typeof output.changed === 'boolean' };
336
+ }
337
+ }
338
+ function orchestrationDomainEvidence(operation, terminalResult, output, run) {
339
+ if (!operation || !output)
340
+ return { consistent: false };
341
+ const outputRun = output.run && typeof output.run === 'object' && !Array.isArray(output.run)
342
+ ? output.run : null;
343
+ const runMatches = outputRun === null ? run === null : run !== null && JSON.stringify(run) === JSON.stringify(outputRun);
344
+ if (operation.class === 'read-only') {
345
+ return {
346
+ consistent: output.status === terminalResult.status && output.changed === terminalResult.changed && runMatches,
347
+ snapshotValid: true,
348
+ status: run?.status ?? terminalResult.status,
349
+ pendingDelegation: run?.pendingDelegation ?? null
350
+ };
351
+ }
352
+ if (!run || !outputRun)
353
+ return { consistent: false };
354
+ return {
355
+ consistent: output.status === terminalResult.status && output.changed === terminalResult.changed && runMatches,
356
+ status: run.status,
357
+ pendingDelegation: run.pendingDelegation
358
+ };
359
+ }
360
+ function readRecoveryDomain(manifest, manifestPath, request, operation, terminalResult, payloadOutput) {
361
+ if (!operation)
362
+ return { domain: null };
363
+ const taskRef = request.family === 'task-finalization' || request.family === 'task-workflow'
364
+ ? manifest.taskId
365
+ : 'args' in request ? request.args[0] ?? null : manifest.taskId;
366
+ const output = parseControlOutput(payloadOutput);
367
+ if (!taskRef && operation.family !== 'task-create' && operation.family !== 'codex-controller') {
368
+ return { domain: null };
369
+ }
370
+ if (operation.family === 'task-lifecycle') {
371
+ if (operation.intent === 'recover-started') {
372
+ try {
373
+ if (!('args' in request))
374
+ return { domain: null };
375
+ const parsed = parseTaskControlOperation('task-lifecycle', request.args);
376
+ if (parsed.family !== 'task-lifecycle' || parsed.request.intent !== 'recover-started') {
377
+ return { domain: null };
378
+ }
379
+ return {
380
+ domain: readLifecycleRecoveryDomainEvidence(manifest.repoRoot, parsed.request, terminalResult),
381
+ journal: readLifecycleJournalEvidence(manifest.repoRoot, taskRef)
382
+ };
383
+ }
384
+ catch {
385
+ return { domain: null };
386
+ }
387
+ }
388
+ if (!taskRef || !terminalResult.targetState)
389
+ return { domain: null };
390
+ const journal = readLifecycleJournalEvidence(manifest.repoRoot, taskRef);
391
+ try {
392
+ const resolved = resolveTaskRef(taskRef, { repoRoot: manifest.repoRoot });
393
+ const shortIds = loadShortIdByTaskId(manifest.repoRoot);
394
+ const shortIdMatches = terminalResult.targetState === 'active'
395
+ ? shortIds.has(resolved.ok ? resolved.taskId : taskRef)
396
+ : !shortIds.has(resolved.ok ? resolved.taskId : taskRef);
397
+ return {
398
+ domain: { consistent: resolved.ok && resolved.state === terminalResult.targetState && shortIdMatches },
399
+ journal
400
+ };
401
+ }
402
+ catch {
403
+ return { domain: { consistent: false }, journal };
404
+ }
405
+ }
406
+ if (operation.family === 'task-orchestration') {
407
+ if (!taskRef)
408
+ return { domain: null };
409
+ try {
410
+ const resolved = resolveTaskRef(taskRef, { repoRoot: manifest.repoRoot });
411
+ if (!resolved.ok)
412
+ return { domain: { consistent: false } };
413
+ const run = readRun(resolved.taskDir);
414
+ if (!run && operation.class !== 'read-only')
415
+ return { domain: { consistent: false } };
416
+ if (operation.class === 'route.clean-completion') {
417
+ const snapshot = captureRepositorySnapshot(manifest.repoRoot);
418
+ const metadata = parseTypedTaskFrontmatter(fs.readFileSync(resolved.taskMdPath, 'utf8'));
419
+ const completion = run?.completionEvidence ?? null;
420
+ const consistent = run !== null && completion !== null
421
+ && terminalResult.completionEvidence !== null
422
+ && isDeepStrictEqual(completion, terminalResult.completionEvidence);
423
+ return {
424
+ domain: {
425
+ consistent,
426
+ status: run?.status ?? null,
427
+ pendingDelegation: run?.pendingDelegation ?? null,
428
+ completionEvidence: completion,
429
+ snapshot,
430
+ lastReviewedCommit: metadata.last_reviewed_commit
431
+ }
432
+ };
433
+ }
434
+ return { domain: orchestrationDomainEvidence(operation, terminalResult, output, run) };
435
+ }
436
+ catch {
437
+ return { domain: { consistent: false } };
438
+ }
439
+ }
440
+ if (operation.family === 'task-create') {
441
+ return { domain: taskCreateDomainEvidence(manifest, output) };
442
+ }
443
+ if (operation.family === 'codex-controller') {
444
+ return { domain: controllerDomainEvidence(manifest, manifestPath, request, output) };
445
+ }
446
+ if (operation.family === 'task-workflow') {
447
+ return {
448
+ domain: { consistent: output?.status === terminalResult.status && output.changed === terminalResult.changed, snapshotValid: true }
449
+ };
450
+ }
451
+ return { domain: null };
452
+ }
453
+ function readCommittedCriticalPhases(manifest, requestId) {
454
+ return SANDBOX_CONTROL_REQUIRED_COMPLETION_PHASES.filter((phase) => {
455
+ try {
456
+ readSandboxControlTransition(manifest, requestId, phase);
457
+ return true;
458
+ }
459
+ catch {
460
+ return false;
461
+ }
462
+ });
463
+ }
464
+ export function recoveryResponse(manifest, manifestPath, request, evidence, payload, terminalResult) {
465
+ const operationName = recoveryOperationKey(request, terminalResult, payload?.stdout);
466
+ const operation = operationName ? findSandboxControlRecoveryOperation(request.family, operationName) : null;
467
+ if (!operation)
468
+ return null;
469
+ if (payload) {
470
+ const payloadTerminal = createSandboxControlTerminalResult(manifest, {
471
+ id: request.id, family: request.family, operation: operationName
472
+ }, payload.stdout);
473
+ if (JSON.stringify(payloadTerminal) !== JSON.stringify(terminalResult))
474
+ return unknown(request.id);
475
+ }
476
+ const finalization = request.family === 'task-finalization' ? finalizationRecoveryResponse(manifest, request.id, 0) : null;
477
+ if (evidence.exitCode === 0 && finalization?.status === 'deferred')
478
+ return null;
479
+ if (evidence.exitCode === 0 && request.family === 'task-workflow' && request.authority?.phase === 'task-event.completed') {
480
+ try {
481
+ const registration = readCodexControllerRegistration(manifestPath);
482
+ const recovered = recoverLifecycleRecoveryOperation(request.authority, {
483
+ repoRoot: manifest.repoRoot,
484
+ capabilityStore: createCodexCapabilityStore(),
485
+ buildIdentity: computeLifecycleBuildIdentity(manifest.repoRoot),
486
+ controllerBinding: {
487
+ instanceDigest: registration.controllerInstanceDigest,
488
+ controlGeneration: registration.controlGeneration
489
+ }
490
+ });
491
+ if (recovered.status !== 'committed')
492
+ return null;
493
+ }
494
+ catch (error) {
495
+ appendBrokerAudit(manifest, 'lifecycle-authority-recovery-failed', {
496
+ ...requestAuditFields(manifest, request),
497
+ errorCode: error instanceof Error ? error.name : 'LIFECYCLE_AUTHORITY_RECOVERY_FAILED'
498
+ });
499
+ return null;
500
+ }
501
+ }
502
+ const recovery = finalization
503
+ ? { domain: { consistent: finalization.status === 'matched' } }
504
+ : readRecoveryDomain(manifest, manifestPath, request, operation, terminalResult, payload?.stdout ?? null);
505
+ const binding = operationRecoveryBinding(request.id, manifest.generation, manifest.taskId, request.family, operationName);
506
+ const decision = classifySandboxControlRecovery({
507
+ operation,
508
+ binding,
509
+ startedCommitted: true,
510
+ terminalResult,
511
+ criticalPhases: readCommittedCriticalPhases(manifest, request.id),
512
+ domain: recovery.domain,
513
+ journal: recovery.journal
514
+ });
515
+ if (decision.outcome === 'unknown' || decision.outcome === 'in-progress')
516
+ return unknown(request.id);
517
+ if (decision.outcome === 'not-executed')
518
+ return notExecuted(request.id);
519
+ if (decision.outcome !== 'success' && decision.outcome !== 'failure')
520
+ return null;
240
521
  if (request.family === 'task-finalization' && evidence.exitCode === 0) {
241
- const finalization = finalizationRecoveryResponse(manifest, request.id, evidence.exitCode);
242
- return finalization.status === 'matched' ? finalization.response ?? null : null;
522
+ if (finalization?.status !== 'matched')
523
+ return null;
524
+ return finalization.response ?? null;
243
525
  }
244
- return genericRecoveryResponse(request, evidence.exitCode, payload);
526
+ return genericRecoveryResponse(request, evidence.exitCode, payload, 'recovery', terminalResult.warning ?? null);
245
527
  }
246
- function terminalMatchesEvidence(manifest, request, response, evidence, payload, payloadInvalid) {
528
+ function terminalMatchesEvidence(manifest, manifestPath, request, response, evidence, payload, payloadInvalid, terminalResult) {
247
529
  if (request.family === 'task-finalization' && evidence.exitCode === 0) {
248
- const expected = recoveryResponse(manifest, request, evidence, payload);
530
+ if (!terminalResult)
531
+ return { valid: false, payloadReferenced: false };
532
+ const expected = recoveryResponse(manifest, manifestPath, request, evidence, payload, terminalResult);
249
533
  return {
250
534
  valid: expected !== null && JSON.stringify(response) === JSON.stringify(expected),
251
535
  payloadReferenced: false
@@ -274,7 +558,7 @@ function terminalMatchesEvidence(manifest, request, response, evidence, payload,
274
558
  if (response.outputState === 'unavailable') {
275
559
  const causes = ['recovery', 'publish'];
276
560
  const valid = causes.some((cause) => JSON.stringify(response)
277
- === JSON.stringify(genericRecoveryResponse(request, evidence.exitCode, null, cause)));
561
+ === JSON.stringify(genericRecoveryResponse(request, evidence.exitCode, null, cause, terminalResult?.warning ?? null)));
278
562
  return { valid, payloadReferenced: false };
279
563
  }
280
564
  return {
@@ -286,6 +570,16 @@ function terminalMatchesEvidence(manifest, request, response, evidence, payload,
286
570
  }
287
571
  function publishExecutionResult(manifest, request, result, broker, brokerOwns) {
288
572
  const normalized = sanitizeSandboxControlResult(manifest, result);
573
+ const terminalResult = writeSandboxControlTerminalResult(manifest, {
574
+ id: request.id,
575
+ family: request.family,
576
+ operation: operationKey(request, normalized.stdout)
577
+ }, normalized.stdout);
578
+ if (fs.existsSync(path.join(manifest.processingDir, request.id, 'transitions'))) {
579
+ criticalRequestPhase(manifest, request, 'completed', normalized.exitCode === 0 ? 'success' : 'failure');
580
+ criticalRequestPhase(manifest, request, 'evidence-written', normalized.exitCode === 0 ? 'success' : 'failure');
581
+ criticalRequestPhase(manifest, request, 'publish-authorized', normalized.exitCode === 0 ? 'success' : 'failure');
582
+ }
289
583
  let terminal = null;
290
584
  if (request.family === 'task-finalization') {
291
585
  const finalization = finalizationRecoveryResponse(manifest, request.id, normalized.exitCode);
@@ -316,7 +610,7 @@ function publishExecutionResult(manifest, request, result, broker, brokerOwns) {
316
610
  catch {
317
611
  payload = null;
318
612
  }
319
- terminal = genericRecoveryResponse(request, normalized.exitCode, payload, 'publish');
613
+ terminal = genericRecoveryResponse(request, normalized.exitCode, payload, 'publish', terminalResult.warning ?? null);
320
614
  }
321
615
  }
322
616
  if (!brokerOwns())
@@ -326,7 +620,11 @@ function publishExecutionResult(manifest, request, result, broker, brokerOwns) {
326
620
  if (view.state === 'unknown')
327
621
  return false;
328
622
  }
329
- return writeSandboxControlResponse(manifest, terminal);
623
+ const committed = writeSandboxControlResponse(manifest, terminal);
624
+ if (committed && fs.existsSync(path.join(manifest.processingDir, request.id, 'transitions'))) {
625
+ criticalRequestPhase(manifest, request, 'published-committed', normalized.exitCode === 0 ? 'success' : 'failure');
626
+ }
627
+ return committed;
330
628
  }
331
629
  function writeAcceptedResponse(manifest, response) {
332
630
  atomicWriteJsonNoReplace(acceptedResponsePath(manifest, response.id), response);
@@ -386,7 +684,20 @@ function bindingReason(manifest) {
386
684
  return 'SANDBOX_WORKTREE_BINDING_LOST';
387
685
  }
388
686
  }
389
- function recoverProcessing(manifest, broker, brokerOwns) {
687
+ function assertCurrentSandboxControlIdentity(manifest, manifestPath) {
688
+ const result = validateSandboxControlIdentity({
689
+ publicStatusDir: manifest.publicStatusDir,
690
+ root: path.dirname(path.resolve(manifestPath)),
691
+ mode: manifest.mode,
692
+ taskId: manifest.taskId,
693
+ generation: manifest.generation,
694
+ controlRootId: manifest.controlRootId
695
+ });
696
+ if (result.state !== 'valid') {
697
+ throw new Error(`SANDBOX_CONTROL_IDENTITY_${result.state.replaceAll('-', '_').toUpperCase()}`);
698
+ }
699
+ }
700
+ function recoverProcessing(manifest, manifestPath, broker, brokerOwns) {
390
701
  for (const entry of fs.readdirSync(manifest.processingDir, { withFileTypes: true })) {
391
702
  if (!entry.isDirectory() || !/^[a-f0-9-]{16,64}$/.test(entry.name))
392
703
  continue;
@@ -395,14 +706,19 @@ function recoverProcessing(manifest, broker, brokerOwns) {
395
706
  const descriptor = executionPath(manifest, entry.name);
396
707
  const existingResponse = responsePath(manifest, entry.name);
397
708
  const processingDirectory = path.join(manifest.processingDir, entry.name);
709
+ const transitionsDirectory = path.join(processingDirectory, 'transitions');
710
+ const transitionProtocolActive = fs.existsSync(transitionsDirectory);
711
+ const startedCommitted = fs.existsSync(path.join(transitionsDirectory, 'started-committed.json'));
398
712
  let payloadReferenced = false;
399
713
  if (fs.existsSync(descriptor)) {
400
714
  const execution = readExecution(descriptor);
401
715
  let resultEvidence = null;
716
+ let terminalResult = null;
402
717
  let payload = null;
403
718
  let payloadInvalid = false;
404
719
  let request = null;
405
720
  let existingTerminalResponse = null;
721
+ let terminalReconciled = false;
406
722
  try {
407
723
  const rawRequest = JSON.parse(fs.readFileSync(path.join(processingDirectory, 'request.json'), 'utf8'));
408
724
  request = validateSandboxControlRequest(rawRequest, manifest, {
@@ -424,6 +740,19 @@ function recoverProcessing(manifest, broker, brokerOwns) {
424
740
  resultEvidence = null;
425
741
  }
426
742
  }
743
+ const terminalResultPathValue = path.join(processingDirectory, 'terminal-result.json');
744
+ if (fs.existsSync(terminalResultPathValue)) {
745
+ try {
746
+ terminalResult = readSandboxControlTerminalResult(terminalResultPathValue);
747
+ if (terminalResult.requestId !== entry.name || terminalResult.generation !== manifest.generation
748
+ || !request || !terminalResultMatchesRequest(request, terminalResult)) {
749
+ terminalResult = null;
750
+ }
751
+ }
752
+ catch {
753
+ terminalResult = null;
754
+ }
755
+ }
427
756
  const publishedPayloadPath = payloadPath(manifest, entry.name);
428
757
  if (fs.existsSync(publishedPayloadPath)) {
429
758
  try {
@@ -437,12 +766,24 @@ function recoverProcessing(manifest, broker, brokerOwns) {
437
766
  payloadInvalid = true;
438
767
  }
439
768
  }
769
+ if (transitionProtocolActive && startedCommitted && !terminalResult) {
770
+ if (!brokerOwns())
771
+ return false;
772
+ if (!terminateSandboxControlExecution(execution)) {
773
+ throw new Error(`SANDBOX_CONTROL_EXECUTION_STILL_RUNNING: ${entry.name}`);
774
+ }
775
+ appendDiagnosticAudit(manifest, 'orphan-tree-terminated', { requestId: entry.name });
776
+ writeSandboxControlResponse(manifest, unknown(entry.name));
777
+ continue;
778
+ }
779
+ if (transitionProtocolActive && !startedCommitted && terminalResult)
780
+ continue;
440
781
  if (!terminateSandboxControlExecution(execution)) {
441
782
  throw new Error(`SANDBOX_CONTROL_EXECUTION_STILL_RUNNING: ${entry.name}`);
442
783
  }
443
784
  if (!brokerOwns())
444
785
  return false;
445
- appendSandboxControlAudit(manifest, 'orphan-tree-terminated', { requestId: entry.name });
786
+ appendDiagnosticAudit(manifest, 'orphan-tree-terminated', { requestId: entry.name });
446
787
  let terminal = false;
447
788
  if (fs.existsSync(existingResponse)) {
448
789
  try {
@@ -476,9 +817,12 @@ function recoverProcessing(manifest, broker, brokerOwns) {
476
817
  if (terminal && (!request || !resultEvidence))
477
818
  continue;
478
819
  if (terminal && request && resultEvidence) {
479
- const reconciliation = terminalMatchesEvidence(manifest, request, existingTerminalResponse, resultEvidence, payload, payloadInvalid);
820
+ if (!terminalResult && request.family !== 'task-create')
821
+ continue;
822
+ const reconciliation = terminalMatchesEvidence(manifest, manifestPath, request, existingTerminalResponse, resultEvidence, payload, payloadInvalid, terminalResult);
480
823
  if (!reconciliation.valid)
481
824
  continue;
825
+ terminalReconciled = true;
482
826
  payloadReferenced = reconciliation.payloadReferenced;
483
827
  if (request.family === 'task-finalization' && resultEvidence.exitCode === 0) {
484
828
  const view = publishFinalizationTaskView(manifest, broker, request.id, 'starting', null, null);
@@ -486,12 +830,29 @@ function recoverProcessing(manifest, broker, brokerOwns) {
486
830
  continue;
487
831
  }
488
832
  }
833
+ if (!terminalResult && !terminalReconciled) {
834
+ if (terminal)
835
+ continue;
836
+ if (!brokerOwns())
837
+ return false;
838
+ if (!resultEvidence || !request || payloadInvalid)
839
+ continue;
840
+ if (request.family === 'task-create' && resultEvidence.exitCode === 0 && payload === null) {
841
+ writeSandboxControlResponse(manifest, genericRecoveryResponse(request, resultEvidence.exitCode, null));
842
+ terminalReconciled = true;
843
+ terminal = true;
844
+ }
845
+ else {
846
+ writeSandboxControlResponse(manifest, unknown(entry.name));
847
+ continue;
848
+ }
849
+ }
489
850
  if (!terminal) {
490
851
  if (!brokerOwns())
491
852
  return false;
492
853
  if (!resultEvidence || !request || payloadInvalid)
493
854
  continue;
494
- const recovered = recoveryResponse(manifest, request, resultEvidence, payload);
855
+ const recovered = recoveryResponse(manifest, manifestPath, request, resultEvidence, payload, terminalResult);
495
856
  if (!recovered)
496
857
  continue;
497
858
  if (request.family === 'task-finalization' && resultEvidence.exitCode === 0) {
@@ -500,12 +861,22 @@ function recoverProcessing(manifest, broker, brokerOwns) {
500
861
  continue;
501
862
  }
502
863
  writeSandboxControlResponse(manifest, recovered);
864
+ const preserveRecoveryEvidence = recovered.error?.code === 'SANDBOX_CONTROL_RESULT_UNKNOWN';
865
+ if (transitionProtocolActive && !preserveRecoveryEvidence) {
866
+ writeSandboxControlTransition(manifest, { requestId: entry.name, phase: 'recovered' });
867
+ }
868
+ if (preserveRecoveryEvidence)
869
+ continue;
503
870
  payloadReferenced = Boolean(payload) && request.family !== 'task-finalization';
504
871
  }
505
872
  }
506
873
  else {
507
874
  if (!brokerOwns())
508
875
  return false;
876
+ if (transitionProtocolActive && startedCommitted) {
877
+ writeSandboxControlResponse(manifest, unknown(entry.name));
878
+ continue;
879
+ }
509
880
  writeSandboxControlResponse(manifest, notExecuted(entry.name));
510
881
  }
511
882
  if (!brokerOwns())
@@ -536,6 +907,7 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
536
907
  const prepareExecution = options.prepareExecution ?? prepareSandboxControlExecution;
537
908
  const manifest = readSandboxControlManifest(manifestPath);
538
909
  const root = path.dirname(manifestPath);
910
+ assertCurrentSandboxControlIdentity(manifest, manifestPath);
539
911
  const startTime = getProcessStartTime(process.pid);
540
912
  if (!startTime)
541
913
  throw new Error('SANDBOX_CONTROL_BROKER_IDENTITY_UNAVAILABLE');
@@ -634,22 +1006,9 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
634
1006
  return;
635
1007
  appendBrokerAudit(manifest, 'broker-start', {
636
1008
  pid: broker.pid,
637
- brokerId: broker.brokerId,
638
- hostCwd: process.cwd(),
639
- manifestPath,
640
- manifestPathRealpath: safeRealpath(manifestPath),
641
- repoRoot: manifest.repoRoot,
642
- repoRootRealpath: safeRealpath(manifest.repoRoot),
643
- worktreeRoot: manifest.worktreeRoot,
644
- worktreeRootRealpath: safeRealpath(manifest.worktreeRoot),
645
- runtimeDir: manifest.runtimeDir,
646
- runtimeDirRealpath: safeRealpath(manifest.runtimeDir),
647
- channelDir: manifest.channelDir,
648
- publicStatusDir: manifest.publicStatusDir,
649
- processingDir: manifest.processingDir,
650
- internalCliPath: options.internalCliPath ?? process.argv[1] ?? null
1009
+ brokerId: broker.brokerId
651
1010
  });
652
- if (!recoverProcessing(manifest, broker, brokerOwns))
1011
+ if (!recoverProcessing(manifest, manifestPath, broker, brokerOwns))
653
1012
  return;
654
1013
  try {
655
1014
  const published = readSandboxControlStatus(manifest.publicStatusDir);
@@ -666,6 +1025,7 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
666
1025
  let current;
667
1026
  try {
668
1027
  current = readSandboxControlManifest(manifestPath);
1028
+ assertCurrentSandboxControlIdentity(current, manifestPath);
669
1029
  }
670
1030
  catch {
671
1031
  break;
@@ -702,7 +1062,7 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
702
1062
  }
703
1063
  else if (observation.state === 'unknown') {
704
1064
  containerReasonCode = 'SANDBOX_CONTROL_CONTAINER_UNKNOWN';
705
- appendSandboxControlAudit(manifest, 'container-heartbeat-unknown', { reason: observation.reason });
1065
+ appendDiagnosticAudit(manifest, 'container-heartbeat-unknown', { reason: observation.reason });
706
1066
  const backoff = nextSandboxControlBackoff(containerBackoffMs, timing.slowCheckMs);
707
1067
  nextContainerHeartbeatAt = Date.now() + backoff.delayMs;
708
1068
  containerBackoffMs = backoff.nextDelayMs;
@@ -721,7 +1081,7 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
721
1081
  if (!brokerOwns())
722
1082
  break;
723
1083
  containerReasonCode = 'SANDBOX_CONTROL_CONTAINER_ABSENT_GC_FAILED';
724
- appendSandboxControlAudit(manifest, 'container-gc-failed', { reason: controlError(error).code });
1084
+ appendDiagnosticAudit(manifest, 'container-gc-failed', { reason: controlError(error).code });
725
1085
  const backoff = nextSandboxControlBackoff(containerBackoffMs, timing.slowCheckMs);
726
1086
  nextContainerHeartbeatAt = Date.now() + backoff.delayMs;
727
1087
  containerBackoffMs = backoff.nextDelayMs;
@@ -790,6 +1150,10 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
790
1150
  break;
791
1151
  let terminalCommitted = false;
792
1152
  if (settledExecution.result && settledExecution.resultEvidenceWritten) {
1153
+ if (!consumeLifecycleAuthorityForResult(manifest, settledExecution)) {
1154
+ active = settledExecution;
1155
+ continue;
1156
+ }
793
1157
  terminalCommitted = publishExecutionResult(manifest, settledExecution.request, settledExecution.result, broker, brokerOwns);
794
1158
  if (terminalCommitted && settledExecution.request.family === 'task-finalization') {
795
1159
  try {
@@ -808,7 +1172,7 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
808
1172
  if (!terminalCommitted)
809
1173
  return;
810
1174
  appendBrokerAudit(manifest, 'executor-result-published', {
811
- ...requestAuditFields(manifest, manifestPath, settledExecution.request),
1175
+ ...requestAuditFields(manifest, settledExecution.request),
812
1176
  resultAvailable: settledExecution.result !== null,
813
1177
  resultEvidenceWritten: settledExecution.resultEvidenceWritten,
814
1178
  ...(settledExecution.result ? resultAuditFields(settledExecution.result) : {})
@@ -826,6 +1190,7 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
826
1190
  const source = path.join(requestsDir, name);
827
1191
  let claimed = null;
828
1192
  let validatedRequest = null;
1193
+ let prepared = null;
829
1194
  try {
830
1195
  if (!brokerOwns()) {
831
1196
  retiring = true;
@@ -853,63 +1218,76 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
853
1218
  throw new Error('SANDBOX_CONTROL_BUSY');
854
1219
  const request = validateSandboxControlRequest(JSON.parse(fs.readFileSync(claimed, 'utf8')), manifest);
855
1220
  validatedRequest = request;
1221
+ criticalRequestPhase(manifest, request, 'validated', 'in-progress');
856
1222
  appendBrokerAudit(manifest, 'request-validated', {
857
- ...requestAuditFields(manifest, manifestPath, request),
1223
+ ...requestAuditFields(manifest, request),
858
1224
  requestPath: claimed
859
1225
  });
860
1226
  if (bindingCheck(manifest))
861
1227
  throw new Error('SANDBOX_WORKTREE_BINDING_LOST');
862
1228
  if (readActiveLease(manifest))
863
1229
  throw new Error('SANDBOX_CONTROL_HANDOFF_ACTIVE');
1230
+ criticalRequestPhase(manifest, request, 'gated', 'in-progress');
864
1231
  appendBrokerAudit(manifest, 'request-gates-passed', {
865
- ...requestAuditFields(manifest, manifestPath, request),
1232
+ ...requestAuditFields(manifest, request),
866
1233
  requestPath: claimed,
867
1234
  bindingChecked: true,
868
1235
  handoffLeaseChecked: true
869
1236
  });
1237
+ criticalRequestPhase(manifest, request, 'reserved', 'in-progress');
870
1238
  writeSandboxControlReservation(manifest, request.id, sandboxControlGenerationUsage(manifest));
871
1239
  appendBrokerAudit(manifest, 'executor-reservation-written', {
872
- ...requestAuditFields(manifest, manifestPath, request),
1240
+ ...requestAuditFields(manifest, request),
873
1241
  requestPath: claimed,
874
1242
  executionPath: executionPath(manifest, request.id)
875
1243
  });
876
1244
  appendBrokerAudit(manifest, 'executor-prepare-start', {
877
- ...requestAuditFields(manifest, manifestPath, request),
1245
+ ...requestAuditFields(manifest, request),
878
1246
  requestPath: claimed,
879
1247
  executorCwd: manifest.repoRoot,
880
1248
  executorEntry: options.internalCliPath ?? process.argv[1] ?? null
881
1249
  });
882
- const prepared = await prepareExecution({
1250
+ const lifecycleAuthority = lifecycleAuthorityForRequest(manifest, manifestPath, request);
1251
+ if (lifecycleAuthority?.status === 'rejected') {
1252
+ throw Object.assign(new Error(lifecycleAuthority.error?.message ?? 'lifecycle authority was rejected'), { code: lifecycleAuthority.error?.code ?? 'LIFECYCLE_AUTHORITY_REJECTED' });
1253
+ }
1254
+ prepared = await prepareExecution({
883
1255
  manifest, manifestPath, request, requestPath: claimed,
884
- internalCliPath: options.internalCliPath ?? process.argv[1]
1256
+ internalCliPath: options.internalCliPath ?? process.argv[1],
1257
+ ...(lifecycleAuthority ? { lifecycleAuthority } : {})
885
1258
  });
1259
+ const preparedExecution = prepared;
1260
+ if (!preparedExecution)
1261
+ throw new Error('SANDBOX_CONTROL_EXECUTION_PREPARE_INVALID');
1262
+ criticalRequestPhase(manifest, request, 'prepared', 'in-progress', `${preparedExecution.execution.child.pid}`);
886
1263
  appendBrokerAudit(manifest, 'executor-prepared', {
887
- ...requestAuditFields(manifest, manifestPath, request),
1264
+ ...requestAuditFields(manifest, request),
888
1265
  requestPath: claimed,
889
1266
  executorCwd: manifest.repoRoot,
890
- childPid: prepared.execution.child.pid,
891
- childStartTime: prepared.execution.child.startTime,
892
- childProcessGroupId: prepared.execution.child.processGroupId,
1267
+ childPid: preparedExecution.execution.child.pid,
1268
+ childStartTime: preparedExecution.execution.child.startTime,
1269
+ childProcessGroupId: preparedExecution.execution.child.processGroupId,
893
1270
  executionPath: executionPath(manifest, request.id)
894
1271
  });
895
1272
  const execution = {
896
- request, prepared, result: null, resultEvidenceWritten: false, failure: null, settled: false
1273
+ request, prepared: preparedExecution, lifecycleAuthority, result: null,
1274
+ resultEvidenceWritten: false, failure: null, settled: false
897
1275
  };
898
1276
  active = execution;
899
- prepared.completion.then((result) => {
1277
+ preparedExecution.completion.then((result) => {
900
1278
  execution.result = sanitizeSandboxControlResult(manifest, result);
901
1279
  appendBrokerAudit(manifest, 'executor-completed', {
902
- ...requestAuditFields(manifest, manifestPath, request),
1280
+ ...requestAuditFields(manifest, request),
903
1281
  ...resultAuditFields(execution.result),
904
- childPid: prepared.execution.child.pid,
905
- childStartTime: prepared.execution.child.startTime
1282
+ childPid: preparedExecution.execution.child.pid,
1283
+ childStartTime: preparedExecution.execution.child.startTime
906
1284
  });
907
1285
  try {
908
1286
  writeSandboxControlResultEvidence(manifest, request.id, execution.result);
909
1287
  execution.resultEvidenceWritten = true;
910
1288
  execution.settled = true;
911
1289
  appendBrokerAudit(manifest, 'executor-result-evidence-written', {
912
- ...requestAuditFields(manifest, manifestPath, request),
1290
+ ...requestAuditFields(manifest, request),
913
1291
  resultPath: path.join(manifest.processingDir, request.id, 'result.json'),
914
1292
  ...resultAuditFields(execution.result)
915
1293
  });
@@ -917,7 +1295,7 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
917
1295
  catch (error) {
918
1296
  execution.failure = error;
919
1297
  appendBrokerAudit(manifest, 'executor-result-evidence-failed', {
920
- ...requestAuditFields(manifest, manifestPath, request),
1298
+ ...requestAuditFields(manifest, request),
921
1299
  errorType: error instanceof Error ? error.name : typeof error,
922
1300
  });
923
1301
  }
@@ -925,9 +1303,9 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
925
1303
  execution.failure = error;
926
1304
  execution.settled = true;
927
1305
  appendBrokerAudit(manifest, 'executor-failed', {
928
- ...requestAuditFields(manifest, manifestPath, request),
929
- childPid: prepared.execution.child.pid,
930
- childStartTime: prepared.execution.child.startTime,
1306
+ ...requestAuditFields(manifest, request),
1307
+ childPid: preparedExecution.execution.child.pid,
1308
+ childStartTime: preparedExecution.execution.child.startTime,
931
1309
  errorType: error instanceof Error ? error.name : typeof error,
932
1310
  });
933
1311
  });
@@ -943,11 +1321,13 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
943
1321
  retiring = true;
944
1322
  continue;
945
1323
  }
1324
+ criticalRequestPhase(manifest, request, 'accepted-authorized', 'in-progress');
946
1325
  writeAcceptedResponse(manifest, {
947
1326
  version: 2, id, phase: 'accepted', exitCode: null, stdout: '', stderr: '', error: null
948
1327
  });
1328
+ writeSandboxControlTransition(manifest, { requestId: request.id, phase: 'accepted-committed' });
949
1329
  appendBrokerAudit(manifest, 'request-accepted', {
950
- ...requestAuditFields(manifest, manifestPath, request),
1330
+ ...requestAuditFields(manifest, request),
951
1331
  acceptedPath: acceptedResponsePath(manifest, id),
952
1332
  executionPhase: 'prepared'
953
1333
  });
@@ -958,8 +1338,9 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
958
1338
  break;
959
1339
  }
960
1340
  try {
1341
+ criticalRequestPhase(manifest, request, 'start-authorized', 'in-progress', `${prepared.execution.child.pid}`);
961
1342
  appendBrokerAudit(manifest, 'executor-start', {
962
- ...requestAuditFields(manifest, manifestPath, request),
1343
+ ...requestAuditFields(manifest, request),
963
1344
  childPid: prepared.execution.child.pid,
964
1345
  childStartTime: prepared.execution.child.startTime,
965
1346
  executionPath: executionPath(manifest, request.id)
@@ -968,7 +1349,7 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
968
1349
  }
969
1350
  catch (error) {
970
1351
  appendBrokerAudit(manifest, 'executor-start-failed', {
971
- ...requestAuditFields(manifest, manifestPath, request),
1352
+ ...requestAuditFields(manifest, request),
972
1353
  errorType: error instanceof Error ? error.name : typeof error,
973
1354
  });
974
1355
  const owned = brokerOwns();
@@ -997,6 +1378,14 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
997
1378
  }
998
1379
  }
999
1380
  catch (error) {
1381
+ if (prepared) {
1382
+ try {
1383
+ prepared.terminate(brokerOwns());
1384
+ }
1385
+ catch { /* cleanup is best effort after a failed admission */ }
1386
+ prepared = null;
1387
+ active = null;
1388
+ }
1000
1389
  if (!brokerOwns()) {
1001
1390
  retiring = true;
1002
1391
  break;
@@ -1016,6 +1405,7 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
1016
1405
  retiring = true;
1017
1406
  break;
1018
1407
  }
1408
+ removeAcceptedResponse(manifest, id);
1019
1409
  if (claimed)
1020
1410
  fs.rmSync(path.dirname(claimed), { recursive: true, force: true });
1021
1411
  }
@@ -1041,7 +1431,8 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
1041
1431
  }
1042
1432
  }
1043
1433
  if (owned && active.result && active.resultEvidenceWritten) {
1044
- if (publishExecutionResult(manifest, active.request, active.result, broker, brokerOwns)) {
1434
+ if (consumeLifecycleAuthorityForResult(manifest, active)
1435
+ && publishExecutionResult(manifest, active.request, active.result, broker, brokerOwns)) {
1045
1436
  if (brokerOwns()) {
1046
1437
  removeAcceptedResponse(manifest, active.request.id);
1047
1438
  fs.rmSync(path.join(manifest.processingDir, active.request.id), { recursive: true, force: true });
@@ -1071,7 +1462,7 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
1071
1462
  }
1072
1463
  }
1073
1464
  if (brokerOwns()) {
1074
- appendSandboxControlAudit(manifest, 'broker-stop', { pid: broker.pid, brokerId: broker.brokerId });
1465
+ appendDiagnosticAudit(manifest, 'broker-stop', { pid: broker.pid, brokerId: broker.brokerId });
1075
1466
  try {
1076
1467
  if (fs.readFileSync(brokerPath, 'utf8') === brokerRecord)
1077
1468
  fs.unlinkSync(brokerPath);