@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,186 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { isCompletionEvidence } from './orchestration.ts';
3
+ import { TASK_WORKFLOW_OPERATIONS } from './workflow-command.ts';
4
+ import { sameRecoveryWarning } from './recovery-warning.ts';
5
+
6
+ export type ControlRecoveryOutcome = 'not-executed' | 'in-progress' | 'success' | 'failure' | 'unknown' | 'rejected';
7
+
8
+ /** Normalize the command result once at the control transport boundary. */
9
+ export function parseControlOutput(output: string | null): Record<string, unknown> | null {
10
+ if (output === null) return null;
11
+ try {
12
+ const value = JSON.parse(output);
13
+ if (!value || typeof value !== 'object' || Array.isArray(value)) return null;
14
+ return value.result && typeof value.result === 'object' && !Array.isArray(value.result) ? value.result : value;
15
+ } catch { return null; }
16
+ }
17
+
18
+ export type ControlRecoveryOperation = Readonly<{
19
+ family: 'task-lifecycle' | 'task-finalization' | 'task-orchestration' | 'task-create' | 'codex-controller' | 'task-workflow';
20
+ intent: string;
21
+ class: string;
22
+ }>;
23
+
24
+ export type ControlRecoveryBinding = Readonly<{
25
+ requestId: string;
26
+ generation: string;
27
+ taskId: string | null;
28
+ intentDigest: string;
29
+ }>;
30
+
31
+ export type ControlRecoveryInput = Readonly<{
32
+ operation: ControlRecoveryOperation;
33
+ binding: ControlRecoveryBinding;
34
+ startedCommitted: boolean;
35
+ terminalResult?: Readonly<Record<string, unknown>> | null;
36
+ criticalPhases?: readonly string[];
37
+ domain?: Readonly<Record<string, unknown>> | null;
38
+ journal?: Readonly<{
39
+ exists: boolean;
40
+ completedSteps?: readonly string[];
41
+ failure?: string | null;
42
+ }> | null;
43
+ explicitRejection?: boolean;
44
+ evidenceConflict?: boolean;
45
+ }>;
46
+
47
+ export const SANDBOX_CONTROL_REQUIRED_COMPLETION_PHASES = Object.freeze([
48
+ 'completed', 'evidence-written', 'publish-authorized'
49
+ ] as const);
50
+
51
+ export type ControlRecoveryDecision = Readonly<{
52
+ outcome: ControlRecoveryOutcome;
53
+ responseReconstructable: boolean;
54
+ reasonCode: string;
55
+ }>;
56
+
57
+ const LIFECYCLE_INTENTS = ['block', 'activate', 'cancel', 'complete', 'close-codescan', 'close-dependabot', 'restore', 'recover-started'] as const;
58
+ const ORCHESTRATION_INTENTS = [
59
+ 'begin-or-resume', 'route.read', 'route.clean-completion', 'status', 'prepare', 'dispatch',
60
+ 'await-activation', 'recover-prepared', 'hook-start', 'hook-stop', 'advance', 'pause'
61
+ ] as const;
62
+
63
+ export const SANDBOX_CONTROL_RECOVERY_OPERATIONS: readonly ControlRecoveryOperation[] = Object.freeze([
64
+ ...LIFECYCLE_INTENTS.map((intent) => ({ family: 'task-lifecycle' as const, intent, class: 'lifecycle-mutation' })),
65
+ { family: 'task-finalization', intent: 'complete', class: 'finalization' },
66
+ ...ORCHESTRATION_INTENTS.map((intent) => ({
67
+ family: 'task-orchestration' as const,
68
+ intent,
69
+ class: intent === 'route.clean-completion' ? 'route.clean-completion' : intent === 'route.read' || intent === 'status' ? 'read-only' : 'orchestration'
70
+ })),
71
+ { family: 'task-create', intent: 'create', class: 'task-create' },
72
+ ...TASK_WORKFLOW_OPERATIONS.map((intent) => ({ family: 'task-workflow' as const, intent, class: intent === 'artifact-inspect' || intent === 'decision-next-id' ? 'read-only' : 'workflow' })),
73
+ ...(['open', 'close', 'verify'] as const).map((intent) => ({ family: 'codex-controller' as const, intent, class: 'codex-controller' }))
74
+ ]);
75
+
76
+ export function digestControlRecoveryIntent(family: string, intent: string): string {
77
+ return createHash('sha256').update(`${family}\0${intent}`, 'utf8').digest('hex');
78
+ }
79
+
80
+ export function findSandboxControlRecoveryOperation(family: ControlRecoveryOperation['family'], intent: string): ControlRecoveryOperation | null {
81
+ return SANDBOX_CONTROL_RECOVERY_OPERATIONS.find((operation) => operation.family === family && operation.intent === intent) ?? null;
82
+ }
83
+
84
+ function bindingMatchesResult(binding: ControlRecoveryBinding, result: Readonly<Record<string, unknown>>): boolean {
85
+ return result.requestId === binding.requestId
86
+ && result.generation === binding.generation
87
+ && (result.taskId === undefined || result.taskId === binding.taskId)
88
+ && (result.intentDigest === undefined || result.intentDigest === binding.intentDigest);
89
+ }
90
+
91
+ function domainEvidenceMatches(
92
+ operation: ControlRecoveryOperation,
93
+ result: Readonly<Record<string, unknown>>,
94
+ domain: Readonly<Record<string, unknown>> | null | undefined
95
+ ): boolean {
96
+ if (!domain) return false;
97
+ if (domain.consistent !== true) return false;
98
+ if (operation.class === 'route.clean-completion') {
99
+ const completion = domain.completionEvidence as Record<string, unknown> | undefined;
100
+ const snapshot = domain.snapshot as Record<string, unknown> | undefined;
101
+ const lastReviewedCommit = domain.lastReviewedCommit;
102
+ return result.status === 'completed'
103
+ && domain.status === 'completed'
104
+ && domain.pendingDelegation === null
105
+ && isCompletionEvidence(completion)
106
+ && snapshot?.head === completion.head
107
+ && snapshot.headTree === completion.headTree
108
+ && snapshot.worktreeTree === completion.worktreeTree
109
+ && lastReviewedCommit === completion.lastReviewedCommit;
110
+ }
111
+ if (operation.family === 'task-lifecycle' && operation.intent === 'recover-started') {
112
+ const retryRequired = domain.recoveryState === 'retry-required'
113
+ && sameRecoveryWarning(domain.warning, result.warning);
114
+ return result.targetState === 'active'
115
+ && ['applied', 'no-op'].includes(String(result.status))
116
+ && domain.recovery === true
117
+ && domain.targetState === 'active'
118
+ && (domain.recoveryState === 'released' || retryRequired);
119
+ }
120
+ if (operation.class === 'read-only') return result.changed === false && domain.snapshotValid === true;
121
+ return true;
122
+ }
123
+
124
+ export function classifySandboxControlRecovery(input: ControlRecoveryInput): ControlRecoveryDecision {
125
+ const result = input.terminalResult ?? null;
126
+ if (input.evidenceConflict) return { outcome: 'unknown', responseReconstructable: false, reasonCode: 'RECOVERY_EVIDENCE_CONFLICT' };
127
+ if (input.explicitRejection && !input.startedCommitted) {
128
+ return { outcome: 'not-executed', responseReconstructable: true, reasonCode: 'RECOVERY_EXPLICIT_REJECTION' };
129
+ }
130
+ if (!input.startedCommitted) {
131
+ return { outcome: 'not-executed', responseReconstructable: false, reasonCode: 'RECOVERY_NOT_STARTED' };
132
+ }
133
+ if (!result) return { outcome: 'unknown', responseReconstructable: false, reasonCode: 'RECOVERY_TERMINAL_RESULT_MISSING' };
134
+ if (!bindingMatchesResult(input.binding, result)) {
135
+ return { outcome: 'unknown', responseReconstructable: false, reasonCode: 'RECOVERY_RESULT_BINDING_MISMATCH' };
136
+ }
137
+ if (!SANDBOX_CONTROL_REQUIRED_COMPLETION_PHASES.every((phase) => input.criticalPhases?.includes(phase))) {
138
+ return { outcome: 'unknown', responseReconstructable: false, reasonCode: 'RECOVERY_CRITICAL_AUDIT_INCOMPLETE' };
139
+ }
140
+ const status = result.status;
141
+ const failed = status === 'failed' || result.error !== null && result.error !== undefined;
142
+ if (failed) {
143
+ if (input.domain && input.domain.consistent === false) {
144
+ return { outcome: 'failure', responseReconstructable: true, reasonCode: 'RECOVERY_TERMINAL_FAILURE' };
145
+ }
146
+ return { outcome: 'unknown', responseReconstructable: false, reasonCode: 'RECOVERY_FAILURE_EVIDENCE_INCOMPLETE' };
147
+ }
148
+ if (result.changed === null) {
149
+ return { outcome: 'unknown', responseReconstructable: false, reasonCode: 'RECOVERY_RESULT_CHANGE_FLAG_MISSING' };
150
+ }
151
+ if (!domainEvidenceMatches(input.operation, result, input.domain)) {
152
+ if (input.journal?.exists && (input.journal.completedSteps?.length ?? 0) > 0) {
153
+ return { outcome: 'in-progress', responseReconstructable: false, reasonCode: 'RECOVERY_DOMAIN_EVIDENCE_INCOMPLETE' };
154
+ }
155
+ return { outcome: 'unknown', responseReconstructable: false, reasonCode: 'RECOVERY_DOMAIN_EVIDENCE_MISSING' };
156
+ }
157
+ if (input.operation.family === 'task-lifecycle' && input.journal?.exists) {
158
+ if (input.journal.failure) {
159
+ return { outcome: 'failure', responseReconstructable: true, reasonCode: 'RECOVERY_LIFECYCLE_JOURNAL_FAILURE' };
160
+ }
161
+ return {
162
+ outcome: input.journal.completedSteps && input.journal.completedSteps.length > 0 ? 'in-progress' : 'unknown',
163
+ responseReconstructable: false,
164
+ reasonCode: 'RECOVERY_LIFECYCLE_JOURNAL_PRESENT'
165
+ };
166
+ }
167
+ return {
168
+ outcome: 'success',
169
+ responseReconstructable: true,
170
+ reasonCode: input.operation.class === 'route.clean-completion' && result.changed === false
171
+ ? 'RECOVERY_ROUTE_COMPLETION_NOOP'
172
+ : input.operation.intent === 'recover-started' && input.domain?.recoveryState === 'retry-required'
173
+ ? 'RECOVERY_RELEASE_RETRY_REQUIRED'
174
+ : 'RECOVERY_TERMINAL_AND_DOMAIN_MATCH'
175
+ };
176
+ }
177
+
178
+ export function operationRecoveryBinding(
179
+ requestId: string,
180
+ generation: string,
181
+ taskId: string | null,
182
+ family: string,
183
+ intent: string
184
+ ): ControlRecoveryBinding {
185
+ return { requestId, generation, taskId, intentDigest: digestControlRecoveryIntent(family, intent) };
186
+ }
@@ -1,6 +1,3 @@
1
- import fs from 'node:fs';
2
- import path from 'node:path';
3
-
4
1
  import { syncPlatformComment } from '../platform/issue-comments.ts';
