@fitlab-ai/agent-infra 0.9.13 → 0.9.15

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 (428) hide show
  1. package/README.md +10 -0
  2. package/README.zh-CN.md +10 -0
  3. package/bin/cli.ts +26 -9
  4. package/bin/internal-cli.sh +45 -0
  5. package/dist/bin/cli.js +173 -157
  6. package/dist/bin/internal-cli.js +126 -42
  7. package/dist/lib/agent-client.js +5 -0
  8. package/dist/lib/agent-clients/adapters/codex-lifecycle/capability-store.js +165 -27
  9. package/dist/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.js +3 -2
  10. package/dist/lib/defaults.json +103 -103
  11. package/dist/lib/fs/durable-write.js +37 -0
  12. package/dist/lib/fs/file-lock.js +111 -0
  13. package/dist/lib/host-control/audit.js +19 -0
  14. package/dist/lib/host-control/client.js +142 -0
  15. package/dist/lib/host-control/command.js +49 -0
  16. package/dist/lib/host-control/path.js +160 -0
  17. package/dist/lib/host-control/secure-fs.js +140 -0
  18. package/dist/lib/host-control/server.js +159 -0
  19. package/dist/lib/host-control/service.js +112 -0
  20. package/dist/lib/init.js +11 -2
  21. package/dist/lib/internal/agent-client.js +5 -2
  22. package/dist/lib/internal/cli-route-inventory.js +154 -0
  23. package/dist/lib/internal/codex-lifecycle.js +15 -12
  24. package/dist/lib/internal/codex-sandbox-controller.js +8 -2
  25. package/dist/lib/internal/demo-promotion.js +322 -0
  26. package/dist/lib/internal/demo-transcript.js +454 -0
  27. package/dist/lib/internal/git-workflow.js +10 -6
  28. package/dist/lib/internal/host-control.js +28 -0
  29. package/dist/lib/internal/manual-validation.js +322 -0
  30. package/dist/lib/internal/platform-checks.js +16 -4
  31. package/dist/lib/internal/platform-comment.js +16 -4
  32. package/dist/lib/internal/platform-context.js +4 -1
  33. package/dist/lib/internal/platform-issue.js +12 -4
  34. package/dist/lib/internal/platform-metadata.js +88 -0
  35. package/dist/lib/internal/platform-pr-review.js +16 -3
  36. package/dist/lib/internal/platform-pr.js +42 -14
  37. package/dist/lib/internal/platform-release-notes.js +6 -3
  38. package/dist/lib/internal/platform-security.js +93 -0
  39. package/dist/lib/internal/pr-review-grade.js +14 -3
  40. package/dist/lib/internal/release-workflow.js +82 -89
  41. package/dist/lib/internal/sandbox-control.js +171 -7
  42. package/dist/lib/internal/task-activity.js +13 -4
  43. package/dist/lib/internal/task-artifact.js +19 -107
  44. package/dist/lib/internal/task-context.js +4 -1
  45. package/dist/lib/internal/task-create.js +48 -14
  46. package/dist/lib/internal/task-delivery.js +4 -1
  47. package/dist/lib/internal/task-event.js +38 -35
  48. package/dist/lib/internal/task-finalization.js +4 -1
  49. package/dist/lib/internal/task-invalidation.js +4 -1
  50. package/dist/lib/internal/task-ledger.js +12 -1
  51. package/dist/lib/internal/task-lifecycle.js +4 -1
  52. package/dist/lib/internal/task-operation-registry.js +519 -0
  53. package/dist/lib/internal/task-orchestration.js +12 -0
  54. package/dist/lib/internal/task-override.js +10 -3
  55. package/dist/lib/internal/task-qualification.js +90 -0
  56. package/dist/lib/internal/task-review.js +10 -52
  57. package/dist/lib/internal/task-short-id.js +11 -1
  58. package/dist/lib/internal/task-snapshot.js +4 -1
  59. package/dist/lib/internal/task-validate.js +37 -12
  60. package/dist/lib/internal/task-verify.js +5 -1
  61. package/dist/lib/internal/task-warning.js +8 -1
  62. package/dist/lib/owned-path.js +33 -0
  63. package/dist/lib/platform/check-status.js +11 -0
  64. package/dist/lib/platform/comment-safety.js +229 -0
  65. package/dist/lib/platform/completion-backfill.js +1 -1
  66. package/dist/lib/platform/github-data.js +241 -0
  67. package/dist/lib/platform/github-provider.js +248 -26
  68. package/dist/lib/platform/issue-comments.js +146 -34
  69. package/dist/lib/platform/issues.js +1 -179
  70. package/dist/lib/platform/pr-change-report.js +6 -12
  71. package/dist/lib/platform/pr-checks.js +12 -65
  72. package/dist/lib/platform/pr-summary.js +83 -10
  73. package/dist/lib/platform/provider-contract.d.ts +67 -1
  74. package/dist/lib/platform/provider-contract.js +6 -2
  75. package/dist/lib/platform/provider-validation.js +76 -3
  76. package/dist/lib/platform/pull-requests.js +117 -275
  77. package/dist/lib/platform/repository-metadata.js +377 -0
  78. package/dist/lib/platform/security-alerts.js +155 -0
  79. package/dist/lib/platform/snapshots.js +2 -0
  80. package/dist/lib/platform/verification-sync.js +154 -162
  81. package/dist/lib/process-data/index.js +7 -0
  82. package/dist/lib/release/workflow.js +1 -1
  83. package/dist/lib/run/index.js +5 -0
  84. package/dist/lib/sandbox/commands/control-status.js +22 -0
  85. package/dist/lib/sandbox/commands/create.js +39 -8
  86. package/dist/lib/sandbox/commands/ls.js +5 -0
  87. package/dist/lib/sandbox/commands/rm.js +3 -807
  88. package/dist/lib/sandbox/commands/show.js +16 -0
  89. package/dist/lib/sandbox/control/audit.js +203 -0
  90. package/dist/lib/sandbox/control/client.js +125 -12
  91. package/dist/lib/sandbox/control/completed-reentry.js +85 -0
  92. package/dist/lib/sandbox/control/container-identity.js +28 -31
  93. package/dist/lib/sandbox/control/executor.js +233 -34
  94. package/dist/lib/sandbox/control/finalization-response.js +27 -0
  95. package/dist/lib/sandbox/control/identity-sentinel.js +107 -0
  96. package/dist/lib/sandbox/control/lifecycle.js +656 -128
  97. package/dist/lib/sandbox/control/native-file-lock.js +85 -0
  98. package/dist/lib/sandbox/control/protocol.js +55 -4
  99. package/dist/lib/sandbox/control/server.js +720 -79
  100. package/dist/lib/sandbox/control/state.js +63 -3
  101. package/dist/lib/sandbox/control/task-view.js +183 -0
  102. package/dist/lib/sandbox/control/task-workflow.js +74 -0
  103. package/dist/lib/sandbox/control/workflow-executor.js +99 -0
  104. package/dist/lib/sandbox/engines/authority.js +289 -0
  105. package/dist/lib/sandbox/engines/docker-exact-discovery.js +97 -0
  106. package/dist/lib/sandbox/index.js +7 -2
  107. package/dist/lib/sandbox/recovery.js +49 -5
  108. package/dist/lib/sandbox/removal.js +1717 -0
  109. package/dist/lib/sandbox/task-cleanup.js +217 -0
  110. package/dist/lib/sandbox/workspace-view.js +15 -15
  111. package/dist/lib/sandbox/worktree-safety.js +4 -1
  112. package/dist/lib/server/index.js +8 -0
  113. package/dist/lib/task/activity-intent.js +2 -1
  114. package/dist/lib/task/activity-log.js +30 -24
  115. package/dist/lib/task/artifact-command.js +101 -0
  116. package/dist/lib/task/artifact-lifecycle.js +71 -40
  117. package/dist/lib/task/artifact-name.js +36 -0
  118. package/dist/lib/task/artifact-operations.js +474 -0
  119. package/dist/lib/task/artifact-receipts.js +3 -15
  120. package/dist/lib/task/artifact-repair-intent.js +130 -0
  121. package/dist/lib/task/artifact-schema.js +127 -0
  122. package/dist/lib/task/capabilities.js +83 -19
  123. package/dist/lib/task/codex-orchestration.js +71 -25
  124. package/dist/lib/task/commit-identity.js +28 -0
  125. package/dist/lib/task/commit-operation.js +2 -16
  126. package/dist/lib/task/control-authority.js +375 -5
  127. package/dist/lib/task/control-recovery.js +128 -0
  128. package/dist/lib/task/create-service.js +103 -4
  129. package/dist/lib/task/create.js +80 -2
  130. package/dist/lib/task/decision-details.js +2 -46
  131. package/dist/lib/task/events.js +306 -76
  132. package/dist/lib/task/finalization-state.js +9 -0
  133. package/dist/lib/task/human-override.js +3 -6
  134. package/dist/lib/task/implementation-inputs.js +3 -3
  135. package/dist/lib/task/index.js +11 -3
  136. package/dist/lib/task/intermediate-cleanup.js +383 -0
  137. package/dist/lib/task/invalidation.js +18 -65
  138. package/dist/lib/task/ledger-intents.js +7 -22
  139. package/dist/lib/task/ledger.js +1 -3
  140. package/dist/lib/task/lifecycle.js +19 -1
  141. package/dist/lib/task/local-artifact-finalization.js +190 -290
  142. package/dist/lib/task/manual-validation-completion.js +26 -0
  143. package/dist/lib/task/manual-validation-evidence.js +130 -0
  144. package/dist/lib/task/manual-validation-receipt.js +101 -0
  145. package/dist/lib/task/manual-validation-shared.js +40 -0
  146. package/dist/lib/task/manual-validation-transaction.js +243 -0
  147. package/dist/lib/task/markdown.js +47 -0
  148. package/dist/lib/task/orchestration.js +206 -69
  149. package/dist/lib/task/qualification-audit.js +476 -0
  150. package/dist/lib/task/qualification-confirmation.js +109 -0
  151. package/dist/lib/task/qualification-intents.js +148 -0
  152. package/dist/lib/task/review-artifacts.js +23 -40
  153. package/dist/lib/task/review-command.js +25 -0
  154. package/dist/lib/task/review-finalization.js +146 -97
  155. package/dist/lib/task/review-fingerprint.js +2 -2
  156. package/dist/lib/task/sections.js +34 -54
  157. package/dist/lib/task/verification-config.js +11 -0
  158. package/dist/lib/task/verification-engine.js +170 -327
  159. package/dist/lib/task/verification.js +1 -1
  160. package/dist/lib/task/workflow-command.js +17 -0
  161. package/dist/lib/task/workflow-warning-intents.js +0 -16
  162. package/dist/lib/task/workflow-warnings.js +28 -2
  163. package/dist/lib/update.js +2 -0
  164. package/dist/lifecycle-build-manifest.json +3 -3
  165. package/lib/agent-client.ts +9 -0
  166. package/lib/agent-clients/adapters/codex-lifecycle/capability-store.ts +223 -33
  167. package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.ts +3 -2
  168. package/lib/fs/durable-write.ts +29 -0
  169. package/lib/fs/file-lock.ts +113 -0
  170. package/lib/host-control/audit.ts +22 -0
  171. package/lib/host-control/client.ts +172 -0
  172. package/lib/host-control/command.ts +60 -0
  173. package/lib/host-control/path.ts +167 -0
  174. package/lib/host-control/secure-fs.ts +135 -0
  175. package/lib/host-control/server.ts +180 -0
  176. package/lib/host-control/service.ts +133 -0
  177. package/lib/init.ts +15 -2
  178. package/lib/internal/agent-client.ts +4 -2
  179. package/lib/internal/cli-route-inventory.ts +142 -0
  180. package/lib/internal/codex-lifecycle.ts +14 -12
  181. package/lib/internal/codex-sandbox-controller.ts +7 -2
  182. package/lib/internal/demo-promotion.ts +354 -0
  183. package/lib/internal/demo-transcript.ts +419 -0
  184. package/lib/internal/git-workflow.ts +9 -6
  185. package/lib/internal/host-control.ts +28 -0
  186. package/lib/internal/manual-validation.ts +306 -0
  187. package/lib/internal/platform-checks.ts +12 -4
  188. package/lib/internal/platform-comment.ts +12 -4
  189. package/lib/internal/platform-context.ts +3 -1
  190. package/lib/internal/platform-issue.ts +11 -4
  191. package/lib/internal/platform-metadata.ts +70 -0
  192. package/lib/internal/platform-pr-review.ts +14 -3
  193. package/lib/internal/platform-pr.ts +38 -14
  194. package/lib/internal/platform-release-notes.ts +5 -3
  195. package/lib/internal/platform-security.ts +83 -0
  196. package/lib/internal/pr-review-grade.ts +10 -3
  197. package/lib/internal/release-workflow.ts +91 -87
  198. package/lib/internal/sandbox-control.ts +181 -6
  199. package/lib/internal/task-activity.ts +12 -4
  200. package/lib/internal/task-artifact.ts +15 -79
  201. package/lib/internal/task-context.ts +3 -1
  202. package/lib/internal/task-create.ts +76 -15
  203. package/lib/internal/task-delivery.ts +3 -1
  204. package/lib/internal/task-event.ts +21 -10
  205. package/lib/internal/task-finalization.ts +3 -1
  206. package/lib/internal/task-invalidation.ts +3 -1
  207. package/lib/internal/task-ledger.ts +11 -1
  208. package/lib/internal/task-lifecycle.ts +3 -1
  209. package/lib/internal/task-operation-registry.ts +590 -0
  210. package/lib/internal/task-orchestration.ts +12 -0
  211. package/lib/internal/task-override.ts +9 -3
  212. package/lib/internal/task-qualification.ts +61 -0
  213. package/lib/internal/task-review.ts +7 -50
  214. package/lib/internal/task-short-id.ts +10 -1
  215. package/lib/internal/task-snapshot.ts +3 -1
  216. package/lib/internal/task-validate.ts +32 -12
  217. package/lib/internal/task-verify.ts +4 -1
  218. package/lib/internal/task-warning.ts +7 -1
  219. package/lib/owned-path.ts +26 -0
  220. package/lib/platform/check-status.ts +9 -0
  221. package/lib/platform/comment-safety.ts +255 -0
  222. package/lib/platform/completion-backfill.ts +1 -1
  223. package/lib/platform/github-data.ts +322 -0
  224. package/lib/platform/github-provider.ts +244 -27
  225. package/lib/platform/issue-comments.ts +159 -35
  226. package/lib/platform/issues.ts +4 -245
  227. package/lib/platform/pr-change-report.ts +6 -12
  228. package/lib/platform/pr-checks.ts +13 -77
  229. package/lib/platform/pr-summary.ts +88 -11
  230. package/lib/platform/provider-contract.ts +78 -3
  231. package/lib/platform/provider-validation.ts +85 -3
  232. package/lib/platform/pull-requests.ts +129 -348
  233. package/lib/platform/repository-metadata.ts +453 -0
  234. package/lib/platform/security-alerts.ts +191 -0
  235. package/lib/platform/snapshots.ts +53 -0
  236. package/lib/platform/verification-sync.ts +171 -177
  237. package/lib/process-data/index.ts +7 -0
  238. package/lib/release/workflow.ts +3 -3
  239. package/lib/run/index.ts +5 -0
  240. package/lib/sandbox/commands/control-status.ts +26 -0
  241. package/lib/sandbox/commands/create.ts +53 -12
  242. package/lib/sandbox/commands/ls.ts +7 -0
  243. package/lib/sandbox/commands/rm.ts +3 -996
  244. package/lib/sandbox/commands/show.ts +16 -0
  245. package/lib/sandbox/control/audit.ts +253 -0
  246. package/lib/sandbox/control/client.ts +150 -13
  247. package/lib/sandbox/control/completed-reentry.ts +98 -0
  248. package/lib/sandbox/control/container-identity.ts +27 -43
  249. package/lib/sandbox/control/executor.ts +262 -37
  250. package/lib/sandbox/control/finalization-response.ts +29 -0
  251. package/lib/sandbox/control/identity-sentinel.ts +137 -0
  252. package/lib/sandbox/control/lifecycle.ts +672 -18
  253. package/lib/sandbox/control/native-file-lock.ts +108 -0
  254. package/lib/sandbox/control/protocol.ts +80 -8
  255. package/lib/sandbox/control/server.ts +779 -82
  256. package/lib/sandbox/control/state.ts +84 -3
  257. package/lib/sandbox/control/task-view.ts +240 -0
  258. package/lib/sandbox/control/task-workflow.ts +95 -0
  259. package/lib/sandbox/control/workflow-executor.ts +102 -0
  260. package/lib/sandbox/engines/authority.ts +365 -0
  261. package/lib/sandbox/engines/docker-exact-discovery.ts +132 -0
  262. package/lib/sandbox/index.ts +9 -2
  263. package/lib/sandbox/recovery.ts +49 -6
  264. package/lib/sandbox/removal.ts +2144 -0
  265. package/lib/sandbox/task-cleanup.ts +209 -0
  266. package/lib/sandbox/workspace-view.ts +26 -18
  267. package/lib/sandbox/worktree-safety.ts +4 -1
  268. package/lib/server/index.ts +10 -0
  269. package/lib/task/activity-intent.ts +2 -2
  270. package/lib/task/activity-log.ts +27 -19
  271. package/lib/task/artifact-command.ts +104 -0
  272. package/lib/task/artifact-lifecycle.ts +66 -39
  273. package/lib/task/artifact-name.ts +55 -0
  274. package/lib/task/artifact-operations.ts +580 -0
  275. package/lib/task/artifact-receipts.ts +3 -14
  276. package/lib/task/artifact-repair-intent.ts +152 -0
  277. package/lib/task/artifact-schema.ts +163 -0
  278. package/lib/task/capabilities.ts +82 -22
  279. package/lib/task/codex-orchestration.ts +85 -24
  280. package/lib/task/commit-identity.ts +31 -0
  281. package/lib/task/commit-operation.ts +2 -16
  282. package/lib/task/control-authority.ts +513 -6
  283. package/lib/task/control-recovery.ts +174 -0
  284. package/lib/task/create-service.ts +131 -6
  285. package/lib/task/create.ts +83 -1
  286. package/lib/task/decision-details.ts +3 -71
  287. package/lib/task/events.ts +292 -75
  288. package/lib/task/finalization-state.ts +11 -0
  289. package/lib/task/human-override.ts +3 -8
  290. package/lib/task/implementation-inputs.ts +3 -3
  291. package/lib/task/index.ts +16 -3
  292. package/lib/task/intermediate-cleanup.ts +455 -0
  293. package/lib/task/invalidation.ts +18 -62
  294. package/lib/task/ledger-intents.ts +7 -24
  295. package/lib/task/ledger.ts +1 -3
  296. package/lib/task/lifecycle.ts +17 -1
  297. package/lib/task/local-artifact-finalization.ts +223 -388
  298. package/lib/task/manual-validation-completion.ts +40 -0
  299. package/lib/task/manual-validation-evidence.ts +209 -0
  300. package/lib/task/manual-validation-receipt.ts +152 -0
  301. package/lib/task/manual-validation-shared.ts +50 -0
  302. package/lib/task/manual-validation-transaction.ts +304 -0
  303. package/lib/task/markdown.ts +70 -0
  304. package/lib/task/orchestration.ts +191 -68
  305. package/lib/task/qualification-audit.ts +588 -0
  306. package/lib/task/qualification-confirmation.ts +117 -0
  307. package/lib/task/qualification-intents.ts +150 -0
  308. package/lib/task/review-artifacts.ts +21 -41
  309. package/lib/task/review-command.ts +23 -0
  310. package/lib/task/review-finalization.ts +154 -153
  311. package/lib/task/review-fingerprint.ts +2 -2
  312. package/lib/task/sections.ts +33 -52
  313. package/lib/task/verification-config.ts +12 -0
  314. package/lib/task/verification-engine.ts +165 -370
  315. package/lib/task/verification-types.ts +17 -0
  316. package/lib/task/verification.ts +1 -1
  317. package/lib/task/workflow-command.ts +18 -0
  318. package/lib/task/workflow-warning-intents.ts +0 -13
  319. package/lib/task/workflow-warnings.ts +21 -2
  320. package/lib/update.ts +6 -0
  321. package/package.json +7 -2
  322. package/templates/.agents/hooks/lifecycle-delegation.js +6 -6
  323. package/templates/.agents/rules/README.en.md +2 -0
  324. package/templates/.agents/rules/README.zh-CN.md +2 -0
  325. package/templates/.agents/rules/decision-qualification.en.md +22 -0
  326. package/templates/.agents/rules/decision-qualification.zh-CN.md +22 -0
  327. package/templates/.agents/rules/evidence-reporting.en.md +28 -0
  328. package/templates/.agents/rules/evidence-reporting.zh-CN.md +28 -0
  329. package/templates/.agents/rules/label-milestone-setup.en.md +7 -7
  330. package/templates/.agents/rules/label-milestone-setup.zh-CN.md +7 -7
  331. package/templates/.agents/rules/local-artifact-repair.en.md +5 -1
  332. package/templates/.agents/rules/local-artifact-repair.zh-CN.md +5 -1
  333. package/templates/.agents/rules/security-alerts.en.md +8 -8
  334. package/templates/.agents/rules/security-alerts.zh-CN.md +8 -8
  335. package/templates/.agents/rules/testing-discipline.en.md +2 -0
  336. package/templates/.agents/rules/testing-discipline.zh-CN.md +2 -0
  337. package/templates/.agents/scripts/package.json +3 -0
  338. package/templates/.agents/skills/analyze-task/SKILL.en.md +15 -2
  339. package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +19 -2
  340. package/templates/.agents/skills/analyze-task/config/verify.en.json +1 -12
  341. package/templates/.agents/skills/analyze-task/config/verify.zh-CN.json +1 -12
  342. package/templates/.agents/skills/close-codescan/SKILL.en.md +2 -2
  343. package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +2 -2
  344. package/templates/.agents/skills/close-dependabot/SKILL.en.md +2 -2
  345. package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +2 -2
  346. package/templates/.agents/skills/code-task/SKILL.en.md +14 -1
  347. package/templates/.agents/skills/code-task/SKILL.zh-CN.md +14 -1
  348. package/templates/.agents/skills/code-task/config/verify.en.json +5 -14
  349. package/templates/.agents/skills/code-task/config/verify.zh-CN.json +5 -14
  350. package/templates/.agents/skills/code-task/reference/code-rules.en.md +5 -4
  351. package/templates/.agents/skills/code-task/reference/code-rules.zh-CN.md +5 -4
  352. package/templates/.agents/skills/code-task/reference/dual-mode.en.md +1 -1
  353. package/templates/.agents/skills/code-task/reference/dual-mode.zh-CN.md +1 -1
  354. package/templates/.agents/skills/code-task/reference/fix-mode.en.md +3 -1
  355. package/templates/.agents/skills/code-task/reference/fix-mode.zh-CN.md +3 -1
  356. package/templates/.agents/skills/code-task/reference/report-template.en.md +42 -15
  357. package/templates/.agents/skills/code-task/reference/report-template.zh-CN.md +39 -12
  358. package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +37 -21
  359. package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +37 -21
  360. package/templates/.agents/skills/complete-manual-validation/config/verify.en.json +1 -1
  361. package/templates/.agents/skills/complete-manual-validation/config/verify.zh-CN.json +1 -1
  362. package/templates/.agents/skills/complete-manual-validation/reference/report-template.en.md +4 -0
  363. package/templates/.agents/skills/complete-manual-validation/reference/report-template.zh-CN.md +4 -0
  364. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.en.md +16 -7
  365. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.zh-CN.md +16 -7
  366. package/templates/.agents/skills/complete-task/SKILL.en.md +10 -23
  367. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +10 -23
  368. package/templates/.agents/skills/create-task/SKILL.en.md +8 -0
  369. package/templates/.agents/skills/create-task/SKILL.zh-CN.md +7 -0
  370. package/templates/.agents/skills/import-codescan/SKILL.en.md +1 -1
  371. package/templates/.agents/skills/import-codescan/SKILL.zh-CN.md +1 -1
  372. package/templates/.agents/skills/import-dependabot/SKILL.en.md +1 -1
  373. package/templates/.agents/skills/import-dependabot/SKILL.zh-CN.md +1 -1
  374. package/templates/.agents/skills/init-labels/SKILL.en.md +8 -8
  375. package/templates/.agents/skills/init-labels/SKILL.zh-CN.md +8 -8
  376. package/templates/.agents/skills/init-milestones/SKILL.en.md +9 -11
  377. package/templates/.agents/skills/init-milestones/SKILL.zh-CN.md +9 -11
  378. package/templates/.agents/skills/plan-task/SKILL.en.md +14 -1
  379. package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +16 -1
  380. package/templates/.agents/skills/plan-task/config/verify.en.json +4 -14
  381. package/templates/.agents/skills/plan-task/config/verify.zh-CN.json +4 -14
  382. package/templates/.agents/skills/release/SKILL.en.md +7 -3
  383. package/templates/.agents/skills/release/SKILL.zh-CN.md +7 -3
  384. package/templates/.agents/skills/review-analysis/SKILL.en.md +14 -1
  385. package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +14 -1
  386. package/templates/.agents/skills/review-analysis/config/verify.en.json +1 -18
  387. package/templates/.agents/skills/review-analysis/config/verify.zh-CN.json +1 -18
  388. package/templates/.agents/skills/review-analysis/reference/report-template.en.md +50 -23
  389. package/templates/.agents/skills/review-analysis/reference/report-template.zh-CN.md +45 -18
  390. package/templates/.agents/skills/review-code/SKILL.en.md +14 -1
  391. package/templates/.agents/skills/review-code/SKILL.zh-CN.md +14 -1
  392. package/templates/.agents/skills/review-code/config/verify.en.json +1 -23
  393. package/templates/.agents/skills/review-code/config/verify.zh-CN.json +1 -23
  394. package/templates/.agents/skills/review-code/reference/report-template.en.md +49 -22
  395. package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +45 -18
  396. package/templates/.agents/skills/review-plan/SKILL.en.md +14 -1
  397. package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +14 -1
  398. package/templates/.agents/skills/review-plan/config/verify.en.json +1 -18
  399. package/templates/.agents/skills/review-plan/config/verify.zh-CN.json +1 -18
  400. package/templates/.agents/skills/review-plan/reference/report-template.en.md +49 -22
  401. package/templates/.agents/skills/review-plan/reference/report-template.zh-CN.md +45 -18
  402. package/templates/.agents/skills/review-pr/SKILL.en.md +5 -1
  403. package/templates/.agents/skills/review-pr/SKILL.zh-CN.md +5 -1
  404. package/templates/.agents/skills/review-pr/reference/report-template.en.md +4 -3
  405. package/templates/.agents/skills/review-pr/reference/report-template.zh-CN.md +4 -3
  406. package/templates/.agents/skills/run-manual-validation/SKILL.en.md +25 -4
  407. package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +25 -4
  408. package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.en.md +4 -1
  409. package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.zh-CN.md +4 -1
  410. package/templates/.agents/skills/run-manual-validation/reference/report-template.en.md +5 -1
  411. package/templates/.agents/skills/run-manual-validation/reference/report-template.zh-CN.md +5 -1
  412. package/templates/.agents/skills/run-task/SKILL.en.md +1 -1
  413. package/templates/.agents/skills/run-task/SKILL.zh-CN.md +1 -1
  414. package/templates/.agents/skills/test/SKILL.en.md +29 -7
  415. package/templates/.agents/skills/test/SKILL.zh-CN.md +29 -7
  416. package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +14 -0
  417. package/templates/.agents/templates/task.en.md +6 -0
  418. package/templates/.agents/templates/task.zh-CN.md +6 -0
  419. package/templates/.claude/commands/run-manual-validation.en.md +1 -1
  420. package/templates/.claude/commands/run-manual-validation.zh-CN.md +1 -1
  421. package/dist/lib/platform/adapters.js +0 -56
  422. package/lib/platform/adapters.ts +0 -197
  423. package/templates/.agents/scripts/security-alerts.github.sh +0 -151
  424. package/templates/.agents/scripts/security-alerts.sh +0 -31
  425. package/templates/.agents/skills/init-labels/scripts/init-labels.github.sh +0 -146
  426. package/templates/.agents/skills/init-labels/scripts/init-labels.sh +0 -32
  427. package/templates/.agents/skills/init-milestones/scripts/init-milestones.github.sh +0 -294
  428. package/templates/.agents/skills/init-milestones/scripts/init-milestones.sh +0 -6
@@ -4,14 +4,13 @@ import path from 'node:path';
4
4
  import { appendActivityEntry, locateActivityLog, pairEntries, startedBackedRows } from './activity-log.ts';
5
5
  import {
6
6
  buildArtifactLinkSection,
7
- artifactName,
8
7
  inspectArtifactDirectory,
9
8
  parseReviewedInputReference,
10
9
  parseCodePlanInputReference,
11
- parseArtifactName,
12
10
  resolveArtifactContext,
13
11
  validateCompletedArtifact
14
12
  } from './artifact-lifecycle.ts';
13
+ import { artifactName, parseArtifactName } from './artifact-name.ts';
15
14
  import type { ArtifactContextResult, ArtifactErrorCode, ArtifactFamily, ArtifactIdentity } from './artifact-lifecycle.ts';
16
15
  import { ArtifactReceiptError, parseArtifactReceipts, sha256File, upsertArtifactReceipt } from './artifact-receipts.ts';
17
16
  import type { ArtifactReceipt } from './artifact-receipts.ts';
@@ -35,19 +34,28 @@ import { captureTaskWriteMetadata, writeTask } from './write.ts';
35
34
  import type { TaskOperationSummary, TaskWriteErrorCode, TaskWriteOptions } from './write.ts';
36
35
  import { allowsManualOverride } from './guard-override.ts';
37
36
  import {
38
- LOCAL_ARTIFACT_REQUIRED_PATTERNS,
39
- LOCAL_ARTIFACT_REQUIRED_SECTIONS,
40
37
  consumeLocalArtifactFinalizationIntent,
41
38
  readLocalArtifactFinalizationIntent,
42
39
  validateLocalArtifact
43
40
  } from './local-artifact-finalization.ts';
41
+ import { writeArtifactRepairIntent } from './artifact-repair-intent.ts';
44
42
  import type { LocalArtifactFamily, LocalArtifactFinalizationIntent } from './local-artifact-finalization.ts';
45
- import { loadVerificationConfig } from './verification-config.ts';
46
43
  import { buildLifecycleFacts, canStart } from './capabilities.ts';
47
44
  import type { ExplicitTrigger, LifecycleAction, TriggerInitiator, TriggerReason } from './capabilities.ts';
48
45
  import { createInvalidationOperation, invalidationMutation, parseInvalidationDocument, targetIdFor, upsertInvalidation } from './invalidation.ts';
49
46
  import type { InvalidationTargetKind } from './invalidation.ts';
50
47
  import { consumeReworkIntents, parseReworkIntentDocument, reworkIntentMutation, supersedeReworkIntents } from './rework-intent.ts';
48
+ import { ARTIFACT_FAMILIES, expectedQualificationRelations, parseQualificationAudit, parseTaskQualification, upstreamArtifactDigest, validateQualificationAudit } from './qualification-audit.ts';
49
+ import type { QualificationAudit, UpstreamRelation } from './qualification-audit.ts';
50
+ import { getArtifactSchema } from './artifact-schema.ts';
51
+ import { inspectArtifactContract } from './artifact-operations.ts';
52
+ import {
53
+ consumeLifecycleRecoveryAttestation,
54
+ lifecycleRecoveryAttestationDigest,
55
+ validateLifecycleRecoveryAttestation,
56
+ type LifecycleRecoveryAttestationV1
57
+ } from './control-authority.ts';
58
+ import { readManualValidationCompletion } from './manual-validation-completion.ts';
51
59
 
52
60
  const eventCatalog = [
53
61
  'analyze.started', 'analyze.awaiting-input', 'analyze.completed',
@@ -68,6 +76,10 @@ type TaskEventErrorCode =
68
76
  | 'EVENT_LOG_CONFLICT' | 'EVENT_ARTIFACT_CONFLICT' | 'EVENT_FINDING_COUNT_MISMATCH'
69
77
  | 'EVENT_VERDICT_INVALID'
70
78
  | 'EVENT_ORCHESTRATION_COMMIT_FAILED'
79
+ | 'MANUAL_VALIDATION_RECEIPT_MISSING' | 'MANUAL_VALIDATION_RECEIPT_INVALID'
80
+ | 'MANUAL_VALIDATION_RECEIPT_IDENTITY_MISMATCH'
81
+ | 'MANUAL_VALIDATION_TRANSACTION_MISSING' | 'MANUAL_VALIDATION_TRANSACTION_INVALID'
82
+ | 'MANUAL_VALIDATION_TRANSACTION_IDENTITY_MISMATCH' | 'MANUAL_VALIDATION_TRANSACTION_PHASE_INVALID'
71
83
  | ArtifactErrorCode | TaskWriteErrorCode;
72
84
  type TaskEventRequest = {
73
85
  taskRef: string; event: TaskEventName | string; agent: string; dryRun?: boolean; orchestrated?: boolean;
@@ -79,11 +91,14 @@ type TaskEventRequest = {
79
91
  verdict?: Verdict; blockers?: number; major?: number; minor?: number;
80
92
  manualValidation?: number; filesModified?: number; testsPassed?: number;
81
93
  summaryResult?: string;
94
+ transactionId?: string; receiptDigest?: string; prHeadSha?: string;
82
95
  };
83
96
  type TaskEventError = { code: TaskEventErrorCode; message: string };
84
97
  type TaskEventOptions = TaskWriteOptions & {
85
98
  commitOrchestrationCompletion?: (plan: OrchestrationStageCompletion) => void;
86
99
  lockAlreadyHeld?: boolean;
100
+ lifecycleRecoveryAttestation?: LifecycleRecoveryAttestationV1 | null;
101
+ deferLifecycleRecoveryConsumption?: boolean;
87
102
  };
88
103
  type TaskEventResult = {
89
104
  status: 'planned' | 'applied' | 'no-op' | 'failed'; changed: boolean;
@@ -111,36 +126,12 @@ const SCHEMAS: Record<TaskEventName, { required?: string[]; optional?: string[]
111
126
  'code.completed': { required: ['artifact', 'artifactSha256', 'semanticDigest'], optional: ['round', 'fixFor', 'implementationInput', 'filesModified', 'testsPassed', 'blockers', 'major', 'minor', 'manualValidation', 'orchestrated'] },
112
127
  'review-code.started': { optional: ['round'] },
113
128
  'review-code.completed': { required: ['artifact', 'verdict', 'blockers', 'major', 'minor', 'manualValidation'], optional: ['round', 'orchestrated'] },
114
- 'manual-validation.started': { optional: ['round'] },
115
- 'manual-validation.completed': { required: ['artifact', 'summaryResult'], optional: ['round'] },
129
+ 'manual-validation.started': { optional: ['round', 'transactionId'] },
130
+ 'manual-validation.completed': { required: ['artifact', 'summaryResult', 'transactionId', 'receiptDigest', 'prHeadSha'], optional: ['round'] },
116
131
  'validation-run.started': { optional: ['round'] },
117
132
  'validation-run.completed': { required: ['artifact'], optional: ['round'] }
118
133
  };
119
134
 
120
- function localArtifactValidationConfig(repoRoot: string, family: LocalArtifactFamily) {
121
- const fallback = {
122
- requiredSections: LOCAL_ARTIFACT_REQUIRED_SECTIONS[family],
123
- requiredPatterns: LOCAL_ARTIFACT_REQUIRED_PATTERNS
124
- };
125
- const skillName = family === 'analysis' ? 'analyze-task' : family === 'plan' ? 'plan-task' : 'code-task';
126
- try {
127
- const artifact = loadVerificationConfig(repoRoot, skillName).checks.artifact;
128
- if (!artifact || typeof artifact !== 'object' || Array.isArray(artifact)) return fallback;
129
- const sections = artifact.required_sections;
130
- const patterns = artifact.required_patterns;
131
- return {
132
- requiredSections: Array.isArray(sections)
133
- ? sections.filter((value): value is string => typeof value === 'string')
134
- : fallback.requiredSections,
135
- requiredPatterns: Array.isArray(patterns)
136
- ? patterns.filter((value): value is string => typeof value === 'string')
137
- : fallback.requiredPatterns
138
- };
139
- } catch {
140
- return fallback;
141
- }
142
- }
143
-
144
135
  function validateTaskEventRequest(request: TaskEventRequest): TaskEventError | null {
145
136
  if (!eventCatalog.includes(request.event as TaskEventName)) return { code: 'EVENT_UNKNOWN', message: `unknown task event '${request.event}'` };
146
137
  if (!request.taskRef || !request.agent) return { code: 'EVENT_PAYLOAD_INVALID', message: 'taskRef and agent are required' };
@@ -169,12 +160,23 @@ function validateTaskEventRequest(request: TaskEventRequest): TaskEventError | n
169
160
  const forbidden = fix ? ['filesModified', 'testsPassed'] : ['fixFor', 'blockers', 'major', 'minor', 'manualValidation'];
170
161
  if (modeRequired.some((key) => request[key as keyof TaskEventRequest] === undefined) || forbidden.some((key) => request[key as keyof TaskEventRequest] !== undefined)) return { code: 'EVENT_PAYLOAD_INVALID', message: 'code.completed requires either initial or fix completion payload' };
171
162
  }
172
- if (request.fixFor && !/^review-code(?:-r(?:[2-9]|[1-9]\d+))?\.md$/.test(request.fixFor)) return { code: 'EVENT_PAYLOAD_INVALID', message: 'fixFor must reference a canonical review-code artifact' };
163
+ if (request.fixFor && parseArtifactName(request.fixFor)?.family !== 'review-code') return { code: 'EVENT_PAYLOAD_INVALID', message: 'fixFor must reference a canonical review-code artifact' };
173
164
  if (request.implementationInput && !/^II-[1-9]\d*$/.test(request.implementationInput)) return { code: 'EVENT_PAYLOAD_INVALID', message: 'implementationInput must be a canonical II-N id' };
174
165
  if (request.artifactSha256 !== undefined && !/^[0-9a-f]{64}$/i.test(request.artifactSha256)) return { code: 'EVENT_PAYLOAD_INVALID', message: 'artifactSha256 must be a 64-character hexadecimal digest' };
175
166
  if (request.semanticDigest !== undefined && !/^[0-9a-f]{64}$/i.test(request.semanticDigest)) return { code: 'EVENT_PAYLOAD_INVALID', message: 'semanticDigest must be a 64-character hexadecimal digest' };
176
167
  if (request.fixFor && request.implementationInput) return { code: 'EVENT_PAYLOAD_INVALID', message: 'fixFor and implementationInput are mutually exclusive' };
177
168
  if (request.summaryResult !== undefined && (!request.summaryResult.trim() || /[\r\n]/.test(request.summaryResult))) return { code: 'EVENT_PAYLOAD_INVALID', message: 'summaryResult must be a non-empty single line' };
169
+ if (request.event === 'manual-validation.completed') {
170
+ for (const [name, value, pattern] of [
171
+ ['transactionId', request.transactionId, /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/u],
172
+ ['receiptDigest', request.receiptDigest, /^[a-f0-9]{64}$/u],
173
+ ['prHeadSha', request.prHeadSha, /^[a-f0-9]{40}$/u]
174
+ ] as const) if (!value || !pattern.test(value)) return { code: 'EVENT_PAYLOAD_INVALID', message: `manual-validation.completed requires a valid ${name}` };
175
+ }
176
+ if (request.event === 'manual-validation.started' && request.transactionId !== undefined
177
+ && !/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/u.test(request.transactionId)) {
178
+ return { code: 'EVENT_PAYLOAD_INVALID', message: 'manual-validation.started requires a valid transactionId' };
179
+ }
178
180
  if (request.event !== 'analyze.awaiting-input' && /\.(?:started|completed)$/.test(request.event)) {
179
181
  if (!request.initiator || !request.requestId || !request.reasonCode) {
180
182
  return { code: 'EVENT_TRIGGER_REQUIRED', message: `${request.event} requires --initiator, --request-id, and --reason-code` };
@@ -277,7 +279,9 @@ function identity(request: TaskEventRequest) {
277
279
  if (family === 'manual-validation') {
278
280
  return {
279
281
  family, phase, action: spec.label,
280
- note: phase === 'started' ? 'started' : `Manual validation passed → ${request.artifact}; ${request.summaryResult}`,
282
+ note: phase === 'started'
283
+ ? `started${request.transactionId ? `; transaction=${request.transactionId}` : ''}`
284
+ : `Manual validation passed → ${request.artifact}; ${request.summaryResult}; transaction=${request.transactionId}; receipt=${request.receiptDigest}; head=${request.prHeadSha}`,
281
285
  target: null
282
286
  } as const;
283
287
  }
@@ -326,6 +330,28 @@ function normalizeStarted(request: TaskEventRequest, repoRoot: string): { reques
326
330
  return { request: { ...request, round, artifact: context.next.name, fixFor: expectedFix, implementationInput: expectedImplementation }, context };
327
331
  }
328
332
 
333
+ function validateManualValidationCompletion(
334
+ taskDir: string,
335
+ taskId: string,
336
+ request: TaskEventRequest,
337
+ artifactPath: string
338
+ ): TaskEventError | null {
339
+ const completion = readManualValidationCompletion(taskDir, {
340
+ transactionId: request.transactionId,
341
+ taskId,
342
+ prHeadSha: request.prHeadSha,
343
+ artifact: request.artifact,
344
+ receiptDigest: request.receiptDigest
345
+ });
346
+ if (!completion.ok) return completion.error;
347
+ const { receipt, transaction } = completion.value;
348
+ if (!['receipt-committed', 'final-promotion-in-progress', 'committed'].includes(transaction.phase)) {
349
+ return { code: 'MANUAL_VALIDATION_TRANSACTION_PHASE_INVALID', message: 'manual validation completion requires the receipt-backed transaction phase' };
350
+ }
351
+ if (sha256File(artifactPath) !== receipt.artifactSha256) return { code: 'MANUAL_VALIDATION_RECEIPT_INVALID', message: 'manual-validation artifact digest does not match the receipt' };
352
+ return null;
353
+ }
354
+
329
355
  function openStartedIdentity(rows: ReturnType<typeof pairEntries>, family: EventFamily) {
330
356
  const spec = FAMILY[family];
331
357
  if (family === 'manual-validation') {
@@ -347,6 +373,25 @@ function reviewInputFamily(family: EventFamily): ArtifactFamily {
347
373
  return family === 'review-analysis' ? 'analysis' : family === 'review-plan' ? 'plan' : 'code';
348
374
  }
349
375
 
376
+ function relationForStartedInput(family: EventFamily, input: ArtifactIdentity): UpstreamRelation['relation'] {
377
+ if (family.startsWith('review-')) {
378
+ return input.family === reviewInputFamily(family) ? 'reviewed-input' : 'approval-context';
379
+ }
380
+ const consumer = family === 'analyze' ? 'analysis' : family;
381
+ if ((consumer === 'plan' && input.family === 'analysis') || (consumer === 'code' && input.family === 'plan')) return 'required-input';
382
+ return 'review-context';
383
+ }
384
+
385
+ function qualificationRelationsForStarted(family: EventFamily, inputs: readonly ArtifactIdentity[]): UpstreamRelation[] {
386
+ return inputs.map((input) => ({
387
+ upstreamFamily: input.family as UpstreamRelation['upstreamFamily'],
388
+ upstreamArtifact: input.name,
389
+ upstreamRound: input.round,
390
+ upstreamSha256: sha256File(input.path),
391
+ relation: relationForStartedInput(family, input)
392
+ }));
393
+ }
394
+
350
395
  function lifecycleAction(family: EventFamily): LifecycleAction {
351
396
  return family === 'analyze' ? 'analysis' : family;
352
397
  }
@@ -381,49 +426,118 @@ function invalidationMutationForCompletion(
381
426
  code: ['review-code']
382
427
  };
383
428
  const sourceFamily = family as 'analyze' | 'plan' | 'code';
429
+ const sourceArtifactFamily = sourceFamily === 'analyze' ? 'analysis' : sourceFamily;
384
430
  let receipts: readonly ArtifactReceipt[] = [];
385
431
  try {
386
432
  receipts = parseArtifactReceipts(content).rows;
387
433
  } catch (error) {
388
434
  return { error: error instanceof Error ? error.message : String(error) };
389
435
  }
390
- const targets = downstream[sourceFamily].flatMap((targetFamily: string) => {
391
- const names = fs.readdirSync(taskDir).filter((name) => {
392
- const pattern = targetFamily === 'review-analysis' ? /^review-analysis(?:-r\d+)?\.md$/
393
- : targetFamily === 'review-plan' ? /^review-plan(?:-r\d+)?\.md$/
394
- : targetFamily === 'review-code' ? /^review-code(?:-r\d+)?\.md$/
395
- : new RegExp(`^${targetFamily}(?:-r\\d+)?\\.md$`);
396
- return pattern.test(name);
397
- });
398
- return names.flatMap((name) => {
399
- try {
400
- const stat = fs.lstatSync(path.join(taskDir, name));
401
- if (!stat.isFile() || stat.isSymbolicLink()) return [];
402
- const targetRound = parseArtifactName(name)?.round ?? 1;
403
- const targetSha256 = sha256File(path.join(taskDir, name));
404
- const shapes: Array<{ targetKind: InvalidationTargetKind; targetFamily: string; targetArtifact: string; targetRound: number; targetSha256: string }> = [
405
- { targetKind: 'artifact', targetFamily, targetArtifact: name, targetRound, targetSha256 }
406
- ];
407
- const receipt = receipts.find((candidate) => candidate.output === name);
408
- if (receipt) shapes.push({
409
- targetKind: 'receipt', targetFamily, targetArtifact: name, targetRound,
410
- targetSha256: receipt.inputSha256
411
- });
412
- if (targetFamily.startsWith('review-')) {
413
- shapes.push({ targetKind: 'approval', targetFamily, targetArtifact: name, targetRound, targetSha256 });
414
- }
415
- if (targetFamily === 'review-code') {
416
- shapes.push({ targetKind: 'reviewed-snapshot', targetFamily, targetArtifact: name, targetRound, targetSha256 });
436
+ const inventory = fs.readdirSync(taskDir).flatMap((name) => {
437
+ const identity = parseArtifactName(name);
438
+ if (!identity || !(ARTIFACT_FAMILIES as readonly string[]).includes(identity.family)) return [];
439
+ try {
440
+ const stat = fs.lstatSync(path.join(taskDir, name));
441
+ if (!stat.isFile() || stat.isSymbolicLink()) return [];
442
+ return [{ family: identity.family, name, round: identity.round, sha256: sha256File(path.join(taskDir, name)) }];
443
+ } catch (error) {
444
+ throw new Error(`cannot inspect invalidation artifact '${name}': ${error instanceof Error ? error.message : String(error)}`);
445
+ }
446
+ });
447
+ const taskQualification = parseTaskQualification(content);
448
+ if (!taskQualification.ok) return { error: taskQualification.message };
449
+ const nodeMap = new Map(inventory.map((node) => [`${node.family}/${node.name}`, node]));
450
+ const audits = new Map<string, QualificationAudit>();
451
+ let taskInputChanged = false;
452
+ let graphUsable = taskQualification.qualification.present;
453
+ if (graphUsable) {
454
+ for (const node of inventory) {
455
+ const artifactContent = fs.readFileSync(path.join(taskDir, node.name), 'utf8');
456
+ const parsedAudit = parseQualificationAudit(artifactContent);
457
+ if (!parsedAudit.ok || !parsedAudit.audit.present || !parsedAudit.audit.snapshot) { graphUsable = false; break; }
458
+ audits.set(`${node.family}/${node.name}`, parsedAudit.audit);
459
+ if (parsedAudit.audit.snapshot.taskInputDigest !== taskQualification.qualification.taskInputDigest) taskInputChanged = true;
460
+ if (parsedAudit.audit.snapshot.nonConstraintInputDigest !== taskQualification.qualification.nonConstraintInputDigest) {
461
+ graphUsable = false;
462
+ break;
463
+ }
464
+ if (parsedAudit.audit.snapshot.upstreamArtifactDigest !== upstreamArtifactDigest(parsedAudit.audit.upstreamRelations)) {
465
+ graphUsable = false;
466
+ break;
467
+ }
468
+ for (const relation of parsedAudit.audit.upstreamRelations) {
469
+ const upstream = nodeMap.get(`${relation.upstreamFamily}/${relation.upstreamArtifact}`);
470
+ if (!upstream || upstream.round !== relation.upstreamRound || upstream.sha256 !== relation.upstreamSha256) { graphUsable = false; break; }
471
+ }
472
+ if (!graphUsable) break;
473
+ }
474
+ }
475
+ const selected = new Set<string>();
476
+ if (graphUsable) {
477
+ const changedConstraints = new Set<string>();
478
+ for (const audit of audits.values()) {
479
+ for (const dependency of audit.constraintDependencies) {
480
+ const current = taskQualification.qualification.constraints.find((row) => row.constraintId === dependency.constraintId);
481
+ if (!current || current.digest !== dependency.constraintDigest) changedConstraints.add(dependency.constraintId);
482
+ }
483
+ }
484
+ const currentCandidates = new Map(taskQualification.qualification.candidates.map((candidate) => [candidate.candidateId, candidate]));
485
+ const candidateSnapshotMatches = [...audits.values()].every((audit) => audit.candidateQualifications.length === currentCandidates.size && audit.candidateQualifications.every((row) => {
486
+ const current = currentCandidates.get(row.candidateId);
487
+ return Boolean(current && current.status === row.status && current.impact === row.impact
488
+ && current.evidence === row.evidence && current.constraintIds.join(',') === row.constraintIds.join(','));
489
+ }));
490
+ // A task-input digest also covers candidates. A narrowed invalidation graph
491
+ // is safe only when the old audit rows show that the candidate projection
492
+ // stayed unchanged and the change is explained by at least one constraint.
493
+ if (taskInputChanged && (changedConstraints.size === 0 || !candidateSnapshotMatches)) graphUsable = false;
494
+ for (const node of inventory) {
495
+ const audit = audits.get(`${node.family}/${node.name}`)!;
496
+ if (audit.constraintDependencies.some((dependency) => changedConstraints.has(dependency.constraintId))) selected.add(`${node.family}/${node.name}`);
497
+ if (audit.upstreamRelations.some((relation) => relation.upstreamFamily === sourceArtifactFamily)) selected.add(`${node.family}/${node.name}`);
498
+ }
499
+ // Relation rows point from a consumer to its actual upstream artifact. Walk
500
+ // that reverse edge so review-only seeds and multi-hop consumers receive the
501
+ // same derived targets as ordinary executor seeds.
502
+ let changed = true;
503
+ while (changed) {
504
+ changed = false;
505
+ for (const node of inventory) {
506
+ const key = `${node.family}/${node.name}`;
507
+ const audit = audits.get(key)!;
508
+ if (audit.upstreamRelations.some((relation) => selected.has(`${relation.upstreamFamily}/${relation.upstreamArtifact}`)) && !selected.has(key)) {
509
+ selected.add(key);
510
+ changed = true;
417
511
  }
418
- return shapes.map((targetShape) => ({
419
- ...targetShape,
420
- targetId: targetIdFor('pending', targetShape), operationId: 'pending', status: 'pending' as const,
421
- reasonCode: 'upstream-replaced', updatedAt: timestamp
422
- }));
423
- } catch (error) {
424
- throw new Error(`cannot inspect invalidation target '${name}': ${error instanceof Error ? error.message : String(error)}`);
425
512
  }
426
- });
513
+ }
514
+ // A relation cycle cannot establish a safe downstream closure.
515
+ const visiting = new Set<string>();
516
+ const visited = new Set<string>();
517
+ const hasCycle = (key: string): boolean => {
518
+ if (visiting.has(key)) return true;
519
+ if (visited.has(key)) return false;
520
+ visiting.add(key);
521
+ const cycle = audits.get(key)?.upstreamRelations.some((relation) => hasCycle(`${relation.upstreamFamily}/${relation.upstreamArtifact}`)) ?? false;
522
+ visiting.delete(key); visited.add(key);
523
+ return cycle;
524
+ };
525
+ if ([...nodeMap.keys()].some(hasCycle)) graphUsable = false;
526
+ }
527
+ const reasonCode = graphUsable ? 'qualification-changed' : 'upstream-replaced';
528
+ const targetNodes = (graphUsable
529
+ ? inventory.filter((node) => selected.has(`${node.family}/${node.name}`))
530
+ : inventory.filter((node) => downstream[sourceFamily].includes(node.family)))
531
+ .filter((node) => !(node.family === FAMILY[family].artifact && node.name === artifact.name));
532
+ const targets = targetNodes.flatMap((node) => {
533
+ const shapes: Array<{ targetKind: InvalidationTargetKind; targetFamily: string; targetArtifact: string; targetRound: number; targetSha256: string }> = [
534
+ { targetKind: 'artifact', targetFamily: node.family, targetArtifact: node.name, targetRound: node.round, targetSha256: node.sha256 }
535
+ ];
536
+ const receipt = receipts.find((candidate) => candidate.output === node.name);
537
+ if (receipt) shapes.push({ targetKind: 'receipt', targetFamily: node.family, targetArtifact: node.name, targetRound: node.round, targetSha256: receipt.inputSha256 });
538
+ if (node.family.startsWith('review-')) shapes.push({ targetKind: 'approval', targetFamily: node.family, targetArtifact: node.name, targetRound: node.round, targetSha256: node.sha256 });
539
+ if (node.family === 'review-code') shapes.push({ targetKind: 'reviewed-snapshot', targetFamily: node.family, targetArtifact: node.name, targetRound: node.round, targetSha256: node.sha256 });
540
+ return shapes.map((targetShape) => ({ ...targetShape, targetId: targetIdFor('pending', targetShape), operationId: 'pending', status: 'pending' as const, reasonCode, updatedAt: timestamp }));
427
541
  });
428
542
  if (targets.length === 0) return { mutation: null };
429
543
  const source = {
@@ -561,6 +675,10 @@ function applyTaskEventUnlocked(request: TaskEventRequest, options: TaskEventOpt
561
675
  if (openIdentity && 'conflict' in openIdentity) return failed(request, { code: 'EVENT_LOG_CONFLICT', message: 'artifact family has more than one open started event' }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath });
562
676
  if (openIdentity) {
563
677
  if (openIdentity.row.agent !== request.agent) return failed(request, { code: 'EVENT_LOG_CONFLICT', message: 'open started event has a different agent' }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath });
678
+ if (initialParts.family === 'manual-validation' && request.transactionId !== undefined) {
679
+ const existingTransactionId = /(?:^|;\s*)transaction=([A-Za-z0-9][A-Za-z0-9._-]{0,127})(?:;|$)/u.exec(openIdentity.row.note)?.[1];
680
+ if (existingTransactionId !== undefined && existingTransactionId !== request.transactionId) return failed(request, { code: 'EVENT_LOG_CONFLICT', message: 'open manual-validation started event has a different transactionId' }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath });
681
+ }
564
682
  if (request.round !== undefined && request.round !== openIdentity.round) return failed(request, { code: 'EVENT_ARTIFACT_CONFLICT', message: `round ${request.round} conflicts with open round ${openIdentity.round}` }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath });
565
683
  if (request.fixFor !== undefined && request.fixFor !== openIdentity.fixFor) return failed(request, { code: 'EVENT_ARTIFACT_CONFLICT', message: `fixFor '${request.fixFor}' conflicts with open event` }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath });
566
684
  if (request.implementationInput !== openIdentity.implementationInput) return failed(request, { code: 'EVENT_ARTIFACT_CONFLICT', message: `implementationInput '${request.implementationInput ?? ''}' conflicts with open event` }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath });
@@ -576,6 +694,23 @@ function applyTaskEventUnlocked(request: TaskEventRequest, options: TaskEventOpt
576
694
  if (identity?.family === FAMILY[initialParts.family].artifact) normalized = { ...request, round: identity.round };
577
695
  }
578
696
  const eventIdentity = identity(normalized);
697
+ const lifecycleAuthority = options.lifecycleRecoveryAttestation ?? null;
698
+ if (lifecycleAuthority) {
699
+ try { validateLifecycleRecoveryAttestation(lifecycleAuthority); }
700
+ catch (error) {
701
+ return failed(normalized, { code: 'EVENT_ARTIFACT_CONFLICT', message: error instanceof Error ? error.message : String(error) }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath });
702
+ }
703
+ const expectedFamily = eventIdentity.family === 'analyze' ? 'analysis' : eventIdentity.family;
704
+ if (eventIdentity.phase !== 'completed'
705
+ || lifecycleAuthority.phase !== 'task-event.completed'
706
+ || lifecycleAuthority.taskId !== resolved.taskId
707
+ || lifecycleAuthority.family !== expectedFamily
708
+ || lifecycleAuthority.artifact !== normalized.artifact
709
+ || lifecycleAuthority.round !== normalized.round
710
+ || (normalized.requestId !== undefined && lifecycleAuthority.lifecycleRequestId !== normalized.requestId)) {
711
+ return failed(normalized, { code: 'EVENT_ARTIFACT_CONFLICT', message: 'lifecycle completion authority does not match the event tuple' }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath });
712
+ }
713
+ }
579
714
  const currentStep = typeof frontmatter.current_step === 'string' ? frontmatter.current_step : '';
580
715
  const matchingRows = rows.filter((item) => item.step === eventIdentity.action);
581
716
  const manual = eventIdentity.family === 'manual-validation';
@@ -587,6 +722,12 @@ function applyTaskEventUnlocked(request: TaskEventRequest, options: TaskEventOpt
587
722
  let localFinalizationIntent: LocalArtifactFinalizationIntent | null = null;
588
723
  if (eventIdentity.phase === 'started' && row) {
589
724
  if (row.agent !== normalized.agent) return failed(normalized, { code: 'EVENT_LOG_CONFLICT', message: 'open started event has a different agent' }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath });
725
+ if (manual && normalized.transactionId !== undefined) {
726
+ const existingTransactionId = /(?:^|;\s*)transaction=([A-Za-z0-9][A-Za-z0-9._-]{0,127})(?:;|$)/u.exec(row.note)?.[1];
727
+ if (existingTransactionId !== undefined && existingTransactionId !== normalized.transactionId) {
728
+ return failed(normalized, { code: 'EVENT_LOG_CONFLICT', message: 'open manual-validation started event has a different transactionId' }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath });
729
+ }
730
+ }
590
731
  return successNoOp(normalized, resolved.taskId, resolved.taskMdPath, currentStep, eventIdentity, row.started, frontmatter, artifactContext);
591
732
  }
592
733
  if (eventIdentity.phase === 'started' && !manual && completedRows.length > 0) return failed(normalized, { code: 'EVENT_ALREADY_COMPLETED', message: 'event identity is already completed' }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath });
@@ -598,6 +739,39 @@ function applyTaskEventUnlocked(request: TaskEventRequest, options: TaskEventOpt
598
739
  const validated = validateCompletedArtifact(resolved.taskDir, FAMILY[eventIdentity.family].artifact, normalized.artifact!, normalized.round);
599
740
  if (!validated.ok) return failed(normalized, validated.error, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase });
600
741
  completedArtifact = validated.artifact;
742
+ if (eventIdentity.family === 'manual-validation') {
743
+ const validationError = validateManualValidationCompletion(
744
+ resolved.taskDir,
745
+ resolved.taskId,
746
+ normalized,
747
+ completedArtifact.path
748
+ );
749
+ if (validationError) return failed(normalized, validationError, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase });
750
+ }
751
+ if (eventIdentity.family.startsWith('review-')) {
752
+ let reviewContent: string;
753
+ try { reviewContent = fs.readFileSync(completedArtifact.path, 'utf8'); }
754
+ catch (error) {
755
+ return failed(normalized, { code: 'EVENT_ARTIFACT_CONFLICT', message: `cannot read qualification audit from ${completedArtifact.name}: ${error instanceof Error ? error.message : String(error)}` }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase });
756
+ }
757
+ const schema = getArtifactSchema(eventIdentity.family);
758
+ const structure = schema ? inspectArtifactContract(reviewContent, schema) : null;
759
+ if (structure && !structure.ok) {
760
+ return failed(normalized, { code: 'EVENT_ARTIFACT_CONFLICT', message: `shared artifact structure invalid: ${structure.diagnostics.map((item) => `${item.code}: ${item.message}`).join('; ')}` }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase });
761
+ }
762
+ const expected = expectedQualificationRelations(content, eventIdentity.family as 'review-analysis' | 'review-plan' | 'review-code');
763
+ if (!expected.ok) {
764
+ return failed(normalized, { code: 'EVENT_ARTIFACT_CONFLICT', message: `${expected.code}: ${expected.message}` }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase });
765
+ }
766
+ const qualification = validateQualificationAudit(content, reviewContent, {
767
+ family: eventIdentity.family as 'review-analysis' | 'review-plan' | 'review-code',
768
+ artifact: completedArtifact.name,
769
+ expectedUpstreamRelations: expected.relations
770
+ });
771
+ if (!qualification.ok) {
772
+ return failed(normalized, { code: 'EVENT_ARTIFACT_CONFLICT', message: `${qualification.code}: ${qualification.message}` }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase });
773
+ }
774
+ }
601
775
  if (eventIdentity.family === 'analyze' || eventIdentity.family === 'plan' || eventIdentity.family === 'code') {
602
776
  const localFamily = eventIdentity.family === 'analyze'
603
777
  ? 'analysis'
@@ -607,11 +781,10 @@ function applyTaskEventUnlocked(request: TaskEventRequest, options: TaskEventOpt
607
781
  catch (error) {
608
782
  return failed(normalized, { code: 'EVENT_ARTIFACT_CONFLICT', message: `cannot read ${completedArtifact.name}: ${String(error)}` }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase });
609
783
  }
610
- const validationConfig = localArtifactValidationConfig(resolved.repoRoot, localFamily);
611
784
  const local = validateLocalArtifact(artifactContent, {
612
785
  family: localFamily,
613
- requiredSections: validationConfig.requiredSections,
614
- requiredPatterns: validationConfig.requiredPatterns
786
+ taskContent: content,
787
+ artifact: completedArtifact.name
615
788
  });
616
789
  if (!local.ok) {
617
790
  return failed(normalized, {
@@ -642,7 +815,7 @@ function applyTaskEventUnlocked(request: TaskEventRequest, options: TaskEventOpt
642
815
  message: `cannot read local finalizer provenance for ${completedArtifact.name}: ${error instanceof Error ? error.message : String(error)}`
643
816
  }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase });
644
817
  }
645
- if (!localFinalizationIntent || !['passed', 'consumed'].includes(localFinalizationIntent.state)) {
818
+ if (!localFinalizationIntent || !['passed', 'consumed', 'finalize-ready', 'commit-started'].includes(localFinalizationIntent.state)) {
646
819
  return failed(normalized, {
647
820
  code: 'EVENT_ARTIFACT_CONFLICT',
648
821
  message: `local finalizer provenance is missing or incomplete for ${completedArtifact.name}`
@@ -654,6 +827,18 @@ function applyTaskEventUnlocked(request: TaskEventRequest, options: TaskEventOpt
654
827
  message: `local finalizer provenance does not match ${completedArtifact.name}`
655
828
  }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase });
656
829
  }
830
+ if (localFinalizationIntent.recoveryOperationId !== null) {
831
+ if (!lifecycleAuthority
832
+ || localFinalizationIntent.recoveryOperationId !== lifecycleAuthority.operationId
833
+ || localFinalizationIntent.requestId !== lifecycleAuthority.lifecycleRequestId
834
+ || (localFinalizationIntent.phase !== 'artifact.finalize-local'
835
+ && localFinalizationIntent.authorityDigest !== lifecycleRecoveryAttestationDigest(lifecycleAuthority))) {
836
+ return failed(normalized, {
837
+ code: 'EVENT_ARTIFACT_CONFLICT',
838
+ message: `local finalizer provenance does not match lifecycle completion authority for ${completedArtifact.name}`
839
+ }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase, artifactContext });
840
+ }
841
+ }
657
842
  }
658
843
  }
659
844
  if (eventIdentity.phase === 'waiting' && section.entries.some((entry) => entry.step === eventIdentity.action && entry.note === eventIdentity.note)) {
@@ -730,6 +915,13 @@ function applyTaskEventUnlocked(request: TaskEventRequest, options: TaskEventOpt
730
915
  const body = appendActivityEntry(section, { time: metadata.timestamp, step: logStep, agent: normalized.agent, note: eventIdentity.note });
731
916
  const frontmatterSet: Record<string, string> = { current_step: step, assigned_to: normalized.agent };
732
917
  let frontmatterRemove: string[] | undefined;
918
+ if (eventIdentity.phase === 'started' && artifactContext) {
919
+ try {
920
+ frontmatterSet.qualification_input_relations = JSON.stringify(qualificationRelationsForStarted(eventIdentity.family, artifactContext.inputs));
921
+ } catch (error) {
922
+ return failed(normalized, { code: 'EVENT_ARTIFACT_CONFLICT', message: `cannot capture qualification input relations: ${error instanceof Error ? error.message : String(error)}` }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: step, action: eventIdentity.action, phase: eventIdentity.phase, artifactContext });
923
+ }
924
+ }
733
925
  if (eventIdentity.phase === 'started' && eventIdentity.family === 'code') {
734
926
  const planInput = artifactContext?.inputs.find((input) => input.family === 'plan');
735
927
  if (!planInput) return failed(normalized, { code: 'EVENT_ARTIFACT_CONFLICT', message: 'code.started plan input context is unavailable' }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: step, action: eventIdentity.action, phase: eventIdentity.phase, artifactContext });
@@ -754,6 +946,7 @@ function applyTaskEventUnlocked(request: TaskEventRequest, options: TaskEventOpt
754
946
  } else if (eventIdentity.phase === 'completed' && eventIdentity.family.startsWith('review-')) {
755
947
  frontmatterRemove = ['review_input_artifact', 'review_input_sha256'];
756
948
  }
949
+ if (eventIdentity.phase === 'completed') frontmatterRemove = [...(frontmatterRemove ?? []), 'qualification_input_relations'];
757
950
  if (eventIdentity.phase === 'started' && normalized.implementationInput) frontmatterSet.last_reviewed_commit = '';
758
951
  const mutations: Parameters<typeof writeTask>[0]['mutations'][number][] = [
759
952
  { kind: 'frontmatter', set: frontmatterSet, remove: frontmatterRemove }
@@ -791,13 +984,26 @@ function applyTaskEventUnlocked(request: TaskEventRequest, options: TaskEventOpt
791
984
  });
792
985
  }
793
986
  mutations.push({ kind: 'section', aliases: ['活动日志', 'Activity Log'], heading: section.heading, body });
794
- if (!normalized.dryRun && localFinalizationIntent && completedArtifact && localFinalizationIntent.state === 'passed') {
987
+ if (!normalized.dryRun && localFinalizationIntent && completedArtifact) {
795
988
  try {
796
- localFinalizationIntent = consumeLocalArtifactFinalizationIntent(resolved.repoRoot, localFinalizationIntent);
989
+ if (lifecycleAuthority && localFinalizationIntent.state === 'passed') {
990
+ const expectedIntent = localFinalizationIntent;
991
+ localFinalizationIntent = {
992
+ ...localFinalizationIntent,
993
+ state: 'commit-started',
994
+ phase: 'task-event.completed',
995
+ authorityDigest: lifecycleRecoveryAttestationDigest(lifecycleAuthority),
996
+ requestId: lifecycleAuthority.lifecycleRequestId,
997
+ updatedAt: Date.now()
998
+ };
999
+ writeArtifactRepairIntent(resolved.repoRoot, localFinalizationIntent, { expected: expectedIntent });
1000
+ } else if (!lifecycleAuthority && localFinalizationIntent.state === 'passed') {
1001
+ localFinalizationIntent = consumeLocalArtifactFinalizationIntent(resolved.repoRoot, localFinalizationIntent);
1002
+ }
797
1003
  } catch (error) {
798
1004
  return failed(normalized, {
799
1005
  code: 'EVENT_ARTIFACT_CONFLICT',
800
- message: `local finalizer provenance could not be consumed before task write for ${completedArtifact.name}: ${error instanceof Error ? error.message : String(error)}`
1006
+ message: `local finalizer provenance could not be advanced before task write for ${completedArtifact.name}: ${error instanceof Error ? error.message : String(error)}`
801
1007
  }, {
802
1008
  taskId: resolved.taskId,
803
1009
  taskMdPath: resolved.taskMdPath,
@@ -813,6 +1019,17 @@ function applyTaskEventUnlocked(request: TaskEventRequest, options: TaskEventOpt
813
1019
  && ['analyze', 'plan', 'code'].includes(eventIdentity.family);
814
1020
  const result = writeTask({ taskRef: normalized.taskRef, expectedState: stateOverride ? resolved.state : 'active', dryRun: normalized.dryRun, mutations }, { ...options, invalidationContext: sourceCompletion ? 'source-completion' : 'standard', metadataProvider: () => metadata });
815
1021
  if (result.status === 'failed') 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 });
1022
+ if (!normalized.dryRun && lifecycleAuthority && localFinalizationIntent && completedArtifact) {
1023
+ try {
1024
+ if (!options.deferLifecycleRecoveryConsumption) consumeLifecycleRecoveryAttestation(lifecycleAuthority);
1025
+ writeArtifactRepairIntent(resolved.repoRoot, { ...localFinalizationIntent, state: 'consumed', updatedAt: Date.now() }, { expected: localFinalizationIntent });
1026
+ } catch (error) {
1027
+ return failed(normalized, {
1028
+ code: 'EVENT_ARTIFACT_CONFLICT',
1029
+ message: `task completion was written but lifecycle recovery consumption is incomplete: ${error instanceof Error ? error.message : String(error)}`
1030
+ }, { 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 });
1031
+ }
1032
+ }
816
1033
  if (!normalized.dryRun && orchestrationCompletion) {
817
1034
  try {
818
1035
  (options.commitOrchestrationCompletion ?? commitOrchestrationStageCompletion)(orchestrationCompletion);
@@ -0,0 +1,11 @@
1
+ import type { TaskFinalizationReceipt } from './finalization.ts';
2
+
3
+ export type TaskControlBinding = NonNullable<TaskFinalizationReceipt['controlBinding']>;
4
+ export type TaskControlBindingEvidence = (taskId: string, binding: TaskControlBinding) => 'terminal' | 'pending' | 'mismatch';
5
+
6
+ export function taskFinalizationReceiptState(receipt: TaskFinalizationReceipt): 'pending' | 'unresolved' | 'complete' {
7
+ if (receipt.lifecycle !== 'done' || receipt.taskComment === 'pending'
8
+ || receipt.verification === 'pending' || receipt.warningProjection !== 'done') return 'pending';
9
+ if (receipt.lastError !== null || receipt.warnings.some((warning) => warning.status === 'open')) return 'unresolved';
10
+ return 'complete';
11
+ }
@@ -2,6 +2,7 @@ import fs from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import { createHash, randomUUID } from 'node:crypto';
4
4
 
5
+ import { parseArtifactName } from './artifact-name.ts';
5
6
  import { locateActivityLog } from './activity-log.ts';
6
7
  import { applyPrReviewActivityIntent } from './activity-intent.ts';
7
8
  import { applyHumanDecision } from './decision-intents.ts';
@@ -814,14 +815,8 @@ async function probeVerificationFailure(taskRef: string, policy: FailurePolicy,
814
815
  function latestArtifact(taskDir: string, family: string): string | undefined {
815
816
  let names: string[];
816
817
  try { names = fs.readdirSync(taskDir); } catch { return undefined; }
817
- const candidates = names.filter((name) => (
818
- name === `${family}.md` || new RegExp(`^${family}-r\\d+\\.md$`).test(name)
819
- ));
820
- candidates.sort((a, b) => {
821
- const round = (name: string) => Number(/-r(\d+)\.md$/.exec(name)?.[1] ?? 1);
822
- return round(b) - round(a);
823
- });
824
- return candidates[0];
818
+ return names.map(parseArtifactName).filter((identity) => identity !== null)
819
+ .filter((identity) => identity.family === family).sort((left, right) => right.round - left.round)[0]?.name;
825
820
  }
826
821
 
827
822
  async function probeVerificationEngineFailure(policy: FailurePolicy, resolved: ProbeTask): Promise<ProducerFailure | OverrideError> {
@@ -1,3 +1,4 @@
1
+ import { parseArtifactName } from './artifact-name.ts';
1
2
  import { parseTable } from './sections.ts';
2
3
 
3
4
  const SECTION_ALIASES = ['实现输入', 'Implementation Inputs'] as const;
@@ -6,7 +7,6 @@ const COLUMNS = [
6
7
  'decided_at', 'status', 'consumed_by'
7
8
  ] as const;
8
9
  const TIMESTAMP_RE = /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}[+-]\d{2}:\d{2}$/;
9
- const ARTIFACT_RE = /^code(?:-r(?:[2-9]|[1-9]\d+))?\.md$/;
10
10
 
11
11
  type ImplementationInputStatus = 'declared' | 'pending' | 'not-required' | 'consumed';
12
12
  type ImplementationInput = {
@@ -36,7 +36,7 @@ function validateInput(row: ImplementationInput): void {
36
36
  (row.status === 'declared' && row.decidedAt === '' && row.consumedBy === '') ||
37
37
  (hasValidTimestamp && row.needsImplementation && row.status === 'pending' && row.consumedBy === '') ||
38
38
  (hasValidTimestamp && !row.needsImplementation && row.status === 'not-required' && row.consumedBy === '') ||
39
- (hasValidTimestamp && row.needsImplementation && row.status === 'consumed' && ARTIFACT_RE.test(row.consumedBy));
39
+ (hasValidTimestamp && row.needsImplementation && row.status === 'consumed' && parseArtifactName(row.consumedBy)?.family === 'code');
40
40
  if (!valid) throw new Error(`implementation input ${row.id} has an invalid state combination`);
41
41
  }
42
42
 
@@ -143,7 +143,7 @@ function selectPendingImplementationInput(
143
143
  function consumeImplementationInput(
144
144
  rows: readonly ImplementationInput[], id: string, artifact: string
145
145
  ): ImplementationInput[] {
146
- if (!ARTIFACT_RE.test(artifact)) throw new Error(`implementation input artifact '${artifact}' is invalid`);
146
+ if (parseArtifactName(artifact)?.family !== 'code') throw new Error(`implementation input artifact '${artifact}' is invalid`);
147
147
  let found = false;
148
148
  const next = rows.map((row) => {
149
149
  if (row.id !== id) return row;