@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
@@ -0,0 +1,304 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { createHash, randomUUID } from 'node:crypto';
4
+
5
+ import { validateManualValidationReceipt } from './manual-validation-receipt.ts';
6
+ import {
7
+ MANUAL_VALIDATION_ARTIFACT as ARTIFACT,
8
+ MANUAL_VALIDATION_SHA40 as SHA40,
9
+ MANUAL_VALIDATION_SHA64 as SHA64,
10
+ MANUAL_VALIDATION_TASK_ID as TASK_ID,
11
+ exactKeys,
12
+ isRecord,
13
+ validTimestamp,
14
+ writeJsonAtomic
15
+ } from './manual-validation-shared.ts';
16
+
17
+ const MANUAL_VALIDATION_TRANSACTION_SCHEMA = 'agent-infra/manual-validation-transaction';
18
+ const MANUAL_VALIDATION_TRANSACTION_VERSION = 1;
19
+ const TRANSACTION_ID = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/u;
20
+
21
+ type ManualValidationTransactionPhase = 'prepared' | 'summary-staged' | 'receipt-committed' | 'final-promotion-in-progress' | 'committed' | 'aborted' | 'recovery-required';
22
+ type ManualValidationSummaryPreimage = Readonly<{ commentId: number | string | null; body: string; digest: string }>;
23
+ type ManualValidationTransaction = Readonly<{
24
+ schema: typeof MANUAL_VALIDATION_TRANSACTION_SCHEMA;
25
+ version: typeof MANUAL_VALIDATION_TRANSACTION_VERSION;
26
+ transactionId: string;
27
+ taskId: string;
28
+ prNumber: number;
29
+ prHeadSha: string;
30
+ evidenceDigest: string;
31
+ summaryPreimage: ManualValidationSummaryPreimage;
32
+ pendingSummaryDigest: string;
33
+ finalSummaryDigest: string;
34
+ artifact: string;
35
+ phase: ManualValidationTransactionPhase;
36
+ committedReceipt: string | null;
37
+ eventAppended: boolean;
38
+ attempt: number;
39
+ postWriteVerified: boolean;
40
+ createdAt: string;
41
+ updatedAt: string;
42
+ error: string | null;
43
+ }>;
44
+ type ManualValidationTransactionInput = Omit<ManualValidationTransaction, 'schema' | 'version' | 'phase' | 'committedReceipt' | 'eventAppended' | 'postWriteVerified' | 'error'> & {
45
+ transactionId?: string;
46
+ };
47
+ type ManualValidationTransactionPatch = Partial<Pick<ManualValidationTransaction, 'committedReceipt' | 'eventAppended' | 'postWriteVerified' | 'error' | 'updatedAt' | 'attempt'>>;
48
+ type ManualValidationTransactionErrorCode =
49
+ | 'MANUAL_VALIDATION_TRANSACTION_INVALID'
50
+ | 'MANUAL_VALIDATION_TRANSACTION_MISSING'
51
+ | 'MANUAL_VALIDATION_TRANSACTION_IDENTITY_MISMATCH'
52
+ | 'MANUAL_VALIDATION_TRANSACTION_PHASE_INVALID';
53
+ type ManualValidationTransactionError = { code: ManualValidationTransactionErrorCode; message: string };
54
+ type ManualValidationTransactionResult =
55
+ | { ok: true; value: ManualValidationTransaction }
56
+ | { ok: false; error: ManualValidationTransactionError };
57
+ type ManualValidationGenerationArchiveOptions = Readonly<{
58
+ afterReceiptMove?: () => void;
59
+ }>;
60
+
61
+ function invalid(message: string): ManualValidationTransactionResult {
62
+ return { ok: false, error: { code: 'MANUAL_VALIDATION_TRANSACTION_INVALID', message } };
63
+ }
64
+
65
+ function summaryPreimageDigest(body: string): string {
66
+ return createHash('sha256').update(body, 'utf8').digest('hex');
67
+ }
68
+
69
+ function validateManualValidationTransaction(value: unknown, expected?: Partial<Pick<ManualValidationTransaction, 'transactionId' | 'taskId' | 'prNumber' | 'prHeadSha' | 'evidenceDigest' | 'artifact'>>): ManualValidationTransactionResult {
70
+ if (!isRecord(value)) return invalid('transaction must be a JSON object');
71
+ const keys = ['schema', 'version', 'transactionId', 'taskId', 'prNumber', 'prHeadSha', 'evidenceDigest', 'summaryPreimage', 'pendingSummaryDigest', 'finalSummaryDigest', 'artifact', 'phase', 'committedReceipt', 'eventAppended', 'attempt', 'postWriteVerified', 'createdAt', 'updatedAt', 'error'];
72
+ const keyError = exactKeys(value, keys, 'transaction');
73
+ if (keyError) return invalid(keyError);
74
+ if (value.schema !== MANUAL_VALIDATION_TRANSACTION_SCHEMA || value.version !== MANUAL_VALIDATION_TRANSACTION_VERSION) return invalid('transaction schema or version is unsupported');
75
+ if (typeof value.transactionId !== 'string' || !TRANSACTION_ID.test(value.transactionId)) return invalid('transactionId is invalid');
76
+ if (typeof value.taskId !== 'string' || !TASK_ID.test(value.taskId)) return invalid('taskId is invalid');
77
+ const prNumber = value.prNumber;
78
+ if (!Number.isSafeInteger(prNumber) || (prNumber as number) <= 0) return invalid('prNumber is invalid');
79
+ if (typeof value.prHeadSha !== 'string' || !SHA40.test(value.prHeadSha)) return invalid('prHeadSha is invalid');
80
+ if (typeof value.evidenceDigest !== 'string' || !SHA64.test(value.evidenceDigest)) return invalid('evidenceDigest is invalid');
81
+ if (!isRecord(value.summaryPreimage)) return invalid('summaryPreimage is invalid');
82
+ const preimage = value.summaryPreimage;
83
+ const preimageKeyError = exactKeys(preimage, ['commentId', 'body', 'digest'], 'summaryPreimage');
84
+ if (preimageKeyError) return invalid(preimageKeyError);
85
+ if (preimage.commentId !== null && typeof preimage.commentId !== 'number' && typeof preimage.commentId !== 'string') return invalid('summaryPreimage.commentId is invalid');
86
+ if (typeof preimage.body !== 'string' || typeof preimage.digest !== 'string' || !SHA64.test(preimage.digest) || summaryPreimageDigest(preimage.body) !== preimage.digest) return invalid('summaryPreimage digest is invalid');
87
+ for (const field of ['pendingSummaryDigest', 'finalSummaryDigest'] as const) if (typeof value[field] !== 'string' || !SHA64.test(value[field])) return invalid(`${field} is invalid`);
88
+ if (typeof value.artifact !== 'string' || !ARTIFACT.test(value.artifact)) return invalid('artifact is invalid');
89
+ if (!['prepared', 'summary-staged', 'receipt-committed', 'final-promotion-in-progress', 'committed', 'aborted', 'recovery-required'].includes(value.phase as string)) return invalid('phase is invalid');
90
+ if (value.committedReceipt !== null && (typeof value.committedReceipt !== 'string' || !SHA64.test(value.committedReceipt))) return invalid('committedReceipt is invalid');
91
+ if (typeof value.eventAppended !== 'boolean' || typeof value.postWriteVerified !== 'boolean') return invalid('transaction completion flags are invalid');
92
+ const attempt = value.attempt;
93
+ if (!Number.isSafeInteger(attempt) || (attempt as number) < 1) return invalid('attempt is invalid');
94
+ if (!validTimestamp(value.createdAt) || !validTimestamp(value.updatedAt)) return invalid('transaction timestamps are invalid');
95
+ if (value.error !== null && (typeof value.error !== 'string' || !value.error || /[\r\n]/u.test(value.error))) return invalid('transaction error is invalid');
96
+ if (['receipt-committed', 'final-promotion-in-progress', 'committed'].includes(value.phase as string) && value.committedReceipt === null) return invalid('receipt-backed phases require committedReceipt');
97
+ if (['final-promotion-in-progress', 'committed'].includes(value.phase as string) && !value.eventAppended) return invalid('final promotion requires an appended completion event');
98
+ if (value.phase === 'committed' && !value.postWriteVerified) return invalid('committed phase requires post-write verification');
99
+ const transaction = value as unknown as ManualValidationTransaction;
100
+ if (expected && Object.entries(expected).some(([key, expectedValue]) => expectedValue !== undefined && transaction[key as keyof ManualValidationTransaction] !== expectedValue)) {
101
+ return { ok: false, error: { code: 'MANUAL_VALIDATION_TRANSACTION_IDENTITY_MISMATCH', message: 'transaction identity does not match the expected operation' } };
102
+ }
103
+ return { ok: true, value: transaction };
104
+ }
105
+
106
+ function createManualValidationTransaction(input: ManualValidationTransactionInput): ManualValidationTransaction {
107
+ const transaction: ManualValidationTransaction = {
108
+ schema: MANUAL_VALIDATION_TRANSACTION_SCHEMA,
109
+ version: MANUAL_VALIDATION_TRANSACTION_VERSION,
110
+ transactionId: input.transactionId ?? `mv-${randomUUID()}`,
111
+ taskId: input.taskId,
112
+ prNumber: input.prNumber,
113
+ prHeadSha: input.prHeadSha,
114
+ evidenceDigest: input.evidenceDigest,
115
+ summaryPreimage: input.summaryPreimage,
116
+ pendingSummaryDigest: input.pendingSummaryDigest,
117
+ finalSummaryDigest: input.finalSummaryDigest,
118
+ artifact: input.artifact,
119
+ phase: 'prepared',
120
+ committedReceipt: null,
121
+ eventAppended: false,
122
+ attempt: input.attempt,
123
+ postWriteVerified: false,
124
+ createdAt: input.createdAt,
125
+ updatedAt: input.updatedAt,
126
+ error: null
127
+ };
128
+ const checked = validateManualValidationTransaction(transaction);
129
+ if (!checked.ok) throw new Error(`${checked.error.code}: ${checked.error.message}`);
130
+ return checked.value;
131
+ }
132
+
133
+ const allowedTransitions: Record<ManualValidationTransactionPhase, readonly ManualValidationTransactionPhase[]> = {
134
+ prepared: ['prepared', 'summary-staged', 'aborted', 'recovery-required'],
135
+ 'summary-staged': ['summary-staged', 'receipt-committed', 'aborted', 'recovery-required'],
136
+ 'receipt-committed': ['receipt-committed', 'final-promotion-in-progress', 'aborted', 'recovery-required'],
137
+ 'final-promotion-in-progress': ['final-promotion-in-progress', 'committed', 'aborted', 'recovery-required'],
138
+ committed: ['committed'],
139
+ aborted: ['aborted', 'prepared'],
140
+ 'recovery-required': ['recovery-required', 'prepared']
141
+ };
142
+
143
+ function transitionManualValidationTransaction(
144
+ current: ManualValidationTransaction,
145
+ phase: ManualValidationTransactionPhase,
146
+ patch: ManualValidationTransactionPatch = {}
147
+ ): ManualValidationTransactionResult {
148
+ if (!allowedTransitions[current.phase].includes(phase)) return { ok: false, error: { code: 'MANUAL_VALIDATION_TRANSACTION_PHASE_INVALID', message: `cannot transition from ${current.phase} to ${phase}` } };
149
+ if (phase === 'final-promotion-in-progress' && (!current.eventAppended && patch.eventAppended !== true)) return { ok: false, error: { code: 'MANUAL_VALIDATION_TRANSACTION_PHASE_INVALID', message: 'final promotion requires an appended completion event' } };
150
+ const next: ManualValidationTransaction = {
151
+ ...current,
152
+ phase,
153
+ committedReceipt: patch.committedReceipt !== undefined ? patch.committedReceipt : current.committedReceipt,
154
+ eventAppended: patch.eventAppended ?? current.eventAppended,
155
+ postWriteVerified: patch.postWriteVerified ?? current.postWriteVerified,
156
+ attempt: patch.attempt ?? current.attempt,
157
+ error: patch.error !== undefined ? patch.error : current.error,
158
+ updatedAt: patch.updatedAt ?? new Date().toISOString()
159
+ };
160
+ const checked = validateManualValidationTransaction(next);
161
+ if (!checked.ok) return checked;
162
+ return checked;
163
+ }
164
+
165
+ function retryManualValidationTransaction(current: ManualValidationTransaction, now = new Date().toISOString()): ManualValidationTransactionResult {
166
+ if (!['aborted', 'recovery-required'].includes(current.phase)) return { ok: false, error: { code: 'MANUAL_VALIDATION_TRANSACTION_PHASE_INVALID', message: 'only aborted or recovery-required transactions can be retried' } };
167
+ return transitionManualValidationTransaction(current, 'prepared', {
168
+ updatedAt: now,
169
+ error: null,
170
+ committedReceipt: null,
171
+ eventAppended: false,
172
+ postWriteVerified: false,
173
+ attempt: current.attempt + 1
174
+ });
175
+ }
176
+
177
+ function manualValidationTransactionPath(taskDir: string): string {
178
+ return path.join(taskDir, '.manual-validation', 'transaction.json');
179
+ }
180
+
181
+ function manualValidationGenerationPaths(taskDir: string, transaction: ManualValidationTransaction): {
182
+ receipt: { current: string; history: string };
183
+ transaction: { current: string; history: string };
184
+ } {
185
+ const stateDir = path.join(taskDir, '.manual-validation');
186
+ const historyDir = path.join(stateDir, 'history');
187
+ return {
188
+ receipt: {
189
+ current: path.join(stateDir, 'receipt.json'),
190
+ history: path.join(historyDir, `receipt-${transaction.transactionId}-attempt-${transaction.attempt}.json`)
191
+ },
192
+ transaction: {
193
+ current: manualValidationTransactionPath(taskDir),
194
+ history: path.join(historyDir, `transaction-${transaction.transactionId}-attempt-${transaction.attempt}.json`)
195
+ }
196
+ };
197
+ }
198
+
199
+ function readReceiptAt(file: string, transaction: ManualValidationTransaction): 'missing' | 'valid' {
200
+ let value: unknown;
201
+ try {
202
+ value = JSON.parse(fs.readFileSync(file, 'utf8')) as unknown;
203
+ } catch (error) {
204
+ if ((error as NodeJS.ErrnoException).code === 'ENOENT') return 'missing';
205
+ throw new Error(`manual-validation receipt is unreadable: ${error instanceof Error ? error.message : String(error)}`);
206
+ }
207
+ const checked = validateManualValidationReceipt(value, {
208
+ transactionId: transaction.transactionId,
209
+ taskId: transaction.taskId,
210
+ prNumber: transaction.prNumber,
211
+ prHeadSha: transaction.prHeadSha,
212
+ evidenceDigest: transaction.evidenceDigest,
213
+ artifact: transaction.artifact
214
+ });
215
+ if (!checked.ok) throw new Error(`manual-validation receipt is invalid: ${checked.error.message}`);
216
+ if (transaction.committedReceipt !== null && checked.value.receiptDigest !== transaction.committedReceipt) {
217
+ throw new Error('manual-validation receipt does not match the committed transaction');
218
+ }
219
+ return 'valid';
220
+ }
221
+
222
+ function validateManualValidationGenerationArchive(taskDir: string, transaction: ManualValidationTransaction, requireReceipt = false): void {
223
+ const paths = manualValidationGenerationPaths(taskDir, transaction);
224
+ const currentTransaction = fs.existsSync(paths.transaction.current);
225
+ const archivedTransaction = fs.existsSync(paths.transaction.history);
226
+ if (currentTransaction && archivedTransaction) throw new Error(`manual-validation archive has conflicting transaction files for ${transaction.transactionId}`);
227
+ if (!currentTransaction && !archivedTransaction) throw new Error(`manual-validation archive source is missing: ${paths.transaction.current}`);
228
+ const currentReceipt = requireReceipt
229
+ ? readReceiptAt(paths.receipt.current, transaction)
230
+ : (fs.existsSync(paths.receipt.current) ? 'valid' : 'missing');
231
+ const archivedReceipt = requireReceipt
232
+ ? readReceiptAt(paths.receipt.history, transaction)
233
+ : (fs.existsSync(paths.receipt.history) ? 'valid' : 'missing');
234
+ if (requireReceipt && currentReceipt === 'missing' && archivedReceipt === 'missing') {
235
+ throw new Error(`manual-validation receipt is missing for transaction ${transaction.transactionId}`);
236
+ }
237
+ if (currentReceipt === 'valid' && archivedReceipt === 'valid') {
238
+ throw new Error(`manual-validation archive has conflicting receipt files for ${transaction.transactionId}`);
239
+ }
240
+ }
241
+
242
+ function archiveManualValidationGeneration(
243
+ taskDir: string,
244
+ transaction: ManualValidationTransaction,
245
+ requireReceipt = false,
246
+ options: ManualValidationGenerationArchiveOptions = {}
247
+ ): void {
248
+ const paths = manualValidationGenerationPaths(taskDir, transaction);
249
+ validateManualValidationGenerationArchive(taskDir, transaction, requireReceipt);
250
+ fs.mkdirSync(path.dirname(paths.receipt.history), { recursive: true });
251
+ const move = (source: string, target: string, required: boolean): void => {
252
+ if (!fs.existsSync(source)) {
253
+ if (fs.existsSync(target) || !required) return;
254
+ throw new Error(`manual-validation archive source is missing: ${source}`);
255
+ }
256
+ if (fs.existsSync(target)) throw new Error(`manual-validation archive target already exists: ${target}`);
257
+ fs.renameSync(source, target);
258
+ };
259
+ move(paths.receipt.current, paths.receipt.history, requireReceipt);
260
+ options.afterReceiptMove?.();
261
+ move(paths.transaction.current, paths.transaction.history, true);
262
+ }
263
+
264
+ function writeManualValidationTransactionAtomic(taskDir: string, transaction: ManualValidationTransaction): string {
265
+ const checked = validateManualValidationTransaction(transaction);
266
+ if (!checked.ok) throw new Error(`${checked.error.code}: ${checked.error.message}`);
267
+ const target = manualValidationTransactionPath(taskDir);
268
+ return writeJsonAtomic(target, transaction);
269
+ }
270
+
271
+ function readManualValidationTransaction(taskDir: string, expected?: Parameters<typeof validateManualValidationTransaction>[1]): ManualValidationTransactionResult {
272
+ const file = manualValidationTransactionPath(taskDir);
273
+ let value: unknown;
274
+ try { value = JSON.parse(fs.readFileSync(file, 'utf8')) as unknown; }
275
+ catch (error) {
276
+ return { ok: false, error: { code: (error as NodeJS.ErrnoException).code === 'ENOENT' ? 'MANUAL_VALIDATION_TRANSACTION_MISSING' : 'MANUAL_VALIDATION_TRANSACTION_INVALID', message: error instanceof Error ? error.message : String(error) } };
277
+ }
278
+ return validateManualValidationTransaction(value, expected);
279
+ }
280
+
281
+ export {
282
+ MANUAL_VALIDATION_TRANSACTION_SCHEMA,
283
+ MANUAL_VALIDATION_TRANSACTION_VERSION,
284
+ archiveManualValidationGeneration,
285
+ createManualValidationTransaction,
286
+ manualValidationTransactionPath,
287
+ validateManualValidationGenerationArchive,
288
+ readManualValidationTransaction,
289
+ retryManualValidationTransaction,
290
+ summaryPreimageDigest,
291
+ transitionManualValidationTransaction,
292
+ validateManualValidationTransaction,
293
+ writeManualValidationTransactionAtomic
294
+ };
295
+ export type {
296
+ ManualValidationSummaryPreimage,
297
+ ManualValidationTransaction,
298
+ ManualValidationTransactionError,
299
+ ManualValidationTransactionErrorCode,
300
+ ManualValidationTransactionInput,
301
+ ManualValidationTransactionPatch,
302
+ ManualValidationTransactionPhase,
303
+ ManualValidationTransactionResult
304
+ };
@@ -0,0 +1,70 @@
1
+ type SourceLine = {
2
+ start: number;
3
+ end: number;
4
+ text: string;
5
+ };
6
+
7
+ type VisibleHeading = SourceLine & {
8
+ level: number;
9
+ text: string;
10
+ };
11
+
12
+ type VisibleAnchor = SourceLine & {
13
+ id: string;
14
+ };
15
+
16
+ type VisibleMarkdown = {
17
+ hasUnclosedFence: boolean;
18
+ lines: SourceLine[];
19
+ headings: VisibleHeading[];
20
+ anchors: VisibleAnchor[];
21
+ };
22
+
23
+ const HEADING = /^ {0,3}(#{1,6})[ \t]+(.+?)\s*$/;
24
+ const FENCE_OPEN = /^ {0,3}(`{3,}|~{3,})(.*)$/;
25
+ const FENCE_CLOSE = /^ {0,3}(`{3,}|~{3,})\s*$/;
26
+ const ANCHOR = /^\s*<a\s+id=(['"])([^'"]+)\1\s*><\/a>\s*$/;
27
+
28
+ function sourceLines(content: string): SourceLine[] {
29
+ const lines: SourceLine[] = [];
30
+ let start = 0;
31
+ while (start < content.length) {
32
+ const newline = content.indexOf('\n', start);
33
+ const end = newline === -1 ? content.length : newline;
34
+ lines.push({ start, end, text: content.slice(start, end).replace(/\r$/, '') });
35
+ start = newline === -1 ? content.length : newline + 1;
36
+ }
37
+ return lines;
38
+ }
39
+
40
+ function scanVisibleMarkdown(content: string): VisibleMarkdown {
41
+ const lines = sourceLines(content);
42
+ const visible: SourceLine[] = [];
43
+ const headings: VisibleHeading[] = [];
44
+ const anchors: VisibleAnchor[] = [];
45
+ let fence: { character: '`' | '~'; length: number } | null = null;
46
+
47
+ for (const line of lines) {
48
+ if (fence) {
49
+ const close = line.text.match(FENCE_CLOSE);
50
+ if (close && close[1]![0] === fence.character && close[1]!.length >= fence.length) fence = null;
51
+ continue;
52
+ }
53
+ const open = line.text.match(FENCE_OPEN);
54
+ const marker = open?.[1];
55
+ const info = open?.[2] ?? '';
56
+ if (marker && !(marker[0] === '`' && info.includes('`'))) {
57
+ fence = { character: marker[0] as '`' | '~', length: marker.length };
58
+ continue;
59
+ }
60
+ visible.push(line);
61
+ const heading = line.text.match(HEADING);
62
+ if (heading) headings.push({ ...line, level: heading[1]!.length, text: heading[2]!.trim() });
63
+ const anchor = line.text.match(ANCHOR);
64
+ if (anchor) anchors.push({ ...line, id: anchor[2]! });
65
+ }
66
+ return { lines: visible, headings, anchors, hasUnclosedFence: fence !== null };
67
+ }
68
+
69
+ export { scanVisibleMarkdown };
70
+ export type { SourceLine, VisibleHeading, VisibleMarkdown };
@@ -2,6 +2,7 @@ import fs from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import { createHash, randomUUID } from 'node:crypto';
4
4
 
5
+ import { artifactName, maxArtifactRound } from './artifact-name.ts';
5
6
  import { parseTypedTaskFrontmatter } from './frontmatter.ts';
6
7
  import { validateCurrentTaskContract } from './current-contract.ts';
7
8
  import { parseReviewSummary } from './review-artifacts.ts';
@@ -10,6 +11,7 @@ import type { LedgerDocument } from './ledger.ts';
10
11
  import { resolveTaskRef } from './resolve-ref.ts';
11
12
  import {
12
13
  activateDelegation,
14
+ abortActivatedDelegation,
13
15
  abortPreparedDelegation,
14
16
  completeDelegationStage,
15
17
  consumeDelegation,
@@ -39,6 +41,7 @@ import type {
39
41
  OrchestrationModelPolicy,
40
42
  OrchestrationRolePolicy
41
43
  } from '../agent-clients/types.ts';
44
+ import type { LifecycleRecoveryAttestationV1 } from './control-authority.ts';
42
45
  import {
43
46
  captureRepositorySnapshot,
44
47
  captureWorkspaceSnapshot,
@@ -57,6 +60,7 @@ import { hasOpenLifecycleExecution } from './activity-log.ts';
57
60
  import { invalidationBlocks } from './invalidation.ts';
58
61
  import type { InvalidationDocument } from './invalidation.ts';
59
62
  import type { LifecycleAction, LifecycleFacts } from './capabilities.ts';
63
+ import { normalizeAgentToken } from '../agent-clients/tokens.ts';
60
64
 
61
65
  type OrchestrationStatus = 'running' | 'paused' | 'completed';
62
66
  type ModelPolicySource = Readonly<{
@@ -173,6 +177,7 @@ type OrchestrationOptions = {
173
177
  supportsLifecycleDelegation?: (client: AgentClientId) => boolean;
174
178
  validateLifecycleCapability?: () => Exclude<OrchestrationResult['error'], null> | null;
175
179
  consumeLifecycleCapability?: () => Exclude<OrchestrationResult['error'], null> | null;
180
+ lifecycleRecoveryAttestation?: LifecycleRecoveryAttestationV1 | null;
176
181
  token?: () => string;
177
182
  monotonicNow?: () => number;
178
183
  sleep?: (milliseconds: number) => Promise<void>;
@@ -275,7 +280,7 @@ function isRecovery(value: unknown): value is OrchestrationRecovery {
275
280
  && value.resultingStatus === 'running';
276
281
  }
277
282
 
278
- function isCompletionEvidence(value: unknown): value is CleanCompletionEvidence {
283
+ export function isCompletionEvidence(value: unknown): value is CleanCompletionEvidence {
279
284
  return hasExactKeys(value, [
280
285
  'kind', 'observedAt', 'head', 'headTree', 'worktreeTree', 'lastReviewedCommit', 'prNumber', 'prHead'
281
286
  ])
@@ -604,25 +609,13 @@ function beginOrResumeOrchestration(taskRef: string, options: OrchestrationOptio
604
609
  return { status: 'running', changed: true, taskId: resolved.taskId, run, next: null, error: null };
605
610
  }
606
611
 
607
- function highestRound(taskDir: string, family: string): number {
608
- const pattern = new RegExp(`^${family}(?:-r(\\d+))?\\.md$`);
609
- return fs.readdirSync(taskDir).reduce((max, name) => {
610
- const match = pattern.exec(name);
611
- return match ? Math.max(max, match[1] ? Number(match[1]) : 1) : max;
612
- }, 0);
613
- }
614
-
615
- function artifactName(family: string, round: number): string {
616
- return round === 1 ? `${family}.md` : `${family}-r${round}.md`;
617
- }
618
-
619
612
  function validateSavedCurrentDiffBase(
620
613
  repoRoot: string,
621
614
  metadata: ReturnType<typeof parseTypedTaskFrontmatter>,
622
615
  taskDir: string,
623
616
  reviewedHead: string
624
617
  ): Readonly<{ code: string; message: string }> | null {
625
- const reviewRound = highestRound(taskDir, 'review-code');
618
+ const reviewRound = maxArtifactRound(fs.readdirSync(taskDir), 'review-code');
626
619
  const reviewContent = fs.readFileSync(path.join(taskDir, artifactName('review-code', reviewRound)), 'utf8');
627
620
  const savedTargetHead = extractReviewTargetHead(reviewContent);
628
621
  const savedDiffBase = extractReviewDiffBase(reviewContent);
@@ -660,10 +653,9 @@ function routeFromFacts(facts: LifecycleFacts): Omit<OrchestrationNext, 'request
660
653
  const action = recommendation.action;
661
654
  if (action === 'manual-validation' || action === 'validation-run') return null;
662
655
  const family = action === 'analysis' ? 'analysis' : action;
663
- const round = Math.max(0, ...[...(facts.artifacts[family] ?? []), ...(facts.staleArtifacts?.[family] ?? [])].map((name) => {
664
- const match = /-r(\d+)\.md$/.exec(name);
665
- return match ? Number(match[1]) : 1;
666
- })) + 1;
656
+ const round = maxArtifactRound([
657
+ ...(facts.artifacts[family] ?? []), ...(facts.staleArtifacts?.[family] ?? [])
658
+ ], family) + 1;
667
659
  const command = action === 'analysis' ? 'analyze-task' : action === 'plan' ? 'plan-task' : action === 'code' ? 'code-task' : action;
668
660
  const role = action.startsWith('review-') ? 'reviewer' : 'executor';
669
661
  return { action: command, role, stage: action, round, artifact: artifactName(family, round) };
@@ -719,7 +711,7 @@ function routeOrchestration(taskRef: string, options: OrchestrationOptions = {})
719
711
  const routed = routeFromFacts(facts.facts);
720
712
  if (!routed) return failed('ORCHESTRATION_ROUTE_UNKNOWN', 'cannot determine a unique lifecycle action', resolved.taskId);
721
713
  if ('completion' in routed) {
722
- const reviewRound = highestRound(resolved.taskDir, 'review-code');
714
+ const reviewRound = maxArtifactRound(fs.readdirSync(resolved.taskDir), 'review-code');
723
715
  const review = parseReviewSummary(fs.readFileSync(
724
716
  path.join(resolved.taskDir, artifactName('review-code', reviewRound)),
725
717
  'utf8'
@@ -830,6 +822,7 @@ function prepareOrchestrationDelegationUnlocked(
830
822
  requestedModel?: string;
831
823
  requestedReasoningEffort?: string;
832
824
  lifecycleProvenance?: DelegationLifecycleProvenance;
825
+ lifecycleRecoveryAttestation?: LifecycleRecoveryAttestationV1 | null;
833
826
  }>,
834
827
  options: OrchestrationOptions = {}
835
828
  ): OrchestrationResult {
@@ -866,6 +859,13 @@ function prepareOrchestrationDelegationUnlocked(
866
859
  if (input.client === 'codex' && !input.lifecycleProvenance) {
867
860
  return failed('ORCHESTRATION_CODEX_PROVENANCE_REQUIRED', 'Codex prepare requires lifecycle provenance', resolved.taskId);
868
861
  }
862
+ if (input.lifecycleRecoveryAttestation
863
+ && (input.lifecycleRecoveryAttestation.phase !== 'orchestration.prepare'
864
+ || input.lifecycleRecoveryAttestation.taskId !== resolved.taskId
865
+ || input.lifecycleRecoveryAttestation.artifact !== next.artifact
866
+ || input.lifecycleRecoveryAttestation.round !== next.round)) {
867
+ return failed('LIFECYCLE_AUTHORITY_TUPLE_MISMATCH', 'orchestration authority does not match the routed stage', resolved.taskId);
868
+ }
869
869
  const expectedPolicy = run.modelPolicy[next.role];
870
870
  if (input.requestedModel !== expectedPolicy.model) {
871
871
  return failed('ORCHESTRATION_REQUESTED_MODEL_MISMATCH', `requested model does not match the persisted ${next.role} model`, resolved.taskId);
@@ -930,6 +930,7 @@ function prepareOrchestrationDelegation(
930
930
  requestedModel?: string;
931
931
  requestedReasoningEffort?: string;
932
932
  lifecycleProvenance?: DelegationLifecycleProvenance;
933
+ lifecycleRecoveryAttestation?: LifecycleRecoveryAttestationV1 | null;
933
934
  }>,
934
935
  options: OrchestrationOptions = {}
935
936
  ): OrchestrationResult {
@@ -1445,6 +1446,68 @@ function recoverPreparedOrchestrationDelegation(
1445
1446
  }
1446
1447
  }
1447
1448
 
1449
+ function recoverActivatedOrchestrationDelegationUnderLock(
1450
+ taskRef: string,
1451
+ event: Readonly<{
1452
+ receiptId: string;
1453
+ stage: DelegationStage;
1454
+ round: number;
1455
+ artifact: string;
1456
+ startedAgent: string;
1457
+ childId: string;
1458
+ stopRevision: number;
1459
+ consumer: string;
1460
+ consumedAt: string;
1461
+ }>,
1462
+ options: OrchestrationOptions = {}
1463
+ ): OrchestrationResult {
1464
+ const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
1465
+ if (!resolved.ok) return failed(resolved.code, resolved.message, resolved.taskId);
1466
+ const run = readRun(resolved.taskDir, options);
1467
+ if (!run) return failed('ORCHESTRATION_RUN_MISSING', 'no orchestration run exists', resolved.taskId);
1468
+ const matches = [
1469
+ ...(run.pendingDelegation ? [run.pendingDelegation] : []),
1470
+ ...run.receipts.filter((receipt) => receipt.id === event.receiptId)
1471
+ ];
1472
+ const receipt = matches.find((candidate) => candidate.id === event.receiptId);
1473
+ if (!receipt) return failed('ORCHESTRATION_DELEGATION_MISSING', 'recovery receipt does not exist', resolved.taskId);
1474
+ if (
1475
+ receipt.taskId !== resolved.taskId
1476
+ || receipt.stage !== event.stage
1477
+ || receipt.round !== event.round
1478
+ || receipt.artifact !== event.artifact
1479
+ || receipt.childId !== event.childId
1480
+ || normalizeAgentToken(event.startedAgent) !== normalizeAgentToken(receipt.client)
1481
+ ) {
1482
+ return failed('ORCHESTRATION_PROVENANCE_MISMATCH', 'recovery selector does not match the delegation receipt', resolved.taskId);
1483
+ }
1484
+ if (receipt.status === 'aborted' && run.pendingDelegation === null) {
1485
+ return { status: run.status, changed: false, taskId: resolved.taskId, run, next: null, error: null };
1486
+ }
1487
+ if (run.pendingDelegation?.id !== receipt.id || receipt.status !== 'activated') {
1488
+ return failed('ORCHESTRATION_RECOVERY_UNSAFE', 'recovery requires one matching activated pending delegation', resolved.taskId);
1489
+ }
1490
+ if (run.receipts.some((candidate) => candidate.id === receipt.id)) {
1491
+ return failed('ORCHESTRATION_RECEIPT_DUPLICATE', 'recovery receipt already exists in the completed receipt history', resolved.taskId);
1492
+ }
1493
+ const aborted = abortActivatedDelegation(receipt, {
1494
+ childId: event.childId,
1495
+ stopRevision: event.stopRevision,
1496
+ consumer: event.consumer,
1497
+ consumedAt: event.consumedAt
1498
+ });
1499
+ if (!aborted.ok) return failed(aborted.code, aborted.message, resolved.taskId);
1500
+ const updated = withUpdatedRun(run, {
1501
+ status: 'running',
1502
+ pause: null,
1503
+ nextStage: null,
1504
+ pendingDelegation: null,
1505
+ receipts: Object.freeze([...run.receipts, aborted.receipt])
1506
+ }, options.now);
1507
+ saveRun(resolved.taskDir, updated);
1508
+ return { status: 'running', changed: true, taskId: resolved.taskId, run: updated, next: null, error: null };
1509
+ }
1510
+
1448
1511
  function completeOrchestrationStage(
1449
1512
  taskRef: string,
1450
1513
  event: Parameters<typeof completeDelegationStage>[1],
@@ -1599,6 +1662,7 @@ export {
1599
1662
  readRun,
1600
1663
  reconcileMatchingOrchestrationDelegation,
1601
1664
  recoverPreparedOrchestrationDelegation,
1665
+ recoverActivatedOrchestrationDelegationUnderLock,
1602
1666
  routeOrchestration,
1603
1667
  sealMatchingOrchestrationDelegation,
1604
1668
  sealMatchingOrchestrationDelegationWithHostEvidence,
@@ -1606,6 +1670,7 @@ export {
1606
1670
  statusOrchestration,
1607
1671
  };
1608
1672
  export type {
1673
+ CleanCompletionEvidence,
1609
1674
  OrchestrationCompletionPlanResult,
1610
1675
  OrchestrationModelPolicy,
1611
1676
  OrchestrationNext,