5
2
  import { createPlatformIssue, syncPlatformIssue } from '../platform/issues.ts';
6
3
  import type { PlatformResult } from '../platform/types.ts';
@@ -190,12 +187,6 @@ function platformOperations(prefix: string, operations: readonly { name: string;
190
187
  }));
191
188
  }
192
189
 
193
- function issueFromTask(repoRoot: string, taskId: string): { number: number; url: string } | null {
194
- const content = fs.readFileSync(path.join(repoRoot, '.agents', 'workspace', 'active', taskId, 'task.md'), 'utf8');
195
- const match = /^issue_number:\s*(\d+)\s*$/m.exec(content);
196
- return match ? { number: Number(match[1]), url: '' } : null;
197
- }
198
-
199
190
  async function createTask(value: unknown, options: CreateTaskOptions): Promise<TaskCreateResult> {
200
191
  const dependencies = { ...DEFAULT_DEPENDENCIES, ...options.dependencies };
201
192
  let candidate: TaskCreateCandidateV1;
@@ -215,7 +206,7 @@ async function createTask(value: unknown, options: CreateTaskOptions): Promise<T
215
206
  const operations: TaskCreateOperation[] = [{
216
207
  name: 'task:local', status: local.status, reasonCode: null
217
208
  }];
218
- let issue = issueFromTask(options.repoRoot, local.task.id);
209
+ let issue: TaskCreateResult['issue'] = null;
219
210
  const created = await dependencies.createIssue(local.task.id, { cwd: options.repoRoot, agent: candidate.agent });
220
211
  operations.push(...platformOperations('platform-create', created.operations));
221
212
  if (created.issue) issue = { number: created.issue.number, url: created.issue.url };
@@ -7,7 +7,7 @@ import { loadShortIdByTaskId, mutateShortIdRegistry } from './short-id.ts';
7
7
  import { TaskExecutionLockError, withTaskExecutionLock } from './task-execution-lock.ts';
8
8
  import { readDeliveryDefaults, validateBaseRef, validateRemote } from './delivery-target.ts';
9
9
  import { buildUnboundFact, encodePrDeliveryFact } from './pr-delivery-fact.ts';
10
- import { CANDIDATE_COLUMNS, CONSTRAINT_COLUMNS } from './qualification-audit.ts';
10
+ import { CANDIDATE_COLUMNS, CONSTRAINT_COLUMNS, parseTaskQualification } from './qualification-audit.ts';
11
11
 
12
12
  const AGENTS = ['claude', 'codex', 'antigravity', 'opencode', 'cursor'] as const;
13
13
  const TYPES = ['feature', 'bugfix', 'refactor', 'docs', 'chore'] as const;
@@ -191,11 +191,64 @@ function renderTable(columns: readonly string[], rows: readonly (readonly string
191
191
  ].join('\n');
192
192
  }
193
193
 
194
- function appendCanonicalRows(content: string, columns: readonly string[], rows: readonly (readonly string[])[]): string {
194
+ function escapeRegExp(value: string): string {
195
+ return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
196
+ }
197
+
198
+ function candidateIdFor(index: number): string {
199
+ let value = index + 1;
200
+ let id = '';
201
+ while (value > 0) {
202
+ const remainder = (value - 1) % 26;
203
+ id = `${String.fromCharCode(65 + remainder)}${id}`;
204
+ value = Math.floor((value - 1) / 26);
205
+ }
206
+ return id;
207
+ }
208
+
209
+ function appendCanonicalRows(content: string, heading: string, columns: readonly string[], rows: readonly (readonly string[])[]): string {
195
210
  if (rows.length === 0) return content;
196
211
  const table = renderTable(columns, rows);
197
- const separator = `| ${columns.map(() => '---').join(' | ')} |`;
198
- return content.replace(separator, `${separator}\n${table.split('\n').slice(2).join('\n')}`);
212
+ const [header, separator, ...rowLines] = table.split('\n');
213
+ const eol = content.includes('\r\n') ? '\r\n' : '\n';
214
+ const lines = content.split(/\r?\n/);
215
+ const headingPattern = new RegExp(`^###\\s+${escapeRegExp(heading)}\\s*$`);
216
+ const sectionStart = lines.findIndex((line) => headingPattern.test(line));
217
+ if (sectionStart < 0) throw new Error(`TASK_CREATE_QUALIFICATION_INVALID: section '${heading}' is missing`);
218
+ let sectionEnd = lines.length;
219
+ for (let index = sectionStart + 1; index < lines.length; index += 1) {
220
+ if (/^#{2,3}\s+/.test(lines[index]!)) {
221
+ sectionEnd = index;
222
+ break;
223
+ }
224
+ }
225
+ const matches: number[] = [];
226
+ for (let index = sectionStart + 1; index + 1 < sectionEnd; index += 1) {
227
+ if (lines[index]!.trim() === header && lines[index + 1]!.trim() === separator) matches.push(index);
228
+ }
229
+ if (matches.length !== 1) {
230
+ throw new Error(`TASK_CREATE_QUALIFICATION_INVALID: section '${heading}' must contain exactly one canonical qualification table`);
231
+ }
232
+ lines.splice(matches[0]! + 2, 0, ...rowLines);
233
+ return lines.join(eol);
234
+ }
235
+
236
+ function validateRenderedQualification(content: string, candidate: TaskCreateCandidateV1): void {
237
+ const parsed = parseTaskQualification(content);
238
+ if (!parsed.ok) {
239
+ throw new Error(`TASK_CREATE_QUALIFICATION_INVALID: ${parsed.code}: ${parsed.message}`);
240
+ }
241
+ if (!parsed.qualification.present) {
242
+ throw new Error('TASK_CREATE_QUALIFICATION_INVALID: rendered task qualification contract is missing');
243
+ }
244
+ const expectedConstraintIds = candidate.taskInput.constraints.map((_, index) => `C-${index + 1}`);
245
+ const expectedCandidateIds = candidate.taskInput.alternatives.map((_, index) => candidateIdFor(index));
246
+ const actualConstraintIds = parsed.qualification.constraints.map((row) => row.constraintId);
247
+ const actualCandidateIds = parsed.qualification.candidates.map((row) => row.candidateId);
248
+ if (JSON.stringify(actualConstraintIds) !== JSON.stringify(expectedConstraintIds)
249
+ || JSON.stringify(actualCandidateIds) !== JSON.stringify(expectedCandidateIds)) {
250
+ throw new Error('TASK_CREATE_QUALIFICATION_INVALID: rendered task qualification rows do not match task input');
251
+ }
199
252
  }
200
253
 
201
254
  function replaceEmptySubsection(content: string, heading: string, items: readonly string[]): string {
@@ -252,11 +305,11 @@ function renderTask(params: Readonly<{
252
305
  ];
253
306
  for (const [heading, key] of sections) content = replaceEmptySubsection(content, heading, candidate.taskInput[key]);
254
307
  const constraintIds = candidate.taskInput.constraints.map((_, index) => `C-${index + 1}`);
255
- content = appendCanonicalRows(content, CONSTRAINT_COLUMNS, candidate.taskInput.constraints.map((statement, index) => [
308
+ content = appendCanonicalRows(content, '约束', CONSTRAINT_COLUMNS, candidate.taskInput.constraints.map((statement, index) => [
256
309
  constraintIds[index]!, statement, 'assumption', 'create-task', 'taskInput', 'create-task input', '', ''
257
310
  ]));
258
- content = appendCanonicalRows(content, CANDIDATE_COLUMNS, candidate.taskInput.alternatives.map((statement, index) => [
259
- String.fromCharCode(65 + index), statement, 'pending', constraintIds.join(','), 'requires qualification', 'create-task input'
311
+ content = appendCanonicalRows(content, '候选与否决方案', CANDIDATE_COLUMNS, candidate.taskInput.alternatives.map((statement, index) => [
312
+ candidateIdFor(index), statement, 'pending', constraintIds.join(','), 'requires qualification', 'create-task input'
260
313
  ]));
261
314
  content = content.replace('- **关联 Issue**:#XXX', '- **关联 Issue**:N/A');
262
315
  content = content.replace('- **关联 PR**:#XXX', '- **关联 PR**:N/A');
@@ -365,7 +418,6 @@ function createLocalTask(value: unknown, options: LocalTaskCreateOptions): Local
365
418
 
366
419
  return withCreateLock(repoRoot, workspaceRoot, () => {
367
420
  if (fs.existsSync(receipts)) assertRealDirectory(receipts, 'TASK_CREATE_RECEIPT_INVALID');
368
- else fs.mkdirSync(receipts, { mode: 0o700 });
369
421
  const receiptPath = path.join(receipts, `${keyDigest}.json`);
370
422
  if (fs.existsSync(receiptPath)) {
371
423
  const stat = fs.lstatSync(receiptPath);
@@ -380,6 +432,7 @@ function createLocalTask(value: unknown, options: LocalTaskCreateOptions): Local
380
432
 
381
433
  const recovered = recoverPublishedTask(activeRoot, repoRoot, keyDigest, candidateDigest);
382
434
  if (recovered) {
435
+ ensureRealDirectory(receipts, 'TASK_CREATE_RECEIPT_INVALID');
383
436
  writeReceipt(receiptPath, {
384
437
  version: 1, candidateDigest, taskId: recovered.taskId, shortId: recovered.shortId, status: 'recovered'
385
438
  });
@@ -401,6 +454,8 @@ function createLocalTask(value: unknown, options: LocalTaskCreateOptions): Local
401
454
  template: fs.readFileSync(templatePath, 'utf8'), candidate, taskId, project: projectConfig.project, delivery: projectConfig.delivery, timestamp,
402
455
  agentInfraVersion, keyDigest, candidateDigest
403
456
  });
457
+ validateRenderedQualification(rendered, candidate);
458
+ ensureRealDirectory(receipts, 'TASK_CREATE_RECEIPT_INVALID');
404
459
  const temporary = path.join(workspaceRoot, `.task-create.tmp.${process.pid}.${randomUUID()}`);
405
460
  fs.mkdirSync(temporary, { mode: 0o700 });
406
461
  fs.writeFileSync(path.join(temporary, 'task.md'), rendered, { flag: 'wx', mode: 0o600 });
@@ -1,23 +1,4 @@
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
- lines: SourceLine[];
18
- headings: VisibleHeading[];
19
- anchors: VisibleAnchor[];
20
- };
1
+ import { scanVisibleMarkdown, type SourceLine } from './markdown.ts';
21
2
 
22
3
  type DecisionDetailBlock = {
23
4
  id: string;
@@ -49,54 +30,9 @@ type DecisionDetailInspection =
49
30
  };
50
31
 
51
32
  const DECISION_ID = /^(AN|PL|CD|HD)-[1-9]\d*(?=$|[\s::])/i;
52
- const HEADING = /^ {0,3}(#{1,6})[ \t]+(.+?)\s*$/;
53
- const FENCE_OPEN = /^ {0,3}(`{3,}|~{3,})(.*)$/;
54
- const FENCE_CLOSE = /^ {0,3}(`{3,}|~{3,})\s*$/;
55
- const ANCHOR = /^\s*<a\s+id=(['"])([^'"]+)\1\s*><\/a>\s*$/;
56
33
  const FIELD = /^\s*[-*]\s+\*\*([^*]+)\*\*\s*[::]\s*/;
57
34
  const OPTION_HEADING = /^(?:方案|Option)\s+[A-Z](?:\b|\s*[::])/i;
58
35
 
59
- function sourceLines(content: string): SourceLine[] {
60
- const lines: SourceLine[] = [];
61
- let start = 0;
62
- while (start < content.length) {
63
- const newline = content.indexOf('\n', start);
64
- const end = newline === -1 ? content.length : newline;
65
- lines.push({ start, end, text: content.slice(start, end).replace(/\r$/, '') });
66
- start = newline === -1 ? content.length : newline + 1;
67
- }
68
- return lines;
69
- }
70
-
71
- function scanVisibleMarkdown(content: string): VisibleMarkdown {
72
- const lines = sourceLines(content);
73
- const visible: SourceLine[] = [];
74
- const headings: VisibleHeading[] = [];
75
- const anchors: VisibleAnchor[] = [];
76
- let fence: { character: '`' | '~'; length: number } | null = null;
77
-
78
- for (const line of lines) {
79
- if (fence) {
80
- const close = line.text.match(FENCE_CLOSE);
81
- if (close && close[1]![0] === fence.character && close[1]!.length >= fence.length) fence = null;
82
- continue;
83
- }
84
- const open = line.text.match(FENCE_OPEN);
85
- const marker = open?.[1];
86
- const info = open?.[2] ?? '';
87
- if (marker && !(marker[0] === '`' && info.includes('`'))) {
88
- fence = { character: marker[0] as '`' | '~', length: marker.length };
89
- continue;
90
- }
91
- visible.push(line);
92
- const heading = line.text.match(HEADING);
93
- if (heading) headings.push({ ...line, level: heading[1]!.length, text: heading[2]!.trim() });
94
- const anchor = line.text.match(ANCHOR);
95
- if (anchor) anchors.push({ ...line, id: anchor[2]! });
96
- }
97
- return { lines: visible, headings, anchors };
98
- }
99
-
100
36
  function parseDecisionDetailBlocks(content: string): DecisionDetailBlock[] {
101
37
  const scanned = scanVisibleMarkdown(content);
102
38
  const details = scanned.headings.filter((heading) => heading.level === 3 && DECISION_ID.test(heading.text));
@@ -270,15 +206,11 @@ export {
270
206
  normalizeDecisionDetailForDisplay,
271
207
  parseDecisionDetailBlocks,
272
208
  parseDecisionEvidence,
273
- resolveDecisionDetail,
274
- scanVisibleMarkdown
209
+ resolveDecisionDetail
275
210
  };
276
211
  export type {
277
212
  DecisionDetailBlock,
278
213
  DecisionDetailDuplicate,
279
214
  DecisionDetailInspection,
280
- DecisionDetailResolution,
281
- SourceLine,
282
- VisibleHeading,
283
- VisibleMarkdown
215
+ DecisionDetailResolution
284
216
  };
@@ -186,7 +186,8 @@ function isDelegationHostEvidence(value: unknown): value is DelegationHostEviden
186
186
  function hasCurrentCodexEvidence(receipt: DelegationReceipt): boolean {
187
187
  const provenance = receipt.lifecycleProvenance;
188
188
  if (!provenance) return false;
189
- const activated = ['activated', 'stage-completed', 'sealed', 'consumed'].includes(receipt.status);
189
+ const activated = ['activated', 'stage-completed', 'sealed', 'consumed'].includes(receipt.status)
190
+ || (receipt.status === 'aborted' && receipt.activatedAt !== null);
190
191
  if (!activated) return receipt.hostEvidence === null;
191
192
  const host = receipt.hostEvidence;
192
193
  if (
@@ -219,6 +220,12 @@ function hasCurrentCodexEvidence(receipt: DelegationReceipt): boolean {
219
220
  && host.consumer === receipt.id
220
221
  && exactText(host.consumedAt);
221
222
  }
223
+ if (receipt.status === 'aborted') {
224
+ return Number.isSafeInteger(host.stopRevision)
225
+ && (host.stopRevision as number) > host.startRevision
226
+ && host.consumer === `lifecycle-recovery:${receipt.taskId}:${receipt.id}`
227
+ && exactText(host.consumedAt);
228
+ }
222
229
  return host.stopRevision === null && host.consumer === null && host.consumedAt === null;
223
230
  }
224
231
 
@@ -244,7 +251,8 @@ function hasStatusBoundEvidence(receipt: DelegationReceipt): boolean {
244
251
  const dispatchComplete = dispatchFields.every((field) => field !== null);
245
252
  if (!dispatchEmpty && !dispatchComplete) return false;
246
253
 
247
- const beforeActivation = ['prepared', 'aborted', 'expired'].includes(receipt.status);
254
+ const beforeActivation = ['prepared', 'expired'].includes(receipt.status)
255
+ || (receipt.status === 'aborted' && receipt.activatedAt === null);
248
256
  if (beforeActivation) {
249
257
  return receipt.parentId === null
250
258
  && receipt.childId === null
@@ -282,6 +290,21 @@ function hasStatusBoundEvidence(receipt: DelegationReceipt): boolean {
282
290
  && receipt.consumedAt === null;
283
291
  }
284
292
 
293
+ if (receipt.status === 'aborted') {
294
+ const host = receipt.hostEvidence;
295
+ return receipt.agent === null
296
+ && receipt.afterFingerprint === null
297
+ && receipt.changedPaths.length === 0
298
+ && receipt.sealedAt === null
299
+ && receipt.consumedAt === null
300
+ && host !== null
301
+ && host !== undefined
302
+ && Number.isSafeInteger(host.stopRevision)
303
+ && (host.stopRevision as number) > host.startRevision
304
+ && host.consumer === `lifecycle-recovery:${receipt.taskId}:${receipt.id}`
305
+ && exactText(host.consumedAt);
306
+ }
307
+
285
308
  if (
286
309
  !exactText(receipt.agent)
287
310
  || normalizeAgentToken(receipt.agent) !== normalizeAgentToken(receipt.client)
@@ -584,6 +607,53 @@ function abortPreparedDelegation(receipt: DelegationReceipt): ReceiptResult {
584
607
  };
585
608
  }
586
609
 
610
+ function abortActivatedDelegation(
611
+ receipt: DelegationReceipt,
612
+ event: Readonly<{ childId: string; stopRevision: number; consumer: string; consumedAt: string }>
613
+ ): ReceiptResult {
614
+ if (receipt.status !== 'activated') {
615
+ return fail('DELEGATION_STATE_INVALID', `delegation ${receipt.id} is ${receipt.status}, expected activated`);
616
+ }
617
+ if (
618
+ receipt.client !== 'codex'
619
+ || !receipt.lifecycleProvenance
620
+ || receipt.hostEvidence?.kind !== 'codex-lifecycle-v2'
621
+ || typeof receipt.lifecycleProvenance.controllerInstanceDigest !== 'string'
622
+ || typeof receipt.lifecycleProvenance.controlGeneration !== 'string'
623
+ || typeof receipt.hostEvidence.controllerInstanceDigest !== 'string'
624
+ || typeof receipt.hostEvidence.controlGeneration !== 'string'
625
+ ) {
626
+ return fail('DELEGATION_RECOVERY_EVIDENCE_INVALID', 'activated delegation lacks the current Codex controller binding');
627
+ }
628
+ if (
629
+ event.childId !== receipt.childId
630
+ || !Number.isSafeInteger(event.stopRevision)
631
+ || event.stopRevision <= receipt.hostEvidence.startRevision
632
+ || event.consumer !== `lifecycle-recovery:${receipt.taskId}:${receipt.id}`
633
+ || !exactText(event.consumedAt)
634
+ ) {
635
+ return fail('DELEGATION_RECOVERY_EVIDENCE_INVALID', 'recovery stop evidence does not match the activated delegation');
636
+ }
637
+ return {
638
+ ok: true,
639
+ receipt: Object.freeze({
640
+ ...receipt,
641
+ status: 'aborted' as const,
642
+ agent: null,
643
+ afterFingerprint: null,
644
+ changedPaths: Object.freeze([]),
645
+ sealedAt: null,
646
+ consumedAt: null,
647
+ hostEvidence: Object.freeze({
648
+ ...receipt.hostEvidence,
649
+ stopRevision: event.stopRevision,
650
+ consumer: event.consumer,
651
+ consumedAt: event.consumedAt
652
+ })
653
+ })
654
+ };
655
+ }
656
+
587
657
  function completeDelegationStage(
588
658
  receipt: DelegationReceipt,
589
659
  event: Readonly<{ stage: DelegationStage; round: number; artifact: string; agent: string }>
@@ -666,6 +736,7 @@ function consumeDelegation(receipt: DelegationReceipt, options: { now?: () => st
666
736
 
667
737
  export {
668
738
  activateDelegation,
739
+ abortActivatedDelegation,
669
740
  abortPreparedDelegation,
670
741
  completeDelegationStage,
671
742
  consumeDelegation,