@fitlab-ai/agent-infra 0.9.10 → 0.9.12

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 (375) hide show
  1. package/README.md +19 -9
  2. package/README.zh-CN.md +19 -9
  3. package/bin/cli.ts +17 -4
  4. package/dist/bin/cli.js +19 -4
  5. package/dist/bin/internal-cli.js +55 -4
  6. package/dist/lib/agent-client.js +0 -1
  7. package/dist/lib/agent-clients/adapters/codex-lifecycle/build-identity.js +71 -10
  8. package/dist/lib/agent-clients/adapters/opencode-sandbox.js +0 -4
  9. package/dist/lib/agent-clients/adapters/traecli.js +7 -8
  10. package/dist/lib/agent-clients/config.js +14 -99
  11. package/dist/lib/agent-clients/reconcile.js +1 -11
  12. package/dist/lib/agent-clients/schema.js +5 -4
  13. package/dist/lib/agent-clients/tokens.js +5 -15
  14. package/dist/lib/decide.js +14 -30
  15. package/dist/lib/defaults.json +8 -0
  16. package/dist/lib/git/workflow.js +43 -1
  17. package/dist/lib/init.js +3 -2
  18. package/dist/lib/internal/git-workflow.js +8 -1
  19. package/dist/lib/internal/platform-pr.js +8 -2
  20. package/dist/lib/internal/release-workflow.js +0 -1
  21. package/dist/lib/internal/task-delivery.js +59 -0
  22. package/dist/lib/internal/task-finalization.js +19 -9
  23. package/dist/lib/internal/task-ledger.js +8 -2
  24. package/dist/lib/internal/task-lifecycle.js +17 -93
  25. package/dist/lib/internal/task-orchestration.js +30 -216
  26. package/dist/lib/internal/task-validate.js +2 -1
  27. package/dist/lib/merge.js +205 -103
  28. package/dist/lib/platform/github-client.js +7 -1
  29. package/dist/lib/platform/pr-checks.js +8 -5
  30. package/dist/lib/platform/pr-summary.js +12 -8
  31. package/dist/lib/platform/pull-requests.js +393 -91
  32. package/dist/lib/platform/verification-required.js +13 -2
  33. package/dist/lib/platform/verification-sync.js +9 -4
  34. package/dist/lib/pr-review/evidence-grading.js +12 -4
  35. package/dist/lib/process-data/normalize.js +2 -9
  36. package/dist/lib/run/host.js +46 -16
  37. package/dist/lib/sandbox/agent-client-reconciler.js +4 -12
  38. package/dist/lib/sandbox/commands/create.js +3 -3
  39. package/dist/lib/sandbox/commands/enter.js +18 -4
  40. package/dist/lib/sandbox/config.js +8 -12
  41. package/dist/lib/sandbox/control/executor.js +131 -39
  42. package/dist/lib/sandbox/control/lifecycle.js +55 -24
  43. package/dist/lib/sandbox/control/server.js +18 -16
  44. package/dist/lib/sandbox/mounts.js +3 -2
  45. package/dist/lib/sandbox/recovery.js +123 -22
  46. package/dist/lib/sandbox/workspace-identity.js +14 -0
  47. package/dist/lib/sandbox/workspace-view.js +8 -9
  48. package/dist/lib/self-update.js +278 -0
  49. package/dist/lib/server/process-state.js +2 -2
  50. package/dist/lib/server/protocol.js +5 -5
  51. package/dist/lib/task/activity-log.js +4 -1
  52. package/dist/lib/task/artifact-lifecycle.js +6 -4
  53. package/dist/lib/task/codex-orchestration.js +1 -1
  54. package/dist/lib/task/command-options.js +1 -3
  55. package/dist/lib/task/commands/cat.js +0 -5
  56. package/dist/lib/task/commands/decisions.js +76 -92
  57. package/dist/lib/task/commands/files.js +6 -6
  58. package/dist/lib/task/commands/grep.js +25 -61
  59. package/dist/lib/task/commands/issue-body.js +6 -6
  60. package/dist/lib/task/commands/log.js +25 -10
  61. package/dist/lib/task/commands/show.js +5 -6
  62. package/dist/lib/task/commands/status.js +8 -8
  63. package/dist/lib/task/commit-intent.js +100 -0
  64. package/dist/lib/task/commit-operation.js +181 -91
  65. package/dist/lib/task/commit-policy.js +1 -1
  66. package/dist/lib/task/control-authority.js +431 -0
  67. package/dist/lib/task/create.js +39 -8
  68. package/dist/lib/task/current-contract.js +35 -0
  69. package/dist/lib/task/decision-details.js +218 -0
  70. package/dist/lib/task/decision-intents.js +5 -2
  71. package/dist/lib/task/delivery-target.js +97 -0
  72. package/dist/lib/task/delivery.js +153 -0
  73. package/dist/lib/task/events.js +11 -5
  74. package/dist/lib/task/human-override.js +5 -2
  75. package/dist/lib/task/index.js +19 -20
  76. package/dist/lib/task/ledger-intents.js +8 -9
  77. package/dist/lib/task/ledger.js +13 -6
  78. package/dist/lib/task/lifecycle.js +122 -9
  79. package/dist/lib/task/orchestration.js +105 -75
  80. package/dist/lib/task/pr-delivery-fact.js +182 -0
  81. package/dist/lib/task/review-artifacts.js +31 -12
  82. package/dist/lib/task/review-finalization.js +13 -4
  83. package/dist/lib/task/review-fingerprint.js +8 -0
  84. package/dist/lib/task/verification-engine.js +86 -39
  85. package/dist/lib/task/verification-review.js +2 -2
  86. package/dist/lib/task/workspace-snapshot.js +5 -11
  87. package/dist/lib/task/write.js +11 -1
  88. package/dist/lib/update.js +13 -35
  89. package/dist/lib/version.js +5 -1
  90. package/dist/lifecycle-build-manifest.json +3 -3
  91. package/lib/agent-client.ts +0 -1
  92. package/lib/agent-clients/adapters/codex-lifecycle/build-identity.ts +105 -10
  93. package/lib/agent-clients/adapters/codex-lifecycle/manifest-files.json +12 -1
  94. package/lib/agent-clients/adapters/opencode-sandbox.ts +0 -5
  95. package/lib/agent-clients/adapters/traecli.ts +7 -8
  96. package/lib/agent-clients/config.ts +14 -144
  97. package/lib/agent-clients/reconcile.ts +1 -12
  98. package/lib/agent-clients/schema.ts +5 -4
  99. package/lib/agent-clients/tokens.ts +5 -18
  100. package/lib/decide.ts +12 -22
  101. package/lib/defaults.json +8 -0
  102. package/lib/git/workflow.ts +38 -1
  103. package/lib/init.ts +5 -2
  104. package/lib/internal/git-workflow.ts +7 -1
  105. package/lib/internal/platform-pr.ts +8 -1
  106. package/lib/internal/release-workflow.ts +0 -1
  107. package/lib/internal/task-delivery.ts +53 -0
  108. package/lib/internal/task-finalization.ts +27 -13
  109. package/lib/internal/task-ledger.ts +8 -2
  110. package/lib/internal/task-lifecycle.ts +33 -76
  111. package/lib/internal/task-orchestration.ts +33 -190
  112. package/lib/internal/task-validate.ts +3 -2
  113. package/lib/merge.ts +236 -120
  114. package/lib/platform/github-client.ts +8 -1
  115. package/lib/platform/pr-checks.ts +7 -5
  116. package/lib/platform/pr-summary.ts +10 -7
  117. package/lib/platform/pull-requests.ts +425 -87
  118. package/lib/platform/verification-required.ts +12 -2
  119. package/lib/platform/verification-sync.ts +9 -4
  120. package/lib/pr-review/evidence-grading.ts +7 -4
  121. package/lib/process-data/normalize.ts +2 -8
  122. package/lib/run/host.ts +63 -14
  123. package/lib/sandbox/agent-client-reconciler.ts +5 -15
  124. package/lib/sandbox/commands/create.ts +4 -4
  125. package/lib/sandbox/commands/enter.ts +24 -4
  126. package/lib/sandbox/config.ts +9 -14
  127. package/lib/sandbox/control/container-identity.ts +2 -2
  128. package/lib/sandbox/control/executor.ts +148 -51
  129. package/lib/sandbox/control/lifecycle.ts +59 -31
  130. package/lib/sandbox/control/protocol.ts +1 -3
  131. package/lib/sandbox/control/server.ts +15 -12
  132. package/lib/sandbox/control/state.ts +7 -7
  133. package/lib/sandbox/mounts.ts +4 -3
  134. package/lib/sandbox/recovery.ts +162 -34
  135. package/lib/sandbox/tools.ts +1 -1
  136. package/lib/sandbox/workspace-identity.ts +28 -0
  137. package/lib/sandbox/workspace-view.ts +14 -10
  138. package/lib/self-update.ts +341 -0
  139. package/lib/server/process-state.ts +2 -2
  140. package/lib/server/protocol.ts +5 -5
  141. package/lib/task/activity-log.ts +6 -1
  142. package/lib/task/artifact-lifecycle.ts +7 -5
  143. package/lib/task/codex-orchestration.ts +2 -1
  144. package/lib/task/command-options.ts +1 -2
  145. package/lib/task/commands/cat.ts +0 -4
  146. package/lib/task/commands/decisions.ts +80 -75
  147. package/lib/task/commands/files.ts +6 -6
  148. package/lib/task/commands/grep.ts +23 -51
  149. package/lib/task/commands/issue-body.ts +6 -6
  150. package/lib/task/commands/log.ts +22 -10
  151. package/lib/task/commands/show.ts +5 -6
  152. package/lib/task/commands/status.ts +8 -8
  153. package/lib/task/commit-intent.ts +137 -0
  154. package/lib/task/commit-operation.ts +161 -79
  155. package/lib/task/commit-policy.ts +1 -2
  156. package/lib/task/control-authority.ts +563 -0
  157. package/lib/task/create.ts +40 -10
  158. package/lib/task/current-contract.ts +49 -0
  159. package/lib/task/decision-details.ts +280 -0
  160. package/lib/task/decision-intents.ts +7 -2
  161. package/lib/task/delivery-target.ts +130 -0
  162. package/lib/task/delivery.ts +160 -0
  163. package/lib/task/events.ts +10 -5
  164. package/lib/task/human-override.ts +5 -2
  165. package/lib/task/index.ts +19 -20
  166. package/lib/task/ledger-intents.ts +6 -8
  167. package/lib/task/ledger.ts +17 -7
  168. package/lib/task/lifecycle.ts +113 -4
  169. package/lib/task/orchestration.ts +102 -80
  170. package/lib/task/pr-delivery-fact.ts +254 -0
  171. package/lib/task/review-artifacts.ts +53 -15
  172. package/lib/task/review-finalization.ts +21 -5
  173. package/lib/task/review-fingerprint.ts +10 -0
  174. package/lib/task/verification-engine.ts +78 -46
  175. package/lib/task/verification-review.ts +3 -1
  176. package/lib/task/workspace-snapshot.ts +4 -13
  177. package/lib/task/write.ts +20 -1
  178. package/lib/update.ts +14 -38
  179. package/lib/version.ts +6 -1
  180. package/package.json +1 -1
  181. package/templates/.agents/README.en.md +5 -3
  182. package/templates/.agents/README.zh-CN.md +5 -3
  183. package/templates/.agents/rules/README.en.md +4 -2
  184. package/templates/.agents/rules/README.zh-CN.md +4 -2
  185. package/templates/.agents/rules/compatibility-policy.en.md +5 -4
  186. package/templates/.agents/rules/compatibility-policy.zh-CN.md +5 -4
  187. package/templates/.agents/rules/create-issue.en.md +11 -2
  188. package/templates/.agents/rules/create-issue.zh-CN.md +24 -2
  189. package/templates/.agents/rules/human-decision-context.en.md +9 -7
  190. package/templates/.agents/rules/human-decision-context.zh-CN.md +9 -7
  191. package/templates/.agents/rules/{issue-fields.github.en.md → issue-fields.en.md} +1 -1
  192. package/templates/.agents/rules/{issue-fields.github.zh-CN.md → issue-fields.zh-CN.md} +1 -1
  193. package/templates/.agents/rules/issue-pr-commands.en.md +5 -18
  194. package/templates/.agents/rules/issue-pr-commands.zh-CN.md +1 -1
  195. package/templates/.agents/rules/issue-sync.en.md +26 -5
  196. package/templates/.agents/rules/issue-sync.zh-CN.md +62 -5
  197. package/templates/.agents/rules/label-milestone-setup.en.md +15 -2
  198. package/templates/.agents/rules/label-milestone-setup.zh-CN.md +15 -2
  199. package/templates/.agents/rules/local-artifact-repair.en.md +46 -0
  200. package/templates/.agents/rules/local-artifact-repair.zh-CN.md +46 -0
  201. package/templates/.agents/rules/milestone-inference.en.md +12 -2
  202. package/templates/.agents/rules/milestone-inference.zh-CN.md +25 -3
  203. package/templates/.agents/rules/next-step-output.en.md +3 -3
  204. package/templates/.agents/rules/next-step-output.zh-CN.md +3 -3
  205. package/templates/.agents/rules/no-mid-flow-questions.en.md +1 -1
  206. package/templates/.agents/rules/no-mid-flow-questions.zh-CN.md +1 -1
  207. package/templates/.agents/rules/pr-sync.en.md +23 -14
  208. package/templates/.agents/rules/pr-sync.zh-CN.md +23 -2
  209. package/templates/.agents/rules/release-commands.en.md +42 -3
  210. package/templates/.agents/rules/release-commands.zh-CN.md +42 -3
  211. package/templates/.agents/rules/review-handshake.en.md +1 -1
  212. package/templates/.agents/rules/review-handshake.zh-CN.md +1 -1
  213. package/templates/.agents/rules/security-alerts.en.md +17 -2
  214. package/templates/.agents/rules/security-alerts.zh-CN.md +17 -2
  215. package/templates/.agents/rules/sync-content-generation.en.md +8 -0
  216. package/templates/.agents/rules/sync-content-generation.zh-CN.md +8 -0
  217. package/templates/.agents/rules/task-management.en.md +2 -3
  218. package/templates/.agents/rules/task-management.zh-CN.md +2 -3
  219. package/templates/.agents/scripts/security-alerts.github.sh +151 -0
  220. package/templates/.agents/scripts/security-alerts.sh +31 -0
  221. package/templates/.agents/skills/analyze-task/SKILL.en.md +4 -3
  222. package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +4 -3
  223. package/templates/.agents/skills/block-task/SKILL.en.md +2 -2
  224. package/templates/.agents/skills/block-task/SKILL.zh-CN.md +2 -2
  225. package/templates/.agents/skills/cancel-task/SKILL.en.md +2 -2
  226. package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +2 -2
  227. package/templates/.agents/skills/check-task/SKILL.en.md +2 -2
  228. package/templates/.agents/skills/check-task/SKILL.zh-CN.md +2 -2
  229. package/templates/.agents/skills/close-codescan/SKILL.en.md +3 -3
  230. package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +3 -3
  231. package/templates/.agents/skills/close-dependabot/SKILL.en.md +3 -3
  232. package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +3 -3
  233. package/templates/.agents/skills/code-task/SKILL.en.md +8 -5
  234. package/templates/.agents/skills/code-task/SKILL.zh-CN.md +9 -6
  235. package/templates/.agents/skills/commit/SKILL.en.md +30 -10
  236. package/templates/.agents/skills/commit/SKILL.zh-CN.md +6 -7
  237. package/templates/.agents/skills/commit/config/verify.json +1 -1
  238. package/templates/.agents/skills/commit/reference/commit-orchestration.en.md +3 -3
  239. package/templates/.agents/skills/commit/reference/commit-orchestration.zh-CN.md +3 -3
  240. package/templates/.agents/skills/commit/reference/pr-summary-sync.en.md +1 -1
  241. package/templates/.agents/skills/commit/reference/pr-summary-sync.zh-CN.md +1 -1
  242. package/templates/.agents/skills/commit/reference/task-status-update.en.md +2 -2
  243. package/templates/.agents/skills/commit/reference/task-status-update.zh-CN.md +2 -2
  244. package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +7 -6
  245. package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +7 -6
  246. package/templates/.agents/skills/complete-manual-validation/config/verify.en.json +1 -1
  247. package/templates/.agents/skills/complete-manual-validation/config/verify.zh-CN.json +1 -1
  248. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.en.md +1 -1
  249. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.zh-CN.md +1 -1
  250. package/templates/.agents/skills/complete-task/SKILL.en.md +13 -13
  251. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +13 -13
  252. package/templates/.agents/skills/complete-task/reference/external-delivery.en.md +2 -2
  253. package/templates/.agents/skills/complete-task/reference/external-delivery.zh-CN.md +2 -2
  254. package/templates/.agents/skills/create-pr/SKILL.en.md +9 -11
  255. package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +9 -11
  256. package/templates/.agents/skills/create-pr/reference/change-report.en.md +9 -4
  257. package/templates/.agents/skills/create-pr/reference/change-report.zh-CN.md +9 -4
  258. package/templates/.agents/skills/create-pr/scripts/change-report.mjs +203 -0
  259. package/templates/.agents/skills/create-release-note/SKILL.en.md +5 -6
  260. package/templates/.agents/skills/create-release-note/SKILL.zh-CN.md +5 -6
  261. package/templates/.agents/skills/create-task/SKILL.en.md +2 -1
  262. package/templates/.agents/skills/create-task/SKILL.zh-CN.md +2 -1
  263. package/templates/.agents/skills/import-codescan/SKILL.en.md +1 -1
  264. package/templates/.agents/skills/import-codescan/SKILL.zh-CN.md +1 -1
  265. package/templates/.agents/skills/import-dependabot/SKILL.en.md +1 -1
  266. package/templates/.agents/skills/import-dependabot/SKILL.zh-CN.md +1 -1
  267. package/templates/.agents/skills/import-issue/SKILL.en.md +1 -1
  268. package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +1 -1
  269. package/templates/.agents/skills/init-labels/SKILL.en.md +9 -10
  270. package/templates/.agents/skills/init-labels/SKILL.zh-CN.md +9 -10
  271. package/templates/.agents/skills/init-labels/scripts/init-labels.github.sh +57 -1
  272. package/templates/.agents/skills/init-labels/scripts/init-labels.sh +27 -1
  273. package/templates/.agents/skills/init-milestones/SKILL.en.md +3 -5
  274. package/templates/.agents/skills/init-milestones/SKILL.zh-CN.md +3 -5
  275. package/templates/.agents/skills/plan-task/SKILL.en.md +4 -3
  276. package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +4 -3
  277. package/templates/.agents/skills/restore-task/SKILL.en.md +1 -1
  278. package/templates/.agents/skills/restore-task/SKILL.zh-CN.md +1 -1
  279. package/templates/.agents/skills/review-analysis/SKILL.en.md +8 -5
  280. package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +8 -5
  281. package/templates/.agents/skills/review-analysis/config/verify.en.json +1 -0
  282. package/templates/.agents/skills/review-analysis/config/verify.zh-CN.json +1 -0
  283. package/templates/.agents/skills/review-analysis/reference/output-templates.en.md +18 -0
  284. package/templates/.agents/skills/review-analysis/reference/output-templates.zh-CN.md +18 -0
  285. package/templates/.agents/skills/review-code/SKILL.en.md +11 -8
  286. package/templates/.agents/skills/review-code/SKILL.zh-CN.md +11 -8
  287. package/templates/.agents/skills/review-code/config/verify.en.json +1 -0
  288. package/templates/.agents/skills/review-code/config/verify.zh-CN.json +1 -0
  289. package/templates/.agents/skills/review-code/reference/migration-risks.en.md +2 -1
  290. package/templates/.agents/skills/review-code/reference/migration-risks.zh-CN.md +2 -1
  291. package/templates/.agents/skills/review-code/reference/output-templates.en.md +19 -1
  292. package/templates/.agents/skills/review-code/reference/output-templates.zh-CN.md +19 -1
  293. package/templates/.agents/skills/review-code/reference/report-template.en.md +4 -2
  294. package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +4 -2
  295. package/templates/.agents/skills/review-plan/SKILL.en.md +8 -5
  296. package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +8 -5
  297. package/templates/.agents/skills/review-plan/config/verify.en.json +1 -0
  298. package/templates/.agents/skills/review-plan/config/verify.zh-CN.json +1 -0
  299. package/templates/.agents/skills/review-plan/reference/output-templates.en.md +18 -0
  300. package/templates/.agents/skills/review-plan/reference/output-templates.zh-CN.md +18 -0
  301. package/templates/.agents/skills/review-pr/SKILL.en.md +1 -0
  302. package/templates/.agents/skills/review-pr/SKILL.zh-CN.md +1 -0
  303. package/templates/.agents/skills/review-pr/reference/evidence-grading.en.md +2 -2
  304. package/templates/.agents/skills/review-pr/reference/evidence-grading.zh-CN.md +2 -2
  305. package/templates/.agents/skills/run-manual-validation/SKILL.en.md +5 -0
  306. package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +5 -0
  307. package/templates/.agents/skills/run-task/SKILL.en.md +5 -1
  308. package/templates/.agents/skills/run-task/SKILL.zh-CN.md +5 -1
  309. package/templates/.agents/skills/update-agent-infra/SKILL.en.md +2 -2
  310. package/templates/.agents/skills/update-agent-infra/SKILL.zh-CN.md +2 -2
  311. package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +144 -109
  312. package/templates/.agents/skills/watch-pr/SKILL.en.md +5 -5
  313. package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +5 -5
  314. package/templates/.agents/templates/task.en.md +6 -2
  315. package/templates/.agents/templates/task.zh-CN.md +6 -2
  316. package/templates/.agents/workflows/bug-fix.en.yaml +9 -8
  317. package/templates/.agents/workflows/bug-fix.zh-CN.yaml +9 -8
  318. package/templates/.agents/workflows/feature-development.en.yaml +9 -8
  319. package/templates/.agents/workflows/feature-development.zh-CN.yaml +9 -8
  320. package/templates/.agents/workflows/refactoring.en.yaml +9 -8
  321. package/templates/.agents/workflows/refactoring.zh-CN.yaml +9 -8
  322. package/templates/.claude/commands/analyze-task.en.md +1 -1
  323. package/templates/.claude/commands/analyze-task.zh-CN.md +1 -1
  324. package/templates/.claude/commands/block-task.en.md +1 -1
  325. package/templates/.claude/commands/block-task.zh-CN.md +1 -1
  326. package/templates/.claude/commands/cancel-task.en.md +1 -1
  327. package/templates/.claude/commands/cancel-task.zh-CN.md +1 -1
  328. package/templates/.claude/commands/check-task.en.md +1 -1
  329. package/templates/.claude/commands/check-task.zh-CN.md +1 -1
  330. package/templates/.claude/commands/code-task.en.md +1 -1
  331. package/templates/.claude/commands/code-task.zh-CN.md +1 -1
  332. package/templates/.claude/commands/commit.en.md +1 -1
  333. package/templates/.claude/commands/commit.zh-CN.md +1 -1
  334. package/templates/.claude/commands/complete-manual-validation.en.md +1 -1
  335. package/templates/.claude/commands/complete-manual-validation.zh-CN.md +1 -1
  336. package/templates/.claude/commands/complete-task.en.md +1 -1
  337. package/templates/.claude/commands/complete-task.zh-CN.md +1 -1
  338. package/templates/.claude/commands/create-pr.en.md +1 -1
  339. package/templates/.claude/commands/create-pr.zh-CN.md +1 -1
  340. package/templates/.claude/commands/plan-task.en.md +1 -1
  341. package/templates/.claude/commands/plan-task.zh-CN.md +1 -1
  342. package/templates/.claude/commands/review-analysis.en.md +1 -1
  343. package/templates/.claude/commands/review-analysis.zh-CN.md +1 -1
  344. package/templates/.claude/commands/review-code.en.md +1 -1
  345. package/templates/.claude/commands/review-code.zh-CN.md +1 -1
  346. package/templates/.claude/commands/review-plan.en.md +1 -1
  347. package/templates/.claude/commands/review-plan.zh-CN.md +1 -1
  348. package/templates/.claude/commands/run-manual-validation.en.md +1 -1
  349. package/templates/.claude/commands/run-manual-validation.zh-CN.md +1 -1
  350. package/templates/.claude/commands/run-task.en.md +1 -1
  351. package/templates/.claude/commands/run-task.zh-CN.md +1 -1
  352. package/templates/.claude/commands/watch-pr.en.md +1 -1
  353. package/templates/.claude/commands/watch-pr.zh-CN.md +1 -1
  354. package/templates/{.trae/skills → .traecli/commands}/update-agent-infra.en.md +0 -1
  355. package/templates/{.trae/skills → .traecli/commands}/update-agent-infra.zh-CN.md +0 -1
  356. package/dist/lib/builtin-tuis.js +0 -40
  357. package/lib/builtin-tuis.ts +0 -56
  358. package/templates/.agents/rules/create-issue.github.en.md +0 -14
  359. package/templates/.agents/rules/create-issue.github.zh-CN.md +0 -27
  360. package/templates/.agents/rules/issue-pr-commands.github.en.md +0 -34
  361. package/templates/.agents/rules/issue-pr-commands.github.zh-CN.md +0 -49
  362. package/templates/.agents/rules/issue-sync.github.en.md +0 -40
  363. package/templates/.agents/rules/issue-sync.github.zh-CN.md +0 -76
  364. package/templates/.agents/rules/label-milestone-setup.github.en.md +0 -60
  365. package/templates/.agents/rules/label-milestone-setup.github.zh-CN.md +0 -60
  366. package/templates/.agents/rules/milestone-inference.github.en.md +0 -15
  367. package/templates/.agents/rules/milestone-inference.github.zh-CN.md +0 -27
  368. package/templates/.agents/rules/pr-checks-commands.github.en.md +0 -18
  369. package/templates/.agents/rules/pr-checks-commands.github.zh-CN.md +0 -34
  370. package/templates/.agents/rules/pr-sync.github.en.md +0 -43
  371. package/templates/.agents/rules/pr-sync.github.zh-CN.md +0 -69
  372. package/templates/.agents/rules/release-commands.github.en.md +0 -38
  373. package/templates/.agents/rules/release-commands.github.zh-CN.md +0 -38
  374. package/templates/.agents/rules/security-alerts.github.en.md +0 -43
  375. package/templates/.agents/rules/security-alerts.github.zh-CN.md +0 -43
@@ -0,0 +1,182 @@
1
+ const PR_DELIVERY_FACT_KEY = 'pr_delivery_fact';
2
+ function factError(message) {
3
+ return Object.assign(new Error(`PR_DELIVERY_FACT_INVALID: ${message}`), { code: 'PR_DELIVERY_FACT_INVALID' });
4
+ }
5
+ function isRecord(value) {
6
+ return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
7
+ }
8
+ function exactKeys(value, keys, label) {
9
+ const actual = Object.keys(value).sort();
10
+ const expected = [...keys].sort();
11
+ if (actual.length !== expected.length || actual.some((key, index) => key !== expected[index])) {
12
+ throw factError(`${label} has an invalid field set`);
13
+ }
14
+ }
15
+ function text(value, label) {
16
+ if (typeof value !== 'string' || !value || value.trim() !== value)
17
+ throw factError(`${label} must be a non-empty string`);
18
+ return value;
19
+ }
20
+ function nullableText(value, label) {
21
+ return value === null ? null : text(value, label);
22
+ }
23
+ function positiveNumber(value, label) {
24
+ if (!Number.isSafeInteger(value) || value <= 0)
25
+ throw factError(`${label} must be a positive integer`);
26
+ return value;
27
+ }
28
+ function optionalTimestamp(value, label) {
29
+ const result = nullableText(value, label);
30
+ if (result !== null && Number.isNaN(Date.parse(result)))
31
+ throw factError(`${label} must be a valid timestamp`);
32
+ return result;
33
+ }
34
+ function parseIdentity(value) {
35
+ if (!isRecord(value))
36
+ throw factError('identity must be an object');
37
+ exactKeys(value, ['repository', 'number', 'nodeId', 'url', 'head', 'base'], 'identity');
38
+ const head = parseRef(value.head, 'head');
39
+ const base = parseRef(value.base, 'base');
40
+ return {
41
+ repository: text(value.repository, 'identity.repository'),
42
+ number: positiveNumber(value.number, 'identity.number'),
43
+ nodeId: text(value.nodeId, 'identity.nodeId'),
44
+ url: text(value.url, 'identity.url'),
45
+ head,
46
+ base
47
+ };
48
+ }
49
+ function parseRef(value, label) {
50
+ if (!isRecord(value))
51
+ throw factError(`${label} must be an object`);
52
+ exactKeys(value, ['repository', 'ref', 'sha'], label);
53
+ return {
54
+ repository: text(value.repository, `${label}.repository`),
55
+ ref: text(value.ref, `${label}.ref`),
56
+ sha: text(value.sha, `${label}.sha`)
57
+ };
58
+ }
59
+ function parseFact(value) {
60
+ if (!isRecord(value))
61
+ throw factError('fact must be an object');
62
+ if (value.version !== 1 || typeof value.state !== 'string')
63
+ throw factError('version or state is invalid');
64
+ if (value.state === 'unbound') {
65
+ exactKeys(value, ['version', 'state', 'reason'], 'unbound fact');
66
+ if (value.reason !== 'initial')
67
+ throw factError('unbound reason is invalid');
68
+ return { version: 1, state: 'unbound', reason: value.reason };
69
+ }
70
+ if (value.state === 'skipped') {
71
+ exactKeys(value, ['version', 'state', 'reason', 'decidedAt'], 'skipped fact');
72
+ if (value.reason !== 'explicit')
73
+ throw factError('skipped reason is invalid');
74
+ const decidedAt = text(value.decidedAt, 'skipped.decidedAt');
75
+ if (Number.isNaN(Date.parse(decidedAt)))
76
+ throw factError('skipped.decidedAt must be a valid timestamp');
77
+ return { version: 1, state: 'skipped', reason: 'explicit', decidedAt };
78
+ }
79
+ if (value.state !== 'bound')
80
+ throw factError('state is invalid');
81
+ exactKeys(value, ['version', 'state', 'identity', 'binding', 'provenance'], 'bound fact');
82
+ if (!isRecord(value.binding))
83
+ throw factError('binding must be an object');
84
+ exactKeys(value.binding, ['status', 'source', 'verifiedAt', 'issueNumber', 'remoteState', 'mergedAt', 'mergeCommitSha'], 'binding');
85
+ if (value.binding.status !== 'verified')
86
+ throw factError('binding status is invalid');
87
+ const sources = ['created', 'reused', 'explicit-bind', 'external-unique', 'external-explicit'];
88
+ if (!sources.includes(value.binding.source))
89
+ throw factError('binding source is invalid');
90
+ const verifiedAt = text(value.binding.verifiedAt, 'binding.verifiedAt');
91
+ if (Number.isNaN(Date.parse(verifiedAt)))
92
+ throw factError('binding.verifiedAt must be a valid timestamp');
93
+ const issueNumber = value.binding.issueNumber === null ? null : positiveNumber(value.binding.issueNumber, 'binding.issueNumber');
94
+ if (value.binding.remoteState !== 'open' && value.binding.remoteState !== 'closed')
95
+ throw factError('binding.remoteState is invalid');
96
+ const mergedAt = optionalTimestamp(value.binding.mergedAt, 'binding.mergedAt');
97
+ const mergeCommitSha = nullableText(value.binding.mergeCommitSha, 'binding.mergeCommitSha');
98
+ if (Boolean(mergedAt) !== Boolean(mergeCommitSha))
99
+ throw factError('mergedAt and mergeCommitSha must be paired');
100
+ if (!isRecord(value.provenance))
101
+ throw factError('provenance must be an object');
102
+ exactKeys(value.provenance, ['establishedBy'], 'provenance');
103
+ const provenance = ['create-post', 'reuse', 'explicit-bind', 'external-unique', 'external-explicit'];
104
+ if (!provenance.includes(value.provenance.establishedBy))
105
+ throw factError('provenance.establishedBy is invalid');
106
+ if (value.provenance.establishedBy !== provenanceForSource(value.binding.source)) {
107
+ throw factError('binding.source and provenance.establishedBy do not match');
108
+ }
109
+ return {
110
+ version: 1,
111
+ state: 'bound',
112
+ identity: parseIdentity(value.identity),
113
+ binding: {
114
+ status: 'verified',
115
+ source: value.binding.source,
116
+ verifiedAt,
117
+ issueNumber,
118
+ remoteState: value.binding.remoteState,
119
+ mergedAt,
120
+ mergeCommitSha
121
+ },
122
+ provenance: { establishedBy: value.provenance.establishedBy }
123
+ };
124
+ }
125
+ function decodePrDeliveryFact(value) {
126
+ if (typeof value === 'string') {
127
+ try {
128
+ return parseFact(JSON.parse(value));
129
+ }
130
+ catch (error) {
131
+ throw error instanceof Error && error.message.startsWith('PR_DELIVERY_FACT_INVALID:') ? error : factError('value is not valid JSON');
132
+ }
133
+ }
134
+ return parseFact(value);
135
+ }
136
+ function encodePrDeliveryFact(fact) {
137
+ return JSON.stringify(parseFact(fact));
138
+ }
139
+ function readPrDeliveryFact(metadata) {
140
+ if (!Object.hasOwn(metadata, PR_DELIVERY_FACT_KEY) || metadata[PR_DELIVERY_FACT_KEY] === '')
141
+ return { status: 'missing', fact: null };
142
+ try {
143
+ return { status: 'valid', fact: decodePrDeliveryFact(metadata[PR_DELIVERY_FACT_KEY]) };
144
+ }
145
+ catch (error) {
146
+ return { status: 'invalid', fact: null, error: error instanceof Error ? error : factError(String(error)) };
147
+ }
148
+ }
149
+ function buildUnboundFact() {
150
+ return { version: 1, state: 'unbound', reason: 'initial' };
151
+ }
152
+ function buildSkippedFact(decidedAt) {
153
+ return parseFact({ version: 1, state: 'skipped', reason: 'explicit', decidedAt });
154
+ }
155
+ function provenanceForSource(source) {
156
+ return source === 'created' ? 'create-post' : source === 'reused' ? 'reuse' : source;
157
+ }
158
+ function buildBoundFact(input) {
159
+ return parseFact({
160
+ version: 1,
161
+ state: 'bound',
162
+ identity: input.identity,
163
+ binding: {
164
+ status: 'verified',
165
+ source: input.source,
166
+ verifiedAt: input.verifiedAt,
167
+ issueNumber: input.issueNumber ?? null,
168
+ remoteState: input.remoteState,
169
+ mergedAt: input.mergedAt ?? null,
170
+ mergeCommitSha: input.mergeCommitSha ?? null
171
+ },
172
+ provenance: { establishedBy: provenanceForSource(input.source) }
173
+ });
174
+ }
175
+ function factFrontmatterMutation(fact) {
176
+ return { set: { [PR_DELIVERY_FACT_KEY]: encodePrDeliveryFact(fact) } };
177
+ }
178
+ function identityFromPullRequest(pullRequest) {
179
+ return parseIdentity(pullRequest);
180
+ }
181
+ export { PR_DELIVERY_FACT_KEY, buildBoundFact, buildSkippedFact, buildUnboundFact, decodePrDeliveryFact, encodePrDeliveryFact, factFrontmatterMutation, identityFromPullRequest, readPrDeliveryFact };
182
+ //# sourceMappingURL=pr-delivery-fact.js.map
@@ -159,27 +159,46 @@ function finalizeReviewSummaryContent(content, counts) {
159
159
  content: content.slice(0, parsed.findingsStart) + line + content.slice(parsed.findingsEnd)
160
160
  };
161
161
  }
162
+ function resolveCanonicalVerdict(summary) {
163
+ if (summary.counts === null) {
164
+ return {
165
+ ok: false,
166
+ verdict: null,
167
+ code: 'REVIEW_FINDING_COUNTS_NOT_FINALIZED',
168
+ message: 'review summary finding counts are not finalized'
169
+ };
170
+ }
171
+ if (summary.verdict === 'Approved' && !equalCounts(summary.counts, { blocker: 0, major: 0, minor: 0 })) {
172
+ return {
173
+ ok: false,
174
+ verdict: null,
175
+ code: 'REVIEW_VERDICT_FINDING_MISMATCH',
176
+ message: 'Approved verdict requires zero finalized findings'
177
+ };
178
+ }
179
+ return { ok: true, verdict: summary.verdict };
180
+ }
162
181
  function parseVerdict(reviewPath) {
163
182
  if (!fs.existsSync(reviewPath)) {
164
- return { ok: false, verdict: null, message: `Review artifact not found: ${path.basename(reviewPath)}` };
183
+ return { ok: false, verdict: null, code: 'REVIEW_ARTIFACT_NOT_FOUND', message: `Review artifact not found: ${path.basename(reviewPath)}` };
184
+ }
185
+ let content;
186
+ try {
187
+ content = fs.readFileSync(reviewPath, 'utf8');
188
+ }
189
+ catch (error) {
190
+ return { ok: false, verdict: null, code: 'REVIEW_ARTIFACT_READ_FAILED', message: `Review artifact could not be read: ${String(error)}` };
165
191
  }
166
- const parsed = parseReviewSummary(fs.readFileSync(reviewPath, 'utf8'));
192
+ const parsed = parseReviewSummary(content);
167
193
  if (!parsed.ok) {
168
194
  return {
169
195
  ok: false,
170
196
  verdict: null,
197
+ code: parsed.code === 'REVIEW_SUMMARY_PLACEHOLDER_INVALID' ? parsed.code : 'REVIEW_SUMMARY_NOT_FOUND',
171
198
  message: `cannot parse review summary in ${path.basename(reviewPath)}: ${parsed.message}`
172
199
  };
173
200
  }
174
- const { verdict, counts } = parsed.summary;
175
- if (verdict !== 'Approved')
176
- return { ok: true, verdict };
177
- return {
178
- ok: true,
179
- verdict: counts && equalCounts(counts, { blocker: 0, major: 0, minor: 0 })
180
- ? 'Approved'
181
- : 'Approved-with-issues'
182
- };
201
+ return resolveCanonicalVerdict(parsed.summary);
183
202
  }
184
- export { artifactName, equalCounts, escapeRegExp, extractSection, finalizeReviewSummaryContent, maxRound, normalizeVerdict, parseReviewSummary, parseVerdict };
203
+ export { artifactName, equalCounts, escapeRegExp, extractSection, finalizeReviewSummaryContent, maxRound, normalizeVerdict, parseReviewSummary, parseVerdict, resolveCanonicalVerdict };
185
204
  //# sourceMappingURL=review-artifacts.js.map
@@ -1,10 +1,11 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import { randomUUID } from 'node:crypto';
4
- import { locateActivityLog, pairEntries } from './activity-log.js';
4
+ import { locateActivityLog, pairEntries, startedBackedRows } from './activity-log.js';
5
5
  import { parseArtifactName, validateCompletedArtifact } from './artifact-lifecycle.js';
6
- import { parseLedger, summarizeLedgerStage, validateLedgerRows } from './ledger.js';
6
+ import { LEDGER_SECTION_MISSING_CODE, LEDGER_SECTION_MISSING_MESSAGE, parseLedgerDocument, summarizeLedgerStage, validateLedgerRows } from './ledger.js';
7
7
  import { finalizeReviewSummaryContent } from './review-artifacts.js';
8
+ import { inspectDecisionDetailDuplicates } from './decision-details.js';
8
9
  import { resolveTaskRef } from './resolve-ref.js';
9
10
  import { validateLifecycleExecution } from './lifecycle-execution.js';
10
11
  import { TaskExecutionLockError, withTaskExecutionLock } from './task-execution-lock.js';
@@ -40,7 +41,7 @@ function openReviewRound(content, action, round) {
40
41
  if (!section)
41
42
  return false;
42
43
  const expected = `${action} (Round ${round})`;
43
- return pairEntries(section.entries).filter((row) => (row.step === expected && Boolean(row.started) && !row.done)).length === 1;
44
+ return startedBackedRows(pairEntries(section.entries)).filter((row) => (row.step === expected && !row.done)).length === 1;
44
45
  }
45
46
  function cleanupTemp(fileSystem, tempPath) {
46
47
  try {
@@ -103,7 +104,11 @@ function finalizeReviewSummaryUnlocked(request, options = {}) {
103
104
  }
104
105
  let rows;
105
106
  try {
106
- rows = parseLedger(taskContent);
107
+ const ledger = parseLedgerDocument(taskContent);
108
+ if (!ledger.present) {
109
+ return failed(request, 'REVIEW_LEDGER_INVALID', `${LEDGER_SECTION_MISSING_CODE}: ${LEDGER_SECTION_MISSING_MESSAGE}`, resolved.taskId);
110
+ }
111
+ rows = ledger.rows;
107
112
  }
108
113
  catch (error) {
109
114
  return failed(request, 'REVIEW_LEDGER_INVALID', String(error), resolved.taskId);
@@ -113,6 +118,10 @@ function finalizeReviewSummaryUnlocked(request, options = {}) {
113
118
  return failed(request, 'REVIEW_LEDGER_INVALID', `${ledgerError.code}: ${ledgerError.message}`, resolved.taskId);
114
119
  }
115
120
  const stageStatus = summarizeLedgerStage(rows, stage);
121
+ const detailInspection = inspectDecisionDetailDuplicates(artifactContent);
122
+ if (!detailInspection.ok) {
123
+ return failed(request, 'REVIEW_DECISION_DETAIL_INVALID', `${detailInspection.code}: ${detailInspection.message}`, resolved.taskId, stageStatus);
124
+ }
116
125
  const transformed = finalizeReviewSummaryContent(artifactContent, stageStatus.unresolvedFindingCounts);
117
126
  if (!transformed.ok) {
118
127
  return failed(request, transformed.code, transformed.message, resolved.taskId, stageStatus);
@@ -44,6 +44,14 @@ export function extractReviewBaseline(content) {
44
44
  const match = String(content).match(/^[-*]?\s*\*\*(?:审查基线提交|Review Baseline Commit)\*\*[::]\s*(.*?)\s*$/m);
45
45
  return match ? match[1].trim().replace(/`/g, "") : "";
46
46
  }
47
+ export function extractReviewTargetHead(content) {
48
+ const match = String(content).match(/^[-*]?\s*\*\*(?:审查目标提交|审查目标 HEAD|Review Target Commit|Review Target Head)\*\*[::]\s*(.*?)\s*$/m);
49
+ return match ? match[1].trim().replace(/`/g, "") : "";
50
+ }
51
+ export function extractReviewedHead(content) {
52
+ const match = String(content).match(/^[-*]?\s*\*\*(?:审查已检视提交|已检视提交|Reviewed Head Commit|Reviewed Head)\*\*[::]\s*(.*?)\s*$/m);
53
+ return match ? match[1].trim().replace(/`/g, "") : "";
54
+ }
47
55
  export function extractReviewDiffBase(content) {
48
56
  const match = String(content).match(/^[-*]?\s*\*\*(?:审查差异基线|Reviewed Diff Base)\*\*[::]\s*(.*?)\s*$/m);
49
57
  return match ? match[1].trim().replace(/`/g, "") : "";
@@ -4,7 +4,7 @@ import path from "node:path";
4
4
  import process from "node:process";
5
5
  import { execFileSync } from "node:child_process";
6
6
  import { fileURLToPath } from "node:url";
7
- import { extractReviewBaseline, extractReviewDiffBase, extractReviewDiffFingerprint, extractReviewedSnapshotTree, findAuthoritativeReviewCodeArtifact, loadPostReviewConfig, parseReviewVerdict, resolvePostReviewGlobs } from "./verification-review.js";
7
+ import { extractReviewBaseline, extractReviewTargetHead, extractReviewedHead, extractReviewDiffBase, extractReviewDiffFingerprint, extractReviewedSnapshotTree, findAuthoritativeReviewCodeArtifact, loadPostReviewConfig, parseReviewVerdict, resolvePostReviewGlobs } from "./verification-review.js";
8
8
  import { check as checkPlatformSync } from "../platform/verification-sync.js";
9
9
  import { check as checkRequiredChecks } from "../platform/verification-required.js";
10
10
  import { inspectPlatformPullRequest } from "../platform/pull-requests.js";
@@ -12,11 +12,15 @@ import { resolveMaterializedReviewedHeadRelation } from "../platform/change-requ
12
12
  import { resolveReviewedHeadRelation } from "../platform/merged-pr-equivalence.js";
13
13
  import { resolveLocalReviewedCommitRelation } from "../git/reviewed-commit-equivalence.js";
14
14
  import { parseTypedTaskFrontmatter } from "./frontmatter.js";
15
- import { parseLedger, summarizeLedgerStage, validateLedgerRows } from "./ledger.js";
15
+ import { LEDGER_SECTION_MISSING_CODE, LEDGER_SECTION_MISSING_MESSAGE, parseLedgerDocument, summarizeLedgerStage, validateLedgerRows } from "./ledger.js";
16
+ import { isValidAgentInfraVersion } from "../version.js";
16
17
  import { equalCounts, parseReviewSummary } from "./review-artifacts.js";
18
+ import { inspectDecisionDetailDuplicates } from "./decision-details.js";
17
19
  import { loadVerificationConfig } from "./verification-config.js";
18
20
  import { snapshotReview } from "../git/review-snapshot.js";
19
21
  import { OrchestrationStateError, readRun } from "./orchestration.js";
22
+ import { resolveDeliveryTarget } from "./delivery-target.js";
23
+ import { readPrDeliveryFact } from "./pr-delivery-fact.js";
20
24
  const TASK_ENUMS = {
21
25
  type: ["feature", "bugfix", "refactor", "docs", "chore"],
22
26
  workflow: ["feature-development", "bug-fix", "refactoring"],
@@ -34,7 +38,6 @@ const DEFAULT_REQUIRED_FIELDS = [
34
38
  "assigned_to"
35
39
  ];
36
40
  const DATE_TIME_PATTERN = /^\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}:\d{2}(?:[+-]\d{2}:\d{2})?$/;
37
- const AGENT_INFRA_VERSION_PATTERN = /^v\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/;
38
41
  const ACTIVITY_LOG_PATTERN = /^- (\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}:\d{2}(?:[+-]\d{2}:\d{2})?) — \*\*(.+?)\*\* by (.+?) — (.+)$/;
39
42
  // Start markers (action suffixed with ` [started]`) are excluded from the
40
43
  // "latest action" / freshness computation so a step's in-flight marker never
@@ -89,6 +92,8 @@ function runCheck(type, context, shared) {
89
92
  return checkTaskMeta(context);
90
93
  case "artifact":
91
94
  return checkArtifact(context);
95
+ case "decision-details":
96
+ return checkDecisionDetails(context);
92
97
  case "implementation-input":
93
98
  return checkImplementationInput(context);
94
99
  case "activity-log":
@@ -173,13 +178,12 @@ function validateCleanCompletionEvidence(run) {
173
178
  || !shaPattern.test(evidence.headTree)
174
179
  || !shaPattern.test(evidence.worktreeTree)
175
180
  || !shaPattern.test(evidence.lastReviewedCommit)
176
- || !Number.isInteger(evidence.prNumber)
177
- || evidence.prNumber <= 0
178
- || !shaPattern.test(evidence.prHead)) {
181
+ || !(evidence.prNumber === null || (Number.isInteger(evidence.prNumber) && evidence.prNumber > 0))
182
+ || !(evidence.prHead === null || shaPattern.test(evidence.prHead))) {
179
183
  return 'Clean completion evidence has an invalid structure';
180
184
  }
181
185
  if (evidence.head !== evidence.lastReviewedCommit
182
- || evidence.head !== evidence.prHead
186
+ || (evidence.prHead !== null && evidence.head !== evidence.prHead)
183
187
  || evidence.headTree !== evidence.worktreeTree) {
184
188
  return 'Clean completion evidence does not bind matching commit and tree identities';
185
189
  }
@@ -248,10 +252,8 @@ function checkOrchestrationEvidence({ taskDir }) {
248
252
  if (evidenceError)
249
253
  return failResult('orchestration-evidence', evidenceError);
250
254
  }
251
- else if (run.status === 'completed' && (run.receipts.length === 0
252
- || run.receipts.at(-1)?.stage !== 'commit'
253
- || run.receipts.at(-1)?.status !== 'consumed')) {
254
- return failResult('orchestration-evidence', 'Completed run requires a final consumed commit receipt');
255
+ else if (run.status === 'completed') {
256
+ return failResult('orchestration-evidence', 'Completed run requires reviewed-head-clean completion evidence');
255
257
  }
256
258
  if (run.pendingDelegation && !['prepared', 'activated', 'stage-completed'].includes(run.pendingDelegation.status)) {
257
259
  return failResult('orchestration-evidence', `Pending receipt has unsafe status '${run.pendingDelegation.status}'`);
@@ -352,18 +354,17 @@ function checkTaskMeta({ taskDir, config }) {
352
354
  const metadata = task.metadata;
353
355
  const requiredFields = config.required_fields || DEFAULT_REQUIRED_FIELDS;
354
356
  const missingFields = requiredFields.filter((field) => isBlank(metadata[field]));
355
- const blockingMissingFields = missingFields.filter((field) => field !== "agent_infra_version");
357
+ const currentTask = metadata.status === 'active';
358
+ const blockingMissingFields = missingFields.filter((field) => field !== "agent_infra_version" || currentTask);
356
359
  const warnings = [];
357
- if (missingFields.includes("agent_infra_version")) {
360
+ if (missingFields.includes("agent_infra_version") && !currentTask) {
358
361
  warnings.push("field 'agent_infra_version' missing — historical task or skipped version stamp");
359
362
  }
360
363
  if (blockingMissingFields.length > 0) {
361
364
  return failResult("task-meta", `Missing required fields: ${blockingMissingFields.join(", ")}`);
362
365
  }
363
- if (!isBlank(metadata.agent_infra_version) &&
364
- metadata.agent_infra_version !== "unknown" &&
365
- !AGENT_INFRA_VERSION_PATTERN.test(metadata.agent_infra_version)) {
366
- return failResult("task-meta", `Invalid agent_infra_version: ${metadata.agent_infra_version}`);
366
+ if (currentTask && !isValidAgentInfraVersion(metadata.agent_infra_version)) {
367
+ return failResult("task-meta", `Invalid agent_infra_version: ${String(metadata.agent_infra_version ?? 'missing')}`);
367
368
  }
368
369
  const invalidDates = ["created_at", "updated_at", "completed_at", "blocked_at", "cancelled_at"]
369
370
  .filter((field) => !isBlank(metadata[field]) && !DATE_TIME_PATTERN.test(metadata[field]));
@@ -432,19 +433,20 @@ function checkRequiredPrDelivery({ taskDir, repositoryRoot, mode }) {
432
433
  return failResult('required-pr-delivery', 'Project configuration is invalid');
433
434
  }
434
435
  const flow = projectConfig.prFlow;
435
- const status = String(task.metadata.pr_status || 'pending');
436
- const prNumber = parsePrNumber(task.metadata.pr_number);
436
+ const factRead = readPrDeliveryFact(task.metadata);
437
+ if (factRead.status === 'invalid')
438
+ return failResult('required-pr-delivery', factRead.error.message);
439
+ const fact = factRead.status === 'valid' ? factRead.fact : null;
440
+ const status = fact?.state || 'unbound';
441
+ const prNumber = fact?.state === 'bound' ? fact.identity.number : null;
437
442
  if (flow === 'disabled')
438
443
  return passResult('required-pr-delivery', 'Pull request delivery is disabled by project policy');
439
- if (flow === 'required' && (status !== 'created' || prNumber === null)) {
444
+ if (flow === 'required' && (status !== 'bound' || prNumber === null)) {
440
445
  return failResult('required-pr-delivery', 'Project policy requires a bound pull request before completion');
441
446
  }
442
- if (flow !== 'required' && status === 'pending') {
447
+ if (flow !== 'required' && status === 'unbound') {
443
448
  return failResult('required-pr-delivery', 'Pull request delivery is pending; create a PR or explicitly skip it');
444
449
  }
445
- if (status === 'created' && prNumber === null) {
446
- return failResult('required-pr-delivery', 'Task claims a created pull request without a valid pr_number');
447
- }
448
450
  if (flow === 'required') {
449
451
  if (mode === 'gate') {
450
452
  return passResult('required-pr-delivery', 'Bound pull request shape is valid; merged-state inspection is reserved for hard preflight');
@@ -456,13 +458,15 @@ function checkRequiredPrDelivery({ taskDir, repositoryRoot, mode }) {
456
458
  const pullRequest = inspected.pullRequest;
457
459
  const repository = inspected.platform.repository?.toLowerCase() || '';
458
460
  const taskBranch = String(task.metadata.branch || '').trim();
461
+ const deliveryBaseRef = String(task.metadata.delivery_base_ref || projectConfig.delivery?.baseRef || '').trim();
459
462
  if (inspected.status !== 'no-op' || !pullRequest || pullRequest.number !== prNumber) {
460
463
  return failResult('required-pr-delivery', 'Bound pull request identity could not be verified');
461
464
  }
462
465
  if (!repository
463
466
  || pullRequest.repository.toLowerCase() !== repository
464
467
  || pullRequest.base.repository.toLowerCase() !== repository
465
- || (taskBranch && pullRequest.head.ref !== taskBranch)) {
468
+ || (taskBranch && pullRequest.head.ref !== taskBranch)
469
+ || (deliveryBaseRef && pullRequest.base.ref !== deliveryBaseRef)) {
466
470
  return failResult('required-pr-delivery', 'Bound pull request identity does not match the task');
467
471
  }
468
472
  if (pullRequest.state !== 'closed' || !pullRequest.mergedAt || !pullRequest.mergeCommitSha) {
@@ -527,6 +531,21 @@ function checkArtifact({ taskDir, config, artifactFile }) {
527
531
  }
528
532
  return passResult("artifact", `${path.basename(artifactPath)} passed (${requiredSections.length} sections)`);
529
533
  }
534
+ function checkDecisionDetails({ taskDir, artifactFile }) {
535
+ const resolvedArtifact = resolveArtifactPath(taskDir, "", artifactFile);
536
+ if (!resolvedArtifact.ok)
537
+ return failResult("decision-details", resolvedArtifact.message);
538
+ const artifactPath = resolvedArtifact.path;
539
+ const stat = safeStat(artifactPath);
540
+ if (!stat?.isFile())
541
+ return failResult("decision-details", `Artifact not found: ${path.basename(artifactPath)}`);
542
+ if (stat.size === 0)
543
+ return passResult("decision-details", `${path.basename(artifactPath)} contains no decision details`);
544
+ const inspection = inspectDecisionDetailDuplicates(fs.readFileSync(artifactPath, "utf8"));
545
+ if (!inspection.ok)
546
+ return failResult("decision-details", `${path.basename(artifactPath)}: ${inspection.message}`);
547
+ return passResult("decision-details", `${path.basename(artifactPath)} has no duplicate decision detail ids`);
548
+ }
530
549
  function checkReviewSummary({ taskDir, config, artifactFile }) {
531
550
  const stage = String(config.stage || "");
532
551
  if (!["analysis", "plan", "code"].includes(stage)) {
@@ -547,7 +566,10 @@ function checkReviewSummary({ taskDir, config, artifactFile }) {
547
566
  return failResult("review-summary", task.message);
548
567
  let rows;
549
568
  try {
550
- rows = parseLedger(task.content);
569
+ const ledger = parseLedgerDocument(task.content);
570
+ if (!ledger.present)
571
+ return failResult("review-summary", `${LEDGER_SECTION_MISSING_CODE}: ${LEDGER_SECTION_MISSING_MESSAGE}`);
572
+ rows = ledger.rows;
551
573
  }
552
574
  catch (error) {
553
575
  return failResult("review-summary", `Invalid disagreement ledger: ${String(error)}`);
@@ -817,13 +839,12 @@ function checkReviewLedger({ taskDir, config }) {
817
839
  if (!task.ok) {
818
840
  return failResult("review-ledger", task.message);
819
841
  }
820
- const section = getSectionContent(task.content, LEDGER_SECTION_NAMES);
821
- if (!section.trim()) {
822
- return passResult("review-ledger", "No disagreement ledger section; treated as no open disagreements");
823
- }
824
842
  let rows;
825
843
  try {
826
- rows = parseLedger(task.content).map((row) => [
844
+ const ledger = parseLedgerDocument(task.content);
845
+ if (!ledger.present)
846
+ return failResult("review-ledger", `${LEDGER_SECTION_MISSING_CODE}: ${LEDGER_SECTION_MISSING_MESSAGE}`);
847
+ rows = ledger.rows.map((row) => [
827
848
  row.id, row.stage, row.round, row.severity, row.status, row.evidence
828
849
  ]);
829
850
  }
@@ -947,9 +968,13 @@ function checkPostReviewCommit({ taskDir, config }) {
947
968
  const task = loadTask(taskDir);
948
969
  if (!task.ok)
949
970
  return failResult("post-review-commit", task.message);
971
+ const factRead = readPrDeliveryFact(task.metadata);
972
+ if (factRead.status === 'invalid')
973
+ return failResult("post-review-commit", factRead.error.message);
974
+ const fact = factRead.status === 'valid' ? factRead.fact : null;
950
975
  const lastReviewedCommit = task.ok ? (task.metadata.last_reviewed_commit || "").trim() : "";
951
976
  const globs = resolvePostReviewGlobs(config, loadPostReviewConfig(repoRoot));
952
- const hasPullRequest = Number(task.metadata.pr_number) > 0;
977
+ const hasPullRequest = fact?.state === 'bound';
953
978
  let inspected = null;
954
979
  if (hasPullRequest) {
955
980
  inspected = inspectPlatformPullRequest(task.metadata.id, { cwd: gitRoot });
@@ -1068,7 +1093,11 @@ function formatPostReviewExemptions(exemptions) {
1068
1093
  }
1069
1094
  function resolvePostReviewExemption(content) {
1070
1095
  try {
1071
- const rows = parseLedger(content);
1096
+ const ledger = parseLedgerDocument(content);
1097
+ if (!ledger.present) {
1098
+ return { ok: false, code: "POST_REVIEW_EXEMPTION_INVALID", message: `${LEDGER_SECTION_MISSING_CODE}: ${LEDGER_SECTION_MISSING_MESSAGE}` };
1099
+ }
1100
+ const rows = ledger.rows;
1072
1101
  const invalid = validateLedgerRows(rows);
1073
1102
  if (invalid) {
1074
1103
  return {
@@ -1101,28 +1130,46 @@ function checkReviewFact({ taskDir, artifactFile }) {
1101
1130
  }
1102
1131
  const content = fs.readFileSync(resolvedArtifact.path, "utf8");
1103
1132
  const verdict = parseReviewVerdict(content);
1104
- const reviewBaseline = extractReviewBaseline(content);
1133
+ const reviewTargetHead = extractReviewTargetHead(content);
1134
+ const reviewReviewedHead = extractReviewedHead(content);
1105
1135
  const reviewDiffBase = extractReviewDiffBase(content);
1106
1136
  const reviewedFingerprint = extractReviewDiffFingerprint(content);
1107
1137
  const reviewedTree = extractReviewedSnapshotTree(content);
1108
1138
  if (!["通过", "需要修改", "拒绝", "Approved", "Changes Requested", "Rejected"].includes(verdict)) {
1109
1139
  return failResult("review-fact", `Unsupported review verdict '${verdict}'`);
1110
1140
  }
1141
+ const deliveryRemote = String(task.metadata.delivery_remote || "").trim();
1142
+ const deliveryBaseRef = String(task.metadata.delivery_base_ref || "").trim();
1143
+ if (!deliveryRemote || !deliveryBaseRef) {
1144
+ return failResult("review-fact", "Task delivery target is not bound; re-run task branch preparation before review-code");
1145
+ }
1146
+ if (!reviewTargetHead || !reviewReviewedHead || !reviewDiffBase) {
1147
+ return failResult("review-fact", "Review fact must record target head, reviewed head, and diff base");
1148
+ }
1111
1149
  let gitRoot;
1112
1150
  let head;
1113
1151
  let baseline;
1152
+ let targetHead;
1114
1153
  let diffBase;
1115
1154
  try {
1116
1155
  gitRoot = execFileSync("git", ["-C", taskDir, "rev-parse", "--show-toplevel"], { encoding: "utf8" }).trim();
1117
1156
  head = execFileSync("git", ["-C", gitRoot, "rev-parse", "HEAD"], { encoding: "utf8" }).trim();
1118
- baseline = execFileSync("git", ["-C", gitRoot, "rev-parse", `${reviewBaseline}^{commit}`], { encoding: "utf8" }).trim();
1119
- diffBase = execFileSync("git", ["-C", gitRoot, "rev-parse", `${reviewDiffBase || reviewBaseline}^{commit}`], { encoding: "utf8" }).trim();
1157
+ const target = resolveDeliveryTarget(gitRoot, { remote: deliveryRemote, baseRef: deliveryBaseRef });
1158
+ if (!target.ok)
1159
+ throw new Error(target.message);
1160
+ const reviewedRef = reviewReviewedHead;
1161
+ baseline = execFileSync("git", ["-C", gitRoot, "rev-parse", `${reviewedRef}^{commit}`], { encoding: "utf8" }).trim();
1162
+ targetHead = execFileSync("git", ["-C", gitRoot, "rev-parse", `${reviewTargetHead}^{commit}`], { encoding: "utf8" }).trim();
1163
+ const computed = execFileSync("git", ["-C", gitRoot, "merge-base", baseline, targetHead], { encoding: "utf8" }).trim();
1164
+ diffBase = execFileSync("git", ["-C", gitRoot, "rev-parse", `${reviewDiffBase}^{commit}`], { encoding: "utf8" }).trim();
1165
+ if (diffBase !== computed)
1166
+ throw new Error('saved review diff base does not match merge-base(reviewed head, target head)');
1120
1167
  }
1121
1168
  catch {
1122
- return blockedResult("review-fact", `Unable to resolve review baseline '${reviewBaseline}' in the task repository; re-run review-code`);
1169
+ return blockedResult("review-fact", `Unable to resolve saved review commits in the task repository; re-run review-code`);
1123
1170
  }
1124
1171
  if (baseline !== head) {
1125
- return failResult("review-fact", `Review baseline ${baseline.slice(0, 8)} does not match current HEAD ${head.slice(0, 8)}; re-run review-code`);
1172
+ return failResult("review-fact", `Reviewed head ${baseline.slice(0, 8)} does not match current HEAD ${head.slice(0, 8)}; re-run review-code`);
1126
1173
  }
1127
1174
  let actualSnapshot;
1128
1175
  try {
@@ -1147,7 +1194,7 @@ function checkReviewFact({ taskDir, artifactFile }) {
1147
1194
  const lastReviewedCommit = String(task.metadata.last_reviewed_commit || "").trim();
1148
1195
  const cleanSnapshot = actualSnapshot.tree === execFileSync("git", ["-C", gitRoot, "rev-parse", `${baseline}^{tree}`], { encoding: "utf8" }).trim();
1149
1196
  if (cleanSnapshot && lastReviewedCommit !== baseline) {
1150
- return failResult("review-fact", `Approved clean review must set task last_reviewed_commit to baseline ${baseline.slice(0, 8)}`);
1197
+ return failResult("review-fact", `Approved clean review must set task last_reviewed_commit to reviewed head ${baseline.slice(0, 8)}`);
1151
1198
  }
1152
1199
  if (!cleanSnapshot && lastReviewedCommit) {
1153
1200
  return failResult("review-fact", "Approved review with uncommitted changes must remain unanchored until commit");
@@ -1,3 +1,3 @@
1
- export { extractReviewBaseline, extractReviewDiffBase, extractReviewDiffFingerprint, extractReviewedSnapshotTree, findAuthoritativeReviewCodeArtifact, loadPostReviewConfig, parseReviewVerdict, resolvePostReviewGlobs } from './review-fingerprint.js';
2
- export { parseLedger, summarizeLedgerStage, validateLedgerRows } from './ledger.js';
1
+ export { extractReviewBaseline, extractReviewTargetHead, extractReviewedHead, extractReviewDiffBase, extractReviewDiffFingerprint, extractReviewedSnapshotTree, findAuthoritativeReviewCodeArtifact, loadPostReviewConfig, parseReviewVerdict, resolvePostReviewGlobs } from './review-fingerprint.js';
2
+ export { parseLedgerDocument, summarizeLedgerStage, validateLedgerRows } from './ledger.js';
3
3
  //# sourceMappingURL=verification-review.js.map
@@ -26,12 +26,6 @@ function captureWorktreeTree(repoRoot, forcedPath) {
26
26
  fs.rmSync(tempDir, { recursive: true, force: true });
27
27
  }
28
28
  }
29
- function captureLegacyWorkspaceSnapshot(repoRoot, taskId) {
30
- const workspaceRelative = taskId
31
- ? `.agents/workspace/active/${taskId}`
32
- : '.agents/workspace/active';
33
- return captureWorktreeTree(repoRoot, workspaceRelative);
34
- }
35
29
  function taskFileFingerprints(stateRoot, taskId) {
36
30
  const activeRoot = path.join(stateRoot, '.agents', 'workspace', 'active');
37
31
  const roots = taskId ? [path.join(activeRoot, taskId)] : [activeRoot];
@@ -46,6 +40,8 @@ function taskFileFingerprints(stateRoot, taskId) {
46
40
  }
47
41
  if (!stat.isFile() && !stat.isSymbolicLink())
48
42
  return;
43
+ if (relative.startsWith('.agents/workspace/active/') && path.posix.basename(relative) === 'orchestration.json')
44
+ return;
49
45
  const content = stat.isSymbolicLink()
50
46
  ? Buffer.from(fs.readlinkSync(absolute))
51
47
  : fs.readFileSync(absolute);
@@ -81,13 +77,11 @@ function decodeWorkspaceFingerprint(value) {
81
77
  throw new Error(`ORCHESTRATION_SNAPSHOT_FAILED: ${error instanceof Error ? error.message : String(error)}`);
82
78
  }
83
79
  }
84
- function captureWorkspaceSnapshot(contextOrRoot, taskId) {
85
- if (typeof contextOrRoot === 'string')
86
- return captureLegacyWorkspaceSnapshot(contextOrRoot, taskId ?? null);
80
+ function captureWorkspaceSnapshot(context) {
87
81
  return encodeWorkspaceFingerprint({
88
82
  version: 2,
89
- gitTree: captureWorktreeTree(contextOrRoot.gitRoot, null),
90
- taskFiles: taskFileFingerprints(contextOrRoot.stateRoot, contextOrRoot.taskId)
83
+ gitTree: captureWorktreeTree(context.gitRoot, null),
84
+ taskFiles: taskFileFingerprints(context.stateRoot, context.taskId)
91
85
  });
92
86
  }
93
87
  function captureRepositorySnapshot(repoRoot) {