@fitlab-ai/agent-infra 0.9.14 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (354) hide show
  1. package/README.md +10 -0
  2. package/README.zh-CN.md +10 -0
  3. package/bin/internal-cli.sh +45 -0
  4. package/dist/bin/internal-cli.js +93 -30
  5. package/dist/lib/agent-clients/adapters/codex-lifecycle/capability-store.js +165 -27
  6. package/dist/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.js +9 -4
  7. package/dist/lib/agent-clients/adapters/codex-lifecycle/store.js +43 -3
  8. package/dist/lib/defaults.json +103 -103
  9. package/dist/lib/fs/durable-write.js +39 -0
  10. package/dist/lib/fs/file-lock.js +111 -0
  11. package/dist/lib/host-control/audit.js +19 -0
  12. package/dist/lib/host-control/client.js +142 -0
  13. package/dist/lib/host-control/command.js +49 -0
  14. package/dist/lib/host-control/path.js +160 -0
  15. package/dist/lib/host-control/secure-fs.js +140 -0
  16. package/dist/lib/host-control/server.js +159 -0
  17. package/dist/lib/host-control/service.js +112 -0
  18. package/dist/lib/init.js +11 -2
  19. package/dist/lib/internal/cli-route-inventory.js +4 -2
  20. package/dist/lib/internal/codex-lifecycle.js +6 -6
  21. package/dist/lib/internal/demo-promotion.js +322 -0
  22. package/dist/lib/internal/demo-transcript.js +454 -0
  23. package/dist/lib/internal/host-control.js +28 -0
  24. package/dist/lib/internal/manual-validation.js +331 -0
  25. package/dist/lib/internal/platform-pr.js +18 -4
  26. package/dist/lib/internal/release-workflow.js +63 -84
  27. package/dist/lib/internal/sandbox-control.js +149 -2
  28. package/dist/lib/internal/task-artifact.js +16 -263
  29. package/dist/lib/internal/task-create.js +6 -5
  30. package/dist/lib/internal/task-event.js +37 -37
  31. package/dist/lib/internal/task-lifecycle.js +2 -2
  32. package/dist/lib/internal/task-operation-registry.js +101 -4
  33. package/dist/lib/internal/task-review.js +11 -56
  34. package/dist/lib/internal/task-validate.js +30 -10
  35. package/dist/lib/owned-path.js +33 -0
  36. package/dist/lib/platform/check-status.js +11 -0
  37. package/dist/lib/platform/comment-safety.js +4 -1
  38. package/dist/lib/platform/completion-backfill.js +1 -1
  39. package/dist/lib/platform/github-data.js +241 -0
  40. package/dist/lib/platform/github-provider.js +15 -20
  41. package/dist/lib/platform/issue-comments.js +18 -12
  42. package/dist/lib/platform/issues.js +2 -180
  43. package/dist/lib/platform/pr-change-report.js +5 -4
  44. package/dist/lib/platform/pr-checks.js +12 -65
  45. package/dist/lib/platform/pr-summary.js +117 -33
  46. package/dist/lib/platform/pull-requests.js +5 -108
  47. package/dist/lib/platform/snapshots.js +2 -0
  48. package/dist/lib/platform/task-identities.js +2 -9
  49. package/dist/lib/platform/verification-sync.js +132 -143
  50. package/dist/lib/pr-review/evidence-grading.js +4 -8
  51. package/dist/lib/release/workflow.js +1 -1
  52. package/dist/lib/sandbox/commands/create.js +7 -2
  53. package/dist/lib/sandbox/commands/rm.js +19 -1511
  54. package/dist/lib/sandbox/control/audit.js +203 -0
  55. package/dist/lib/sandbox/control/client.js +93 -54
  56. package/dist/lib/sandbox/control/executor.js +129 -65
  57. package/dist/lib/sandbox/control/finalization-response.js +27 -0
  58. package/dist/lib/sandbox/control/identity-sentinel.js +107 -0
  59. package/dist/lib/sandbox/control/lifecycle.js +36 -2
  60. package/dist/lib/sandbox/control/native-file-lock.js +6 -98
  61. package/dist/lib/sandbox/control/protocol.js +55 -4
  62. package/dist/lib/sandbox/control/server.js +518 -127
  63. package/dist/lib/sandbox/control/state.js +64 -0
  64. package/dist/lib/sandbox/control/task-workflow.js +74 -0
  65. package/dist/lib/sandbox/control/workflow-executor.js +89 -0
  66. package/dist/lib/sandbox/index.js +1 -2
  67. package/dist/lib/sandbox/recovery.js +28 -4
  68. package/dist/lib/sandbox/removal.js +822 -0
  69. package/dist/lib/sandbox/task-cleanup.js +217 -0
  70. package/dist/lib/sandbox/workspace-identity.js +17 -1
  71. package/dist/lib/sandbox/workspace-view.js +13 -14
  72. package/dist/lib/task/activity-intent.js +2 -1
  73. package/dist/lib/task/activity-log.js +30 -24
  74. package/dist/lib/task/artifact-command.js +79 -0
  75. package/dist/lib/task/artifact-lifecycle.js +30 -28
  76. package/dist/lib/task/artifact-name.js +36 -0
  77. package/dist/lib/task/artifact-operations.js +29 -289
  78. package/dist/lib/task/artifact-receipts.js +3 -15
  79. package/dist/lib/task/artifact-recovery.js +430 -0
  80. package/dist/lib/task/artifact-repair-intent.js +134 -29
  81. package/dist/lib/task/artifact-schema.js +6 -14
  82. package/dist/lib/task/capabilities.js +12 -19
  83. package/dist/lib/task/codex-orchestration.js +71 -25
  84. package/dist/lib/task/commit-identity.js +28 -0
  85. package/dist/lib/task/commit-operation.js +33 -20
  86. package/dist/lib/task/control-authority.js +403 -6
  87. package/dist/lib/task/control-recovery.js +140 -0
  88. package/dist/lib/task/create-service.js +1 -8
  89. package/dist/lib/task/create.js +62 -9
  90. package/dist/lib/task/decision-details.js +2 -46
  91. package/dist/lib/task/delegation-receipts.js +64 -3
  92. package/dist/lib/task/events.js +275 -22
  93. package/dist/lib/task/finalization-state.js +9 -0
  94. package/dist/lib/task/human-override.js +8 -14
  95. package/dist/lib/task/implementation-inputs.js +3 -3
  96. package/dist/lib/task/intermediate-cleanup.js +441 -0
  97. package/dist/lib/task/invalidation.js +18 -65
  98. package/dist/lib/task/ledger-intents.js +7 -22
  99. package/dist/lib/task/ledger.js +1 -3
  100. package/dist/lib/task/lifecycle-recovery.js +549 -0
  101. package/dist/lib/task/lifecycle.js +31 -4
  102. package/dist/lib/task/local-artifact-finalization.js +197 -197
  103. package/dist/lib/task/manual-validation-completion.js +26 -0
  104. package/dist/lib/task/manual-validation-evidence.js +130 -0
  105. package/dist/lib/task/manual-validation-receipt.js +105 -0
  106. package/dist/lib/task/manual-validation-shared.js +40 -0
  107. package/dist/lib/task/manual-validation-transaction.js +243 -0
  108. package/dist/lib/task/markdown.js +47 -0
  109. package/dist/lib/task/orchestration.js +66 -19
  110. package/dist/lib/task/pr-delivery-fact.js +12 -56
  111. package/dist/lib/task/qualification-audit.js +20 -34
  112. package/dist/lib/task/recovery-warning.js +19 -0
  113. package/dist/lib/task/review-artifacts.js +2 -18
  114. package/dist/lib/task/review-command.js +26 -0
  115. package/dist/lib/task/review-finalization.js +175 -216
  116. package/dist/lib/task/review-fingerprint.js +2 -2
  117. package/dist/lib/task/sections.js +34 -56
  118. package/dist/lib/task/short-id.js +15 -7
  119. package/dist/lib/task/task-execution-lock.js +1 -1
  120. package/dist/lib/task/verification-engine.js +119 -337
  121. package/dist/lib/task/verification.js +1 -1
  122. package/dist/lib/task/workflow-command.js +16 -0
  123. package/dist/lib/task/workflow-warning-intents.js +0 -16
  124. package/dist/lib/task/workflow-warnings.js +28 -2
  125. package/dist/lib/update.js +2 -0
  126. package/dist/lib/version.js +1 -11
  127. package/dist/lifecycle-build-manifest.json +3 -3
  128. package/lib/agent-clients/adapters/codex-lifecycle/capability-store.ts +223 -33
  129. package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.ts +10 -4
  130. package/lib/agent-clients/adapters/codex-lifecycle/store.ts +55 -3
  131. package/lib/fs/durable-write.ts +30 -0
  132. package/lib/fs/file-lock.ts +113 -0
  133. package/lib/host-control/audit.ts +22 -0
  134. package/lib/host-control/client.ts +172 -0
  135. package/lib/host-control/command.ts +60 -0
  136. package/lib/host-control/path.ts +167 -0
  137. package/lib/host-control/secure-fs.ts +135 -0
  138. package/lib/host-control/server.ts +180 -0
  139. package/lib/host-control/service.ts +133 -0
  140. package/lib/init.ts +15 -2
  141. package/lib/internal/cli-route-inventory.ts +4 -2
  142. package/lib/internal/codex-lifecycle.ts +6 -6
  143. package/lib/internal/demo-promotion.ts +354 -0
  144. package/lib/internal/demo-transcript.ts +419 -0
  145. package/lib/internal/host-control.ts +28 -0
  146. package/lib/internal/manual-validation.ts +315 -0
  147. package/lib/internal/platform-pr.ts +15 -4
  148. package/lib/internal/release-workflow.ts +74 -82
  149. package/lib/internal/sandbox-control.ts +162 -1
  150. package/lib/internal/task-artifact.ts +14 -217
  151. package/lib/internal/task-create.ts +5 -5
  152. package/lib/internal/task-event.ts +20 -11
  153. package/lib/internal/task-lifecycle.ts +4 -3
  154. package/lib/internal/task-operation-registry.ts +111 -4
  155. package/lib/internal/task-review.ts +9 -54
  156. package/lib/internal/task-validate.ts +26 -10
  157. package/lib/owned-path.ts +26 -0
  158. package/lib/platform/check-status.ts +9 -0
  159. package/lib/platform/comment-safety.ts +5 -0
  160. package/lib/platform/completion-backfill.ts +1 -1
  161. package/lib/platform/github-data.ts +322 -0
  162. package/lib/platform/github-provider.ts +15 -20
  163. package/lib/platform/issue-comments.ts +21 -14
  164. package/lib/platform/issues.ts +5 -246
  165. package/lib/platform/pr-change-report.ts +7 -5
  166. package/lib/platform/pr-checks.ts +13 -77
  167. package/lib/platform/pr-summary.ts +123 -35
  168. package/lib/platform/pull-requests.ts +6 -173
  169. package/lib/platform/snapshots.ts +53 -0
  170. package/lib/platform/task-identities.ts +3 -14
  171. package/lib/platform/verification-sync.ts +150 -156
  172. package/lib/pr-review/evidence-grading.ts +4 -9
  173. package/lib/release/workflow.ts +3 -3
  174. package/lib/sandbox/commands/create.ts +7 -6
  175. package/lib/sandbox/commands/rm.ts +21 -1895
  176. package/lib/sandbox/control/audit.ts +253 -0
  177. package/lib/sandbox/control/client.ts +109 -66
  178. package/lib/sandbox/control/executor.ts +134 -74
  179. package/lib/sandbox/control/finalization-response.ts +29 -0
  180. package/lib/sandbox/control/identity-sentinel.ts +137 -0
  181. package/lib/sandbox/control/lifecycle.ts +45 -3
  182. package/lib/sandbox/control/native-file-lock.ts +6 -107
  183. package/lib/sandbox/control/protocol.ts +80 -6
  184. package/lib/sandbox/control/server.ts +567 -132
  185. package/lib/sandbox/control/state.ts +86 -0
  186. package/lib/sandbox/control/task-workflow.ts +95 -0
  187. package/lib/sandbox/control/workflow-executor.ts +92 -0
  188. package/lib/sandbox/index.ts +1 -2
  189. package/lib/sandbox/recovery.ts +28 -5
  190. package/lib/sandbox/removal.ts +1142 -0
  191. package/lib/sandbox/task-cleanup.ts +209 -0
  192. package/lib/sandbox/workspace-identity.ts +19 -3
  193. package/lib/sandbox/workspace-view.ts +17 -15
  194. package/lib/task/activity-intent.ts +2 -2
  195. package/lib/task/activity-log.ts +27 -19
  196. package/lib/task/artifact-command.ts +85 -0
  197. package/lib/task/artifact-lifecycle.ts +31 -26
  198. package/lib/task/artifact-name.ts +55 -0
  199. package/lib/task/artifact-operations.ts +34 -310
  200. package/lib/task/artifact-receipts.ts +3 -14
  201. package/lib/task/artifact-recovery.ts +519 -0
  202. package/lib/task/artifact-repair-intent.ts +162 -33
  203. package/lib/task/artifact-schema.ts +5 -34
  204. package/lib/task/capabilities.ts +13 -22
  205. package/lib/task/codex-orchestration.ts +85 -24
  206. package/lib/task/commit-identity.ts +31 -0
  207. package/lib/task/commit-operation.ts +37 -20
  208. package/lib/task/control-authority.ts +540 -10
  209. package/lib/task/control-recovery.ts +186 -0
  210. package/lib/task/create-service.ts +1 -10
  211. package/lib/task/create.ts +63 -8
  212. package/lib/task/decision-details.ts +3 -71
  213. package/lib/task/delegation-receipts.ts +73 -2
  214. package/lib/task/events.ts +309 -24
  215. package/lib/task/finalization-state.ts +11 -0
  216. package/lib/task/human-override.ts +8 -16
  217. package/lib/task/implementation-inputs.ts +3 -3
  218. package/lib/task/intermediate-cleanup.ts +493 -0
  219. package/lib/task/invalidation.ts +18 -62
  220. package/lib/task/ledger-intents.ts +7 -24
  221. package/lib/task/ledger.ts +1 -3
  222. package/lib/task/lifecycle-recovery.ts +691 -0
  223. package/lib/task/lifecycle.ts +30 -5
  224. package/lib/task/local-artifact-finalization.ts +244 -234
  225. package/lib/task/manual-validation-completion.ts +40 -0
  226. package/lib/task/manual-validation-evidence.ts +209 -0
  227. package/lib/task/manual-validation-receipt.ts +156 -0
  228. package/lib/task/manual-validation-shared.ts +50 -0
  229. package/lib/task/manual-validation-transaction.ts +304 -0
  230. package/lib/task/markdown.ts +70 -0
  231. package/lib/task/orchestration.ts +84 -19
  232. package/lib/task/pr-delivery-fact.ts +15 -62
  233. package/lib/task/qualification-audit.ts +22 -33
  234. package/lib/task/recovery-warning.ts +21 -0
  235. package/lib/task/review-artifacts.ts +1 -20
  236. package/lib/task/review-command.ts +24 -0
  237. package/lib/task/review-finalization.ts +183 -292
  238. package/lib/task/review-fingerprint.ts +2 -2
  239. package/lib/task/sections.ts +33 -54
  240. package/lib/task/short-id.ts +27 -5
  241. package/lib/task/task-execution-lock.ts +1 -1
  242. package/lib/task/verification-engine.ts +113 -383
  243. package/lib/task/verification-types.ts +15 -0
  244. package/lib/task/verification.ts +1 -1
  245. package/lib/task/workflow-command.ts +17 -0
  246. package/lib/task/workflow-warning-intents.ts +0 -13
  247. package/lib/task/workflow-warnings.ts +21 -2
  248. package/lib/update.ts +6 -0
  249. package/lib/version.ts +1 -15
  250. package/package.json +6 -6
  251. package/templates/.agents/hooks/lifecycle-delegation.js +6 -6
  252. package/templates/.agents/rules/create-issue.en.md +1 -1
  253. package/templates/.agents/rules/create-issue.zh-CN.md +1 -1
  254. package/templates/.agents/rules/issue-sync.en.md +4 -2
  255. package/templates/.agents/rules/issue-sync.zh-CN.md +4 -3
  256. package/templates/.agents/rules/local-artifact-repair.en.md +46 -39
  257. package/templates/.agents/rules/local-artifact-repair.zh-CN.md +43 -36
  258. package/templates/.agents/rules/testing-discipline.en.md +2 -0
  259. package/templates/.agents/rules/testing-discipline.zh-CN.md +2 -0
  260. package/templates/.agents/scripts/package.json +3 -0
  261. package/templates/.agents/skills/analyze-task/SKILL.en.md +6 -6
  262. package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +6 -6
  263. package/templates/.agents/skills/analyze-task/config/verify.en.json +1 -1
  264. package/templates/.agents/skills/analyze-task/config/verify.zh-CN.json +1 -1
  265. package/templates/.agents/skills/block-task/SKILL.en.md +2 -2
  266. package/templates/.agents/skills/block-task/SKILL.zh-CN.md +2 -2
  267. package/templates/.agents/skills/block-task/config/verify.json +1 -1
  268. package/templates/.agents/skills/cancel-task/SKILL.en.md +3 -3
  269. package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +3 -3
  270. package/templates/.agents/skills/cancel-task/config/verify.json +1 -1
  271. package/templates/.agents/skills/code-task/SKILL.en.md +7 -7
  272. package/templates/.agents/skills/code-task/SKILL.zh-CN.md +7 -7
  273. package/templates/.agents/skills/code-task/config/verify.en.json +1 -1
  274. package/templates/.agents/skills/code-task/config/verify.zh-CN.json +1 -1
  275. package/templates/.agents/skills/commit/reference/issue-metadata-sync.en.md +1 -1
  276. package/templates/.agents/skills/commit/reference/issue-metadata-sync.zh-CN.md +1 -1
  277. package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +33 -21
  278. package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +33 -21
  279. package/templates/.agents/skills/complete-manual-validation/config/verify.en.json +1 -1
  280. package/templates/.agents/skills/complete-manual-validation/config/verify.zh-CN.json +1 -1
  281. package/templates/.agents/skills/complete-manual-validation/reference/report-template.en.md +2 -0
  282. package/templates/.agents/skills/complete-manual-validation/reference/report-template.zh-CN.md +2 -0
  283. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.en.md +16 -7
  284. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.zh-CN.md +16 -7
  285. package/templates/.agents/skills/complete-task/SKILL.en.md +3 -3
  286. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +3 -3
  287. package/templates/.agents/skills/complete-task/config/verify.en.json +2 -2
  288. package/templates/.agents/skills/complete-task/config/verify.zh-CN.json +2 -2
  289. package/templates/.agents/skills/complete-task/reference/external-delivery.en.md +1 -1
  290. package/templates/.agents/skills/complete-task/reference/external-delivery.zh-CN.md +1 -1
  291. package/templates/.agents/skills/create-pr/SKILL.en.md +2 -2
  292. package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +2 -2
  293. package/templates/.agents/skills/create-pr/config/verify.json +1 -1
  294. package/templates/.agents/skills/create-pr/reference/pr-body-template.en.md +2 -2
  295. package/templates/.agents/skills/create-pr/reference/pr-body-template.zh-CN.md +2 -2
  296. package/templates/.agents/skills/create-task/SKILL.en.md +5 -5
  297. package/templates/.agents/skills/create-task/SKILL.zh-CN.md +5 -5
  298. package/templates/.agents/skills/create-task/config/verify.json +1 -1
  299. package/templates/.agents/skills/import-issue/SKILL.en.md +8 -8
  300. package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +8 -8
  301. package/templates/.agents/skills/import-issue/config/verify.json +2 -2
  302. package/templates/.agents/skills/plan-task/SKILL.en.md +4 -4
  303. package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +4 -4
  304. package/templates/.agents/skills/plan-task/config/verify.en.json +1 -1
  305. package/templates/.agents/skills/plan-task/config/verify.zh-CN.json +1 -1
  306. package/templates/.agents/skills/release/SKILL.en.md +7 -3
  307. package/templates/.agents/skills/release/SKILL.zh-CN.md +7 -3
  308. package/templates/.agents/skills/restore-task/SKILL.en.md +2 -2
  309. package/templates/.agents/skills/restore-task/SKILL.zh-CN.md +2 -2
  310. package/templates/.agents/skills/restore-task/config/verify.json +1 -1
  311. package/templates/.agents/skills/review-analysis/SKILL.en.md +2 -2
  312. package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +2 -2
  313. package/templates/.agents/skills/review-analysis/config/verify.en.json +1 -1
  314. package/templates/.agents/skills/review-analysis/config/verify.zh-CN.json +1 -1
  315. package/templates/.agents/skills/review-analysis/reference/report-template.en.md +7 -7
  316. package/templates/.agents/skills/review-analysis/reference/report-template.zh-CN.md +6 -6
  317. package/templates/.agents/skills/review-code/SKILL.en.md +2 -2
  318. package/templates/.agents/skills/review-code/SKILL.zh-CN.md +2 -2
  319. package/templates/.agents/skills/review-code/config/verify.en.json +1 -1
  320. package/templates/.agents/skills/review-code/config/verify.zh-CN.json +1 -1
  321. package/templates/.agents/skills/review-code/reference/report-template.en.md +12 -12
  322. package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +12 -12
  323. package/templates/.agents/skills/review-plan/SKILL.en.md +2 -2
  324. package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +2 -2
  325. package/templates/.agents/skills/review-plan/config/verify.en.json +1 -1
  326. package/templates/.agents/skills/review-plan/config/verify.zh-CN.json +1 -1
  327. package/templates/.agents/skills/review-plan/reference/report-template.en.md +7 -7
  328. package/templates/.agents/skills/review-plan/reference/report-template.zh-CN.md +7 -7
  329. package/templates/.agents/skills/review-pr/config/verify.en.json +1 -1
  330. package/templates/.agents/skills/review-pr/config/verify.zh-CN.json +1 -1
  331. package/templates/.agents/skills/review-pr/reference/evidence-grading.en.md +3 -3
  332. package/templates/.agents/skills/review-pr/reference/evidence-grading.zh-CN.md +3 -3
  333. package/templates/.agents/skills/run-manual-validation/SKILL.en.md +20 -3
  334. package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +20 -3
  335. package/templates/.agents/skills/run-manual-validation/config/verify.en.json +1 -1
  336. package/templates/.agents/skills/run-manual-validation/config/verify.zh-CN.json +1 -1
  337. package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.en.md +4 -1
  338. package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.zh-CN.md +4 -1
  339. package/templates/.agents/skills/run-manual-validation/reference/report-template.en.md +2 -0
  340. package/templates/.agents/skills/run-manual-validation/reference/report-template.zh-CN.md +2 -0
  341. package/templates/.agents/skills/run-task/SKILL.en.md +1 -1
  342. package/templates/.agents/skills/run-task/SKILL.zh-CN.md +1 -1
  343. package/templates/.agents/skills/test/SKILL.en.md +17 -6
  344. package/templates/.agents/skills/test/SKILL.zh-CN.md +17 -6
  345. package/templates/.agents/templates/task.en.md +1 -1
  346. package/templates/.agents/templates/task.zh-CN.md +1 -1
  347. package/templates/.claude/commands/import-issue.en.md +1 -1
  348. package/templates/.claude/commands/import-issue.zh-CN.md +1 -1
  349. package/templates/.claude/commands/restore-task.en.md +1 -1
  350. package/templates/.claude/commands/restore-task.zh-CN.md +1 -1
  351. package/templates/.claude/commands/run-manual-validation.en.md +1 -1
  352. package/templates/.claude/commands/run-manual-validation.zh-CN.md +1 -1
  353. package/dist/lib/platform/adapters.js +0 -56
  354. package/lib/platform/adapters.ts +0 -197
@@ -1,7 +1,8 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import { appendActivityEntry, locateActivityLog, pairEntries, startedBackedRows } from './activity-log.js';
4
- import { buildArtifactLinkSection, artifactName, inspectArtifactDirectory, parseReviewedInputReference, parseCodePlanInputReference, parseArtifactName, resolveArtifactContext, validateCompletedArtifact } from './artifact-lifecycle.js';
4
+ import { buildArtifactLinkSection, inspectArtifactDirectory, parseReviewedInputReference, parseCodePlanInputReference, resolveArtifactContext, validateCompletedArtifact } from './artifact-lifecycle.js';
5
+ import { artifactName, parseArtifactName } from './artifact-name.js';
5
6
  import { ArtifactReceiptError, parseArtifactReceipts, sha256File, upsertArtifactReceipt } from './artifact-receipts.js';
6
7
  import { parseTypedTaskFrontmatter } from './frontmatter.js';
7
8
  import { consumeImplementationInput, IMPLEMENTATION_INPUT_ALIASES, parseImplementationInputs, renderImplementationInputs } from './implementation-inputs.js';
@@ -15,12 +16,16 @@ import { TaskExecutionLockError, withTaskExecutionLock } from './task-execution-
15
16
  import { captureTaskWriteMetadata, writeTask } from './write.js';
16
17
  import { allowsManualOverride } from './guard-override.js';
17
18
  import { consumeLocalArtifactFinalizationIntent, readLocalArtifactFinalizationIntent, validateLocalArtifact } from './local-artifact-finalization.js';
19
+ import { writeArtifactRecoveryIntent, readArtifactRecoveryIntent } from './artifact-repair-intent.js';
20
+ import { reconcileArtifactRecovery, recoveryContextFromIntent, sha256Content } from './artifact-recovery.js';
18
21
  import { buildLifecycleFacts, canStart } from './capabilities.js';
19
22
  import { createInvalidationOperation, invalidationMutation, parseInvalidationDocument, targetIdFor, upsertInvalidation } from './invalidation.js';
20
23
  import { consumeReworkIntents, parseReworkIntentDocument, reworkIntentMutation, supersedeReworkIntents } from './rework-intent.js';
21
24
  import { ARTIFACT_FAMILIES, expectedQualificationRelations, parseQualificationAudit, parseTaskQualification, upstreamArtifactDigest, validateQualificationAudit } from './qualification-audit.js';
22
25
  import { getArtifactSchema } from './artifact-schema.js';
23
- import { inspectArtifactContract } from './artifact-operations.js';
26
+ import { canonicalSemanticDigest, inspectArtifactContract } from './artifact-operations.js';
27
+ import { consumeLifecycleRecoveryAttestation, lifecycleRecoveryAttestationDigest, validateLifecycleRecoveryAttestation } from './control-authority.js';
28
+ import { readManualValidationCompletion } from './manual-validation-completion.js';
24
29
  const eventCatalog = [
25
30
  'analyze.started', 'analyze.awaiting-input', 'analyze.completed',
26
31
  'review-analysis.started', 'review-analysis.completed',
@@ -46,8 +51,8 @@ const SCHEMAS = {
46
51
  'code.completed': { required: ['artifact', 'artifactSha256', 'semanticDigest'], optional: ['round', 'fixFor', 'implementationInput', 'filesModified', 'testsPassed', 'blockers', 'major', 'minor', 'manualValidation', 'orchestrated'] },
47
52
  'review-code.started': { optional: ['round'] },
48
53
  'review-code.completed': { required: ['artifact', 'verdict', 'blockers', 'major', 'minor', 'manualValidation'], optional: ['round', 'orchestrated'] },
49
- 'manual-validation.started': { optional: ['round'] },
50
- 'manual-validation.completed': { required: ['artifact', 'summaryResult'], optional: ['round'] },
54
+ 'manual-validation.started': { optional: ['round', 'transactionId'] },
55
+ 'manual-validation.completed': { required: ['artifact', 'summaryResult', 'transactionId', 'receiptDigest', 'prHeadSha'], optional: ['round'] },
51
56
  'validation-run.started': { optional: ['round'] },
52
57
  'validation-run.completed': { required: ['artifact'], optional: ['round'] }
53
58
  };
@@ -92,7 +97,7 @@ function validateTaskEventRequest(request) {
92
97
  if (modeRequired.some((key) => request[key] === undefined) || forbidden.some((key) => request[key] !== undefined))
93
98
  return { code: 'EVENT_PAYLOAD_INVALID', message: 'code.completed requires either initial or fix completion payload' };
94
99
  }
95
- if (request.fixFor && !/^review-code(?:-r(?:[2-9]|[1-9]\d+))?\.md$/.test(request.fixFor))
100
+ if (request.fixFor && parseArtifactName(request.fixFor)?.family !== 'review-code')
96
101
  return { code: 'EVENT_PAYLOAD_INVALID', message: 'fixFor must reference a canonical review-code artifact' };
97
102
  if (request.implementationInput && !/^II-[1-9]\d*$/.test(request.implementationInput))
98
103
  return { code: 'EVENT_PAYLOAD_INVALID', message: 'implementationInput must be a canonical II-N id' };
@@ -104,6 +109,19 @@ function validateTaskEventRequest(request) {
104
109
  return { code: 'EVENT_PAYLOAD_INVALID', message: 'fixFor and implementationInput are mutually exclusive' };
105
110
  if (request.summaryResult !== undefined && (!request.summaryResult.trim() || /[\r\n]/.test(request.summaryResult)))
106
111
  return { code: 'EVENT_PAYLOAD_INVALID', message: 'summaryResult must be a non-empty single line' };
112
+ if (request.event === 'manual-validation.completed') {
113
+ for (const [name, value, pattern] of [
114
+ ['transactionId', request.transactionId, /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/u],
115
+ ['receiptDigest', request.receiptDigest, /^[a-f0-9]{64}$/u],
116
+ ['prHeadSha', request.prHeadSha, /^[a-f0-9]{40}$/u]
117
+ ])
118
+ if (!value || !pattern.test(value))
119
+ return { code: 'EVENT_PAYLOAD_INVALID', message: `manual-validation.completed requires a valid ${name}` };
120
+ }
121
+ if (request.event === 'manual-validation.started' && request.transactionId !== undefined
122
+ && !/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/u.test(request.transactionId)) {
123
+ return { code: 'EVENT_PAYLOAD_INVALID', message: 'manual-validation.started requires a valid transactionId' };
124
+ }
107
125
  if (request.event !== 'analyze.awaiting-input' && /\.(?:started|completed)$/.test(request.event)) {
108
126
  if (!request.initiator || !request.requestId || !request.reasonCode) {
109
127
  return { code: 'EVENT_TRIGGER_REQUIRED', message: `${request.event} requires --initiator, --request-id, and --reason-code` };
@@ -202,7 +220,9 @@ function identity(request) {
202
220
  if (family === 'manual-validation') {
203
221
  return {
204
222
  family, phase, action: spec.label,
205
- note: phase === 'started' ? 'started' : `Manual validation passed → ${request.artifact}; ${request.summaryResult}`,
223
+ note: phase === 'started'
224
+ ? `started${request.transactionId ? `; transaction=${request.transactionId}` : ''}`
225
+ : `Manual validation passed → ${request.artifact}; ${request.summaryResult}; transaction=${request.transactionId}; receipt=${request.receiptDigest}; head=${request.prHeadSha}`,
206
226
  target: null
207
227
  };
208
228
  }
@@ -257,6 +277,24 @@ function normalizeStarted(request, repoRoot) {
257
277
  return { error: { code: 'EVENT_ARTIFACT_CONFLICT', message: `implementationInput '${request.implementationInput ?? ''}' conflicts with artifact context` }, context };
258
278
  return { request: { ...request, round, artifact: context.next.name, fixFor: expectedFix, implementationInput: expectedImplementation }, context };
259
279
  }
280
+ function validateManualValidationCompletion(taskDir, taskId, request, artifactPath) {
281
+ const completion = readManualValidationCompletion(taskDir, {
282
+ transactionId: request.transactionId,
283
+ taskId,
284
+ prHeadSha: request.prHeadSha,
285
+ artifact: request.artifact,
286
+ receiptDigest: request.receiptDigest
287
+ });
288
+ if (!completion.ok)
289
+ return completion.error;
290
+ const { receipt, transaction } = completion.value;
291
+ if (!['receipt-committed', 'final-promotion-in-progress', 'committed'].includes(transaction.phase)) {
292
+ return { code: 'MANUAL_VALIDATION_TRANSACTION_PHASE_INVALID', message: 'manual validation completion requires the receipt-backed transaction phase' };
293
+ }
294
+ if (sha256File(artifactPath) !== receipt.artifactSha256)
295
+ return { code: 'MANUAL_VALIDATION_RECEIPT_INVALID', message: 'manual-validation artifact digest does not match the receipt' };
296
+ return null;
297
+ }
260
298
  function openStartedIdentity(rows, family) {
261
299
  const spec = FAMILY[family];
262
300
  if (family === 'manual-validation') {
@@ -277,6 +315,59 @@ function openStartedIdentity(rows, family) {
277
315
  function reviewInputFamily(family) {
278
316
  return family === 'review-analysis' ? 'analysis' : family === 'review-plan' ? 'plan' : 'code';
279
317
  }
318
+ function reconcileReviewCompletionReplay(repoRoot, taskDir, taskId, family, artifact, round) {
319
+ const stage = REVIEW_LEDGER_STAGES[family];
320
+ if (!stage)
321
+ return null;
322
+ const reviewFamily = family;
323
+ try {
324
+ let intent = readArtifactRecoveryIntent(repoRoot, taskId, reviewFamily, artifact);
325
+ if (!intent)
326
+ return null;
327
+ if (intent.state === 'commit-started') {
328
+ const reconciled = reconcileArtifactRecovery(recoveryContextFromIntent(repoRoot, taskDir, intent), { lockAlreadyHeld: true });
329
+ if (reconciled.status !== 'passed' && reconciled.status !== 'consumed') {
330
+ return {
331
+ code: 'EVENT_ARTIFACT_CONFLICT',
332
+ message: `review finalizer recovery remains ${reconciled.status} for ${artifact}`
333
+ };
334
+ }
335
+ intent = reconciled.intent;
336
+ }
337
+ if (!['passed', 'consumed'].includes(intent.state)) {
338
+ return {
339
+ code: 'EVENT_ARTIFACT_CONFLICT',
340
+ message: `review finalizer provenance is incomplete for ${artifact}`
341
+ };
342
+ }
343
+ const validated = validateCompletedArtifact(taskDir, FAMILY[family].artifact, artifact, round);
344
+ if (!validated.ok) {
345
+ return { code: 'EVENT_ARTIFACT_CONFLICT', message: validated.error.message };
346
+ }
347
+ const content = fs.readFileSync(validated.artifact.path, 'utf8');
348
+ const expectedRequestId = `review-finalize:${taskId}:${stage}:${round}`;
349
+ if (intent.round !== round
350
+ || intent.requestId !== expectedRequestId
351
+ || intent.finalArtifactSha256 !== sha256Content(content)
352
+ || intent.finalSemanticDigest !== canonicalSemanticDigest(content)) {
353
+ return {
354
+ code: 'EVENT_ARTIFACT_CONFLICT',
355
+ message: `review finalizer provenance does not match ${artifact}`
356
+ };
357
+ }
358
+ if (intent.state === 'passed') {
359
+ const consumed = { ...intent, state: 'consumed', updatedAt: Date.now() };
360
+ writeArtifactRecoveryIntent(repoRoot, consumed, { expected: intent });
361
+ }
362
+ return null;
363
+ }
364
+ catch (error) {
365
+ return {
366
+ code: 'EVENT_ARTIFACT_CONFLICT',
367
+ message: `review finalizer recovery could not be reconciled for ${artifact}: ${error instanceof Error ? error.message : String(error)}`
368
+ };
369
+ }
370
+ }
280
371
  function relationForStartedInput(family, input) {
281
372
  if (family.startsWith('review-')) {
282
373
  return input.family === reviewInputFamily(family) ? 'reviewed-input' : 'approval-context';
@@ -623,6 +714,11 @@ function applyTaskEventUnlocked(request, options = {}) {
623
714
  if (openIdentity) {
624
715
  if (openIdentity.row.agent !== request.agent)
625
716
  return failed(request, { code: 'EVENT_LOG_CONFLICT', message: 'open started event has a different agent' }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath });
717
+ if (initialParts.family === 'manual-validation' && request.transactionId !== undefined) {
718
+ const existingTransactionId = /(?:^|;\s*)transaction=([A-Za-z0-9][A-Za-z0-9._-]{0,127})(?:;|$)/u.exec(openIdentity.row.note)?.[1];
719
+ if (existingTransactionId !== undefined && existingTransactionId !== request.transactionId)
720
+ return failed(request, { code: 'EVENT_LOG_CONFLICT', message: 'open manual-validation started event has a different transactionId' }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath });
721
+ }
626
722
  if (request.round !== undefined && request.round !== openIdentity.round)
627
723
  return failed(request, { code: 'EVENT_ARTIFACT_CONFLICT', message: `round ${request.round} conflicts with open round ${openIdentity.round}` }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath });
628
724
  if (request.fixFor !== undefined && request.fixFor !== openIdentity.fixFor)
@@ -644,6 +740,25 @@ function applyTaskEventUnlocked(request, options = {}) {
644
740
  normalized = { ...request, round: identity.round };
645
741
  }
646
742
  const eventIdentity = identity(normalized);
743
+ const lifecycleAuthority = options.lifecycleRecoveryAttestation ?? null;
744
+ if (lifecycleAuthority) {
745
+ try {
746
+ validateLifecycleRecoveryAttestation(lifecycleAuthority);
747
+ }
748
+ catch (error) {
749
+ return failed(normalized, { code: 'EVENT_ARTIFACT_CONFLICT', message: error instanceof Error ? error.message : String(error) }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath });
750
+ }
751
+ const expectedFamily = eventIdentity.family === 'analyze' ? 'analysis' : eventIdentity.family;
752
+ if (eventIdentity.phase !== 'completed'
753
+ || lifecycleAuthority.phase !== 'task-event.completed'
754
+ || lifecycleAuthority.taskId !== resolved.taskId
755
+ || lifecycleAuthority.family !== expectedFamily
756
+ || lifecycleAuthority.artifact !== normalized.artifact
757
+ || lifecycleAuthority.round !== normalized.round
758
+ || (normalized.requestId !== undefined && lifecycleAuthority.lifecycleRequestId !== normalized.requestId)) {
759
+ return failed(normalized, { code: 'EVENT_ARTIFACT_CONFLICT', message: 'lifecycle completion authority does not match the event tuple' }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath });
760
+ }
761
+ }
647
762
  const currentStep = typeof frontmatter.current_step === 'string' ? frontmatter.current_step : '';
648
763
  const matchingRows = rows.filter((item) => item.step === eventIdentity.action);
649
764
  const manual = eventIdentity.family === 'manual-validation';
@@ -654,16 +769,30 @@ function applyTaskEventUnlocked(request, options = {}) {
654
769
  const row = manual ? openRows.at(-1) : matchingRows[0];
655
770
  let completedArtifact = null;
656
771
  let localFinalizationIntent = null;
772
+ let reviewFinalizationIntent = null;
773
+ let reviewContent = null;
657
774
  if (eventIdentity.phase === 'started' && row) {
658
775
  if (row.agent !== normalized.agent)
659
776
  return failed(normalized, { code: 'EVENT_LOG_CONFLICT', message: 'open started event has a different agent' }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath });
777
+ if (manual && normalized.transactionId !== undefined) {
778
+ const existingTransactionId = /(?:^|;\s*)transaction=([A-Za-z0-9][A-Za-z0-9._-]{0,127})(?:;|$)/u.exec(row.note)?.[1];
779
+ if (existingTransactionId !== undefined && existingTransactionId !== normalized.transactionId) {
780
+ return failed(normalized, { code: 'EVENT_LOG_CONFLICT', message: 'open manual-validation started event has a different transactionId' }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath });
781
+ }
782
+ }
660
783
  return successNoOp(normalized, resolved.taskId, resolved.taskMdPath, currentStep, eventIdentity, row.started, frontmatter, artifactContext);
661
784
  }
662
785
  if (eventIdentity.phase === 'started' && !manual && completedRows.length > 0)
663
786
  return failed(normalized, { code: 'EVENT_ALREADY_COMPLETED', message: 'event identity is already completed' }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath });
664
787
  const done = completedRows.find((item) => item.note === eventIdentity.note);
665
- if (eventIdentity.phase === 'completed' && done)
788
+ if (eventIdentity.phase === 'completed' && done) {
789
+ if (!normalized.dryRun && eventIdentity.family.startsWith('review-') && normalized.artifact && normalized.round) {
790
+ const recoveryError = reconcileReviewCompletionReplay(resolved.repoRoot, resolved.taskDir, resolved.taskId, eventIdentity.family, normalized.artifact, normalized.round);
791
+ if (recoveryError)
792
+ return failed(normalized, recoveryError, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase });
793
+ }
666
794
  return successNoOp(normalized, resolved.taskId, resolved.taskMdPath, currentStep, eventIdentity, done.done, frontmatter, artifactContext);
795
+ }
667
796
  if (eventIdentity.phase === 'completed' && completedRows.length > 0 && openRows.length === 0)
668
797
  return failed(normalized, { code: 'EVENT_LOG_CONFLICT', message: 'event identity is already completed with a different payload' }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase });
669
798
  if (eventIdentity.phase === 'completed' && !row?.started && !allowsManualOverride(options.manualOverride, 'task-event', 'EVENT_START_MISSING'))
@@ -673,8 +802,12 @@ function applyTaskEventUnlocked(request, options = {}) {
673
802
  if (!validated.ok)
674
803
  return failed(normalized, validated.error, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase });
675
804
  completedArtifact = validated.artifact;
805
+ if (eventIdentity.family === 'manual-validation') {
806
+ const validationError = validateManualValidationCompletion(resolved.taskDir, resolved.taskId, normalized, completedArtifact.path);
807
+ if (validationError)
808
+ return failed(normalized, validationError, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase });
809
+ }
676
810
  if (eventIdentity.family.startsWith('review-')) {
677
- let reviewContent;
678
811
  try {
679
812
  reviewContent = fs.readFileSync(completedArtifact.path, 'utf8');
680
813
  }
@@ -703,6 +836,20 @@ function applyTaskEventUnlocked(request, options = {}) {
703
836
  const localFamily = eventIdentity.family === 'analyze'
704
837
  ? 'analysis'
705
838
  : eventIdentity.family === 'plan' ? 'plan' : 'code';
839
+ try {
840
+ localFinalizationIntent = readLocalArtifactFinalizationIntent(resolved.repoRoot, resolved.taskId, localFamily, completedArtifact.name);
841
+ if (localFinalizationIntent?.state === 'commit-started') {
842
+ const reconciled = reconcileArtifactRecovery(recoveryContextFromIntent(resolved.repoRoot, resolved.taskDir, localFinalizationIntent), { lockAlreadyHeld: true });
843
+ if (reconciled.status === 'passed' || reconciled.status === 'consumed')
844
+ localFinalizationIntent = reconciled.intent;
845
+ }
846
+ }
847
+ catch (error) {
848
+ return failed(normalized, {
849
+ code: 'EVENT_ARTIFACT_CONFLICT',
850
+ message: `local finalizer recovery could not be reconciled for ${completedArtifact.name}: ${error instanceof Error ? error.message : String(error)}`
851
+ }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase });
852
+ }
706
853
  let artifactContent;
707
854
  try {
708
855
  artifactContent = fs.readFileSync(completedArtifact.path, 'utf8');
@@ -734,27 +881,29 @@ function applyTaskEventUnlocked(request, options = {}) {
734
881
  message: `semantic digest does not match finalizer result for ${completedArtifact.name}`
735
882
  }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase });
736
883
  }
737
- try {
738
- localFinalizationIntent = readLocalArtifactFinalizationIntent(resolved.repoRoot, resolved.taskId, localFamily, completedArtifact.name);
739
- }
740
- catch (error) {
741
- return failed(normalized, {
742
- code: 'EVENT_ARTIFACT_CONFLICT',
743
- message: `cannot read local finalizer provenance for ${completedArtifact.name}: ${error instanceof Error ? error.message : String(error)}`
744
- }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase });
745
- }
746
884
  if (!localFinalizationIntent || !['passed', 'consumed'].includes(localFinalizationIntent.state)) {
747
885
  return failed(normalized, {
748
886
  code: 'EVENT_ARTIFACT_CONFLICT',
749
887
  message: `local finalizer provenance is missing or incomplete for ${completedArtifact.name}`
750
888
  }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase });
751
889
  }
752
- if (localFinalizationIntent.artifactSha256 !== actualSha256 || localFinalizationIntent.semanticDigest !== local.semanticDigest) {
890
+ if (localFinalizationIntent.finalArtifactSha256 !== actualSha256 || localFinalizationIntent.finalSemanticDigest !== local.semanticDigest) {
753
891
  return failed(normalized, {
754
892
  code: 'EVENT_ARTIFACT_CONFLICT',
755
893
  message: `local finalizer provenance does not match ${completedArtifact.name}`
756
894
  }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase });
757
895
  }
896
+ if (localFinalizationIntent.authorityDigest !== null) {
897
+ if (!lifecycleAuthority
898
+ || localFinalizationIntent.recoveryOperationId !== lifecycleAuthority.operationId
899
+ || localFinalizationIntent.requestId !== lifecycleAuthority.lifecycleRequestId
900
+ || localFinalizationIntent.authorityDigest !== lifecycleRecoveryAttestationDigest(lifecycleAuthority)) {
901
+ return failed(normalized, {
902
+ code: 'EVENT_ARTIFACT_CONFLICT',
903
+ message: `local finalizer provenance does not match lifecycle completion authority for ${completedArtifact.name}`
904
+ }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase, artifactContext });
905
+ }
906
+ }
758
907
  }
759
908
  }
760
909
  if (eventIdentity.phase === 'waiting' && section.entries.some((entry) => entry.step === eventIdentity.action && entry.note === eventIdentity.note)) {
@@ -778,6 +927,43 @@ function applyTaskEventUnlocked(request, options = {}) {
778
927
  const findingCountError = validateReviewFindingCounts(normalized, content, eventIdentity.family, completedArtifact?.path ?? null);
779
928
  if (findingCountError)
780
929
  return failed(normalized, findingCountError, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase });
930
+ if (eventIdentity.phase === 'completed' && eventIdentity.family.startsWith('review-') && completedArtifact && reviewContent !== null) {
931
+ try {
932
+ reviewFinalizationIntent = readArtifactRecoveryIntent(resolved.repoRoot, resolved.taskId, eventIdentity.family, completedArtifact.name);
933
+ if (!normalized.dryRun && reviewFinalizationIntent?.state === 'commit-started') {
934
+ const reconciled = reconcileArtifactRecovery(recoveryContextFromIntent(resolved.repoRoot, resolved.taskDir, reviewFinalizationIntent), { lockAlreadyHeld: true });
935
+ if (reconciled.status === 'passed' || reconciled.status === 'consumed')
936
+ reviewFinalizationIntent = reconciled.intent;
937
+ }
938
+ if (!reviewFinalizationIntent || !['passed', 'consumed'].includes(reviewFinalizationIntent.state)) {
939
+ return failed(normalized, {
940
+ code: 'EVENT_ARTIFACT_CONFLICT',
941
+ message: `review finalizer provenance is missing or incomplete for ${completedArtifact.name}`
942
+ }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase });
943
+ }
944
+ const reviewStage = eventIdentity.family === 'review-analysis'
945
+ ? 'analysis'
946
+ : eventIdentity.family === 'review-plan' ? 'plan' : 'code';
947
+ const actualSha256 = sha256File(completedArtifact.path);
948
+ const actualSemanticDigest = canonicalSemanticDigest(reviewContent);
949
+ const expectedRequestId = `review-finalize:${resolved.taskId}:${reviewStage}:${normalized.round}`;
950
+ if (reviewFinalizationIntent.round !== normalized.round
951
+ || reviewFinalizationIntent.requestId !== expectedRequestId
952
+ || reviewFinalizationIntent.finalArtifactSha256 !== actualSha256
953
+ || reviewFinalizationIntent.finalSemanticDigest !== actualSemanticDigest) {
954
+ return failed(normalized, {
955
+ code: 'EVENT_ARTIFACT_CONFLICT',
956
+ message: `review finalizer provenance does not match ${completedArtifact.name}`
957
+ }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase });
958
+ }
959
+ }
960
+ catch (error) {
961
+ return failed(normalized, {
962
+ code: 'EVENT_ARTIFACT_CONFLICT',
963
+ message: `review finalizer recovery could not be reconciled for ${completedArtifact.name}: ${error instanceof Error ? error.message : String(error)}`
964
+ }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase });
965
+ }
966
+ }
781
967
  let orchestrationCompletion = null;
782
968
  if (eventIdentity.phase === 'completed' && eventIdentity.family !== 'manual-validation' && eventIdentity.family !== 'validation-run') {
783
969
  const orchestrationStage = eventIdentity.family === 'analyze' ? 'analysis' : eventIdentity.family;
@@ -914,14 +1100,54 @@ function applyTaskEventUnlocked(request, options = {}) {
914
1100
  });
915
1101
  }
916
1102
  mutations.push({ kind: 'section', aliases: ['活动日志', 'Activity Log'], heading: section.heading, body });
917
- if (!normalized.dryRun && localFinalizationIntent && completedArtifact && localFinalizationIntent.state === 'passed') {
1103
+ if (!normalized.dryRun && localFinalizationIntent && completedArtifact) {
1104
+ try {
1105
+ if (lifecycleAuthority && localFinalizationIntent.state === 'passed') {
1106
+ const expectedIntent = localFinalizationIntent;
1107
+ localFinalizationIntent = {
1108
+ ...localFinalizationIntent,
1109
+ state: 'commit-started',
1110
+ phase: 'task-event.completed',
1111
+ authorityDigest: lifecycleRecoveryAttestationDigest(lifecycleAuthority),
1112
+ requestId: lifecycleAuthority.lifecycleRequestId,
1113
+ updatedAt: Date.now()
1114
+ };
1115
+ writeArtifactRecoveryIntent(resolved.repoRoot, localFinalizationIntent, { expected: expectedIntent });
1116
+ }
1117
+ else if (!lifecycleAuthority && localFinalizationIntent.state === 'passed') {
1118
+ localFinalizationIntent = consumeLocalArtifactFinalizationIntent(resolved.repoRoot, localFinalizationIntent, { lockAlreadyHeld: options.lockAlreadyHeld });
1119
+ }
1120
+ }
1121
+ catch (error) {
1122
+ return failed(normalized, {
1123
+ code: 'EVENT_ARTIFACT_CONFLICT',
1124
+ message: `local finalizer provenance could not be advanced before task write for ${completedArtifact.name}: ${error instanceof Error ? error.message : String(error)}`
1125
+ }, {
1126
+ taskId: resolved.taskId,
1127
+ taskMdPath: resolved.taskMdPath,
1128
+ fromStep: currentStep,
1129
+ toStep: step,
1130
+ action: eventIdentity.action,
1131
+ phase: eventIdentity.phase,
1132
+ artifactContext
1133
+ });
1134
+ }
1135
+ }
1136
+ if (!normalized.dryRun && reviewFinalizationIntent && completedArtifact && reviewFinalizationIntent.state === 'passed') {
918
1137
  try {
919
- localFinalizationIntent = consumeLocalArtifactFinalizationIntent(resolved.repoRoot, localFinalizationIntent);
1138
+ const expectedIntent = reviewFinalizationIntent;
1139
+ reviewFinalizationIntent = {
1140
+ ...reviewFinalizationIntent,
1141
+ state: 'commit-started',
1142
+ phase: 'task-event.completed',
1143
+ updatedAt: Date.now()
1144
+ };
1145
+ writeArtifactRecoveryIntent(resolved.repoRoot, reviewFinalizationIntent, { expected: expectedIntent });
920
1146
  }
921
1147
  catch (error) {
922
1148
  return failed(normalized, {
923
1149
  code: 'EVENT_ARTIFACT_CONFLICT',
924
- message: `local finalizer provenance could not be consumed before task write for ${completedArtifact.name}: ${error instanceof Error ? error.message : String(error)}`
1150
+ message: `review finalizer provenance could not be advanced before task write for ${completedArtifact.name}: ${error instanceof Error ? error.message : String(error)}`
925
1151
  }, {
926
1152
  taskId: resolved.taskId,
927
1153
  taskMdPath: resolved.taskMdPath,
@@ -938,6 +1164,33 @@ function applyTaskEventUnlocked(request, options = {}) {
938
1164
  const result = writeTask({ taskRef: normalized.taskRef, expectedState: stateOverride ? resolved.state : 'active', dryRun: normalized.dryRun, mutations }, { ...options, invalidationContext: sourceCompletion ? 'source-completion' : 'standard', metadataProvider: () => metadata });
939
1165
  if (result.status === 'failed')
940
1166
  return failed(normalized, result.error, { taskId: result.taskId, taskMdPath: result.taskMdPath, fromStep: currentStep, toStep: step, action: eventIdentity.action, phase: eventIdentity.phase, timestamp: result.timestamp, agentInfraVersion: result.agentInfraVersion, operations: result.operations, artifactContext });
1167
+ if (!normalized.dryRun && lifecycleAuthority && localFinalizationIntent && completedArtifact) {
1168
+ try {
1169
+ if (!options.deferLifecycleRecoveryConsumption)
1170
+ consumeLifecycleRecoveryAttestation(lifecycleAuthority);
1171
+ writeArtifactRecoveryIntent(resolved.repoRoot, { ...localFinalizationIntent, state: 'consumed', updatedAt: Date.now() }, { expected: localFinalizationIntent });
1172
+ }
1173
+ catch (error) {
1174
+ return failed(normalized, {
1175
+ code: 'EVENT_ARTIFACT_CONFLICT',
1176
+ message: `task completion was written but lifecycle recovery consumption is incomplete: ${error instanceof Error ? error.message : String(error)}`
1177
+ }, { taskId: result.taskId, taskMdPath: result.taskMdPath, fromStep: currentStep, toStep: step, action: eventIdentity.action, phase: eventIdentity.phase, timestamp: result.timestamp, agentInfraVersion: result.agentInfraVersion, operations: result.operations, artifactContext });
1178
+ }
1179
+ }
1180
+ if (!normalized.dryRun && reviewFinalizationIntent && completedArtifact) {
1181
+ try {
1182
+ if (reviewFinalizationIntent.state === 'commit-started') {
1183
+ const consumed = { ...reviewFinalizationIntent, state: 'consumed', updatedAt: Date.now() };
1184
+ writeArtifactRecoveryIntent(resolved.repoRoot, consumed, { expected: reviewFinalizationIntent });
1185
+ }
1186
+ }
1187
+ catch (error) {
1188
+ return failed(normalized, {
1189
+ code: 'EVENT_ARTIFACT_CONFLICT',
1190
+ message: `review finalizer recovery consumption is incomplete: ${error instanceof Error ? error.message : String(error)}`
1191
+ }, { taskId: result.taskId, taskMdPath: result.taskMdPath, fromStep: currentStep, toStep: step, action: eventIdentity.action, phase: eventIdentity.phase, timestamp: result.timestamp, agentInfraVersion: result.agentInfraVersion, operations: result.operations, artifactContext });
1192
+ }
1193
+ }
941
1194
  if (!normalized.dryRun && orchestrationCompletion) {
942
1195
  try {
943
1196
  (options.commitOrchestrationCompletion ?? commitOrchestrationStageCompletion)(orchestrationCompletion);
@@ -984,7 +1237,7 @@ function applyTaskEvent(request, options = {}) {
984
1237
  if (!resolved.ok)
985
1238
  return applyTaskEventUnlocked(request, options);
986
1239
  try {
987
- return withTaskExecutionLock(resolved.repoRoot, resolved.taskId, `task-event.${request.event}`, () => applyTaskEventUnlocked(request, options));
1240
+ return withTaskExecutionLock(resolved.repoRoot, resolved.taskId, `task-event.${request.event}`, () => applyTaskEventUnlocked(request, { ...options, lockAlreadyHeld: true }));
988
1241
  }
989
1242
  catch (error) {
990
1243
  if (!(error instanceof TaskExecutionLockError))
@@ -0,0 +1,9 @@
1
+ export function taskFinalizationReceiptState(receipt) {
2
+ if (receipt.lifecycle !== 'done' || receipt.taskComment === 'pending'
3
+ || receipt.verification === 'pending' || receipt.warningProjection !== 'done')
4
+ return 'pending';
5
+ if (receipt.lastError !== null || receipt.warnings.some((warning) => warning.status === 'open'))
6
+ return 'unresolved';
7
+ return 'complete';
8
+ }
9
+ //# sourceMappingURL=finalization-state.js.map
@@ -1,6 +1,7 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import { createHash, randomUUID } from 'node:crypto';
4
+ import { parseArtifactName } from './artifact-name.js';
4
5
  import { locateActivityLog } from './activity-log.js';
5
6
  import { applyPrReviewActivityIntent } from './activity-intent.js';
6
7
  import { applyHumanDecision } from './decision-intents.js';
@@ -406,10 +407,7 @@ function resourceForSnapshot(policy, options, frontmatter) {
406
407
  if (policy.producerId === 'platform.issue') {
407
408
  const identity = options.probeIssueNumber !== undefined
408
409
  ? resourceIdentity(options.probeIssueNumber)
409
- : taskIssueIdentity((frontmatter || {})) || (() => {
410
- const number = toPositiveInteger(frontmatter?.issue_number);
411
- return number === null ? null : resourceIdentity(number);
412
- })();
410
+ : taskIssueIdentity((frontmatter || {}));
413
411
  return identity ? { kind: 'issue', identity, number: resourceIdentityNumber(identity) } : null;
414
412
  }
415
413
  if (policy.producerId === 'platform.pull-request') {
@@ -480,11 +478,11 @@ async function probePlatformFailure(taskRef, policy, resolved, options) {
480
478
  }
481
479
  const issueIdentity = options.probeIssueNumber !== undefined
482
480
  ? resourceIdentity(options.probeIssueNumber)
483
- : taskIssueIdentity(frontmatter) || (() => {
484
- const number = toPositiveInteger(frontmatter.issue_number);
485
- return number === null ? null : resourceIdentity(number);
486
- })();
481
+ : taskIssueIdentity(frontmatter);
487
482
  const fact = readPrDeliveryFact(frontmatter);
483
+ if (fact.status === 'invalid') {
484
+ return error(fact.error.code, fact.error.message);
485
+ }
488
486
  const pullRequestIdentity = options.probePullRequestNumber !== undefined
489
487
  ? resourceIdentity(options.probePullRequestNumber)
490
488
  : fact.status === 'valid' && fact.fact.state === 'bound' ? fact.fact.identity.resource : null;
@@ -577,12 +575,8 @@ function latestArtifact(taskDir, family) {
577
575
  catch {
578
576
  return undefined;
579
577
  }
580
- const candidates = names.filter((name) => (name === `${family}.md` || new RegExp(`^${family}-r\\d+\\.md$`).test(name)));
581
- candidates.sort((a, b) => {
582
- const round = (name) => Number(/-r(\d+)\.md$/.exec(name)?.[1] ?? 1);
583
- return round(b) - round(a);
584
- });
585
- return candidates[0];
578
+ return names.map(parseArtifactName).filter((identity) => identity !== null)
579
+ .filter((identity) => identity.family === family).sort((left, right) => right.round - left.round)[0]?.name;
586
580
  }
587
581
  async function probeVerificationEngineFailure(policy, resolved) {
588
582
  const candidates = [
@@ -1,3 +1,4 @@
1
+ import { parseArtifactName } from './artifact-name.js';
1
2
  import { parseTable } from './sections.js';
2
3
  const SECTION_ALIASES = ['实现输入', 'Implementation Inputs'];
3
4
  const COLUMNS = [
@@ -5,7 +6,6 @@ const COLUMNS = [
5
6
  'decided_at', 'status', 'consumed_by'
6
7
  ];
7
8
  const TIMESTAMP_RE = /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}[+-]\d{2}:\d{2}$/;
8
- const ARTIFACT_RE = /^code(?:-r(?:[2-9]|[1-9]\d+))?\.md$/;
9
9
  function validateInput(row) {
10
10
  if (!/^II-[1-9]\d*$/.test(row.id))
11
11
  throw new Error(`implementation input id '${row.id}' is invalid`);
@@ -17,7 +17,7 @@ function validateInput(row) {
17
17
  const valid = (row.status === 'declared' && row.decidedAt === '' && row.consumedBy === '') ||
18
18
  (hasValidTimestamp && row.needsImplementation && row.status === 'pending' && row.consumedBy === '') ||
19
19
  (hasValidTimestamp && !row.needsImplementation && row.status === 'not-required' && row.consumedBy === '') ||
20
- (hasValidTimestamp && row.needsImplementation && row.status === 'consumed' && ARTIFACT_RE.test(row.consumedBy));
20
+ (hasValidTimestamp && row.needsImplementation && row.status === 'consumed' && parseArtifactName(row.consumedBy)?.family === 'code');
21
21
  if (!valid)
22
22
  throw new Error(`implementation input ${row.id} has an invalid state combination`);
23
23
  }
@@ -122,7 +122,7 @@ function selectPendingImplementationInput(rows) {
122
122
  })[0] ?? null;
123
123
  }
124
124
  function consumeImplementationInput(rows, id, artifact) {
125
- if (!ARTIFACT_RE.test(artifact))
125
+ if (parseArtifactName(artifact)?.family !== 'code')
126
126
  throw new Error(`implementation input artifact '${artifact}' is invalid`);
127
127
  let found = false;
128
128
  const next = rows.map((row) => {