@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
@@ -18,6 +18,21 @@ type ReviewSummaryErrorCode =
18
18
  | 'REVIEW_SUMMARY_NOT_FOUND'
19
19
  | 'REVIEW_SUMMARY_PLACEHOLDER_INVALID'
20
20
  | 'REVIEW_SUMMARY_COUNT_MISMATCH';
21
+ type VerdictSemanticErrorCode =
22
+ | 'REVIEW_VERDICT_FINDING_MISMATCH'
23
+ | 'REVIEW_FINDING_COUNTS_NOT_FINALIZED';
24
+ type CanonicalVerdictResult =
25
+ | { ok: true; verdict: ReviewVerdict }
26
+ | { ok: false; verdict: null; code: VerdictSemanticErrorCode; message: string };
27
+ type PathVerdictErrorCode =
28
+ | 'REVIEW_ARTIFACT_NOT_FOUND'
29
+ | 'REVIEW_ARTIFACT_READ_FAILED'
30
+ | 'REVIEW_SUMMARY_NOT_FOUND'
31
+ | 'REVIEW_SUMMARY_PLACEHOLDER_INVALID'
32
+ | VerdictSemanticErrorCode;
33
+ type PathVerdictResult =
34
+ | { ok: true; verdict: ReviewVerdict }
35
+ | { ok: false; verdict: null; code: PathVerdictErrorCode; message: string };
21
36
  type ReviewSummaryParseResult =
22
37
  | { ok: true; summary: ReviewSummary; findingsStart: number; findingsEnd: number }
23
38
  | { ok: false; code: ReviewSummaryErrorCode; message: string };
@@ -203,28 +218,46 @@ function finalizeReviewSummaryContent(
203
218
  };
204
219
  }
205
220
 
206
- function parseVerdict(reviewPath: string):
207
- | { ok: true; verdict: 'Approved' | 'Approved-with-issues' | 'Changes Requested' | 'Rejected' }
208
- | { ok: false; verdict: null; message: string } {
221
+ function resolveCanonicalVerdict(summary: ReviewSummary): CanonicalVerdictResult {
222
+ if (summary.counts === null) {
223
+ return {
224
+ ok: false,
225
+ verdict: null,
226
+ code: 'REVIEW_FINDING_COUNTS_NOT_FINALIZED',
227
+ message: 'review summary finding counts are not finalized'
228
+ };
229
+ }
230
+ if (summary.verdict === 'Approved' && !equalCounts(summary.counts, { blocker: 0, major: 0, minor: 0 })) {
231
+ return {
232
+ ok: false,
233
+ verdict: null,
234
+ code: 'REVIEW_VERDICT_FINDING_MISMATCH',
235
+ message: 'Approved verdict requires zero finalized findings'
236
+ };
237
+ }
238
+ return { ok: true, verdict: summary.verdict };
239
+ }
240
+
241
+ function parseVerdict(reviewPath: string): PathVerdictResult {
209
242
  if (!fs.existsSync(reviewPath)) {
210
- return { ok: false, verdict: null, message: `Review artifact not found: ${path.basename(reviewPath)}` };
243
+ return { ok: false, verdict: null, code: 'REVIEW_ARTIFACT_NOT_FOUND', message: `Review artifact not found: ${path.basename(reviewPath)}` };
244
+ }
245
+ let content: string;
246
+ try {
247
+ content = fs.readFileSync(reviewPath, 'utf8');
248
+ } catch (error) {
249
+ return { ok: false, verdict: null, code: 'REVIEW_ARTIFACT_READ_FAILED', message: `Review artifact could not be read: ${String(error)}` };
211
250
  }
212
- const parsed = parseReviewSummary(fs.readFileSync(reviewPath, 'utf8'));
251
+ const parsed = parseReviewSummary(content);
213
252
  if (!parsed.ok) {
214
253
  return {
215
254
  ok: false,
216
255
  verdict: null,
256
+ code: parsed.code === 'REVIEW_SUMMARY_PLACEHOLDER_INVALID' ? parsed.code : 'REVIEW_SUMMARY_NOT_FOUND',
217
257
  message: `cannot parse review summary in ${path.basename(reviewPath)}: ${parsed.message}`
218
258
  };
219
259
  }
220
- const { verdict, counts } = parsed.summary;
221
- if (verdict !== 'Approved') return { ok: true, verdict };
222
- return {
223
- ok: true,
224
- verdict: counts && equalCounts(counts, { blocker: 0, major: 0, minor: 0 })
225
- ? 'Approved'
226
- : 'Approved-with-issues'
227
- };
260
+ return resolveCanonicalVerdict(parsed.summary);
228
261
  }
229
262
 
230
263
  export {
@@ -236,13 +269,18 @@ export {
236
269
  maxRound,
237
270
  normalizeVerdict,
238
271
  parseReviewSummary,
239
- parseVerdict
272
+ parseVerdict,
273
+ resolveCanonicalVerdict
240
274
  };
241
275
  export type {
276
+ CanonicalVerdictResult,
277
+ PathVerdictErrorCode,
278
+ PathVerdictResult,
242
279
  ReviewFindingCounts,
243
280
  ReviewSummary,
244
281
  ReviewSummaryErrorCode,
245
282
  ReviewSummaryFinalizeResult,
246
283
  ReviewSummaryParseResult,
247
- ReviewVerdict
284
+ ReviewVerdict,
285
+ VerdictSemanticErrorCode
248
286
  };
@@ -2,11 +2,12 @@ import fs from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import { randomUUID } from 'node:crypto';
4
4
 
5
- import { locateActivityLog, pairEntries } from './activity-log.ts';
5
+ import { locateActivityLog, pairEntries, startedBackedRows } from './activity-log.ts';
6
6
  import { parseArtifactName, validateCompletedArtifact } from './artifact-lifecycle.ts';
7
- import { parseLedger, summarizeLedgerStage, validateLedgerRows } from './ledger.ts';
7
+ import { LEDGER_SECTION_MISSING_CODE, LEDGER_SECTION_MISSING_MESSAGE, parseLedgerDocument, summarizeLedgerStage, validateLedgerRows } from './ledger.ts';
8
8
  import type { LedgerStageStatus, ReviewStage } from './ledger.ts';
9
9
  import { finalizeReviewSummaryContent } from './review-artifacts.ts';
10
+ import { inspectDecisionDetailDuplicates } from './decision-details.ts';
10
11
  import { resolveTaskRef } from './resolve-ref.ts';
11
12
  import { validateLifecycleExecution } from './lifecycle-execution.ts';
12
13
  import { TaskExecutionLockError, withTaskExecutionLock } from './task-execution-lock.ts';
@@ -25,6 +26,7 @@ type ReviewFinalizationErrorCode =
25
26
  | 'REVIEW_SUMMARY_NOT_FOUND'
26
27
  | 'REVIEW_SUMMARY_PLACEHOLDER_INVALID'
27
28
  | 'REVIEW_SUMMARY_COUNT_MISMATCH'
29
+ | 'REVIEW_DECISION_DETAIL_INVALID'
28
30
  | 'REVIEW_ARTIFACT_CONFLICT'
29
31
  | 'REVIEW_PROVENANCE_INVALID'
30
32
  | 'REVIEW_TEMP_WRITE_FAILED'
@@ -106,8 +108,8 @@ function openReviewRound(content: string, action: string, round: number): boolea
106
108
  const section = locateActivityLog(content);
107
109
  if (!section) return false;
108
110
  const expected = `${action} (Round ${round})`;
109
- return pairEntries(section.entries).filter((row) => (
110
- row.step === expected && Boolean(row.started) && !row.done
111
+ return startedBackedRows(pairEntries(section.entries)).filter((row) => (
112
+ row.step === expected && !row.done
111
113
  )).length === 1;
112
114
  }
113
115
 
@@ -194,7 +196,11 @@ function finalizeReviewSummaryUnlocked(
194
196
 
195
197
  let rows;
196
198
  try {
197
- rows = parseLedger(taskContent);
199
+ const ledger = parseLedgerDocument(taskContent);
200
+ if (!ledger.present) {
201
+ return failed(request, 'REVIEW_LEDGER_INVALID', `${LEDGER_SECTION_MISSING_CODE}: ${LEDGER_SECTION_MISSING_MESSAGE}`, resolved.taskId);
202
+ }
203
+ rows = ledger.rows;
198
204
  } catch (error) {
199
205
  return failed(request, 'REVIEW_LEDGER_INVALID', String(error), resolved.taskId);
200
206
  }
@@ -208,6 +214,16 @@ function finalizeReviewSummaryUnlocked(
208
214
  );
209
215
  }
210
216
  const stageStatus = summarizeLedgerStage(rows, stage);
217
+ const detailInspection = inspectDecisionDetailDuplicates(artifactContent);
218
+ if (!detailInspection.ok) {
219
+ return failed(
220
+ request,
221
+ 'REVIEW_DECISION_DETAIL_INVALID',
222
+ `${detailInspection.code}: ${detailInspection.message}`,
223
+ resolved.taskId,
224
+ stageStatus
225
+ );
226
+ }
211
227
  const transformed = finalizeReviewSummaryContent(
212
228
  artifactContent,
213
229
  stageStatus.unresolvedFindingCounts
@@ -63,6 +63,16 @@ export function extractReviewBaseline(content: string): string {
63
63
  return match ? match[1]!.trim().replace(/`/g, "") : "";
64
64
  }
65
65
 
66
+ export function extractReviewTargetHead(content: string): string {
67
+ const match = String(content).match(/^[-*]?\s*\*\*(?:审查目标提交|审查目标 HEAD|Review Target Commit|Review Target Head)\*\*[::]\s*(.*?)\s*$/m);
68
+ return match ? match[1]!.trim().replace(/`/g, "") : "";
69
+ }
70
+
71
+ export function extractReviewedHead(content: string): string {
72
+ const match = String(content).match(/^[-*]?\s*\*\*(?:审查已检视提交|已检视提交|Reviewed Head Commit|Reviewed Head)\*\*[::]\s*(.*?)\s*$/m);
73
+ return match ? match[1]!.trim().replace(/`/g, "") : "";
74
+ }
75
+
66
76
  export function extractReviewDiffBase(content: string): string {
67
77
  const match = String(content).match(/^[-*]?\s*\*\*(?:审查差异基线|Reviewed Diff Base)\*\*[::]\s*(.*?)\s*$/m);
68
78
  return match ? match[1]!.trim().replace(/`/g, "") : "";
@@ -7,6 +7,8 @@ import { fileURLToPath } from "node:url";
7
7
 
8
8
  import {
9
9
  extractReviewBaseline,
10
+ extractReviewTargetHead,
11
+ extractReviewedHead,
10
12
  extractReviewDiffBase,
11
13
  extractReviewDiffFingerprint,
12
14
  extractReviewedSnapshotTree,
@@ -22,12 +24,16 @@ import { resolveMaterializedReviewedHeadRelation } from "../platform/change-requ
22
24
  import { resolveReviewedHeadRelation } from "../platform/merged-pr-equivalence.ts";
23
25
  import { resolveLocalReviewedCommitRelation } from "../git/reviewed-commit-equivalence.ts";
24
26
  import { parseTypedTaskFrontmatter } from "./frontmatter.ts";
25
- import { parseLedger, summarizeLedgerStage, validateLedgerRows } from "./ledger.ts";
27
+ import { LEDGER_SECTION_MISSING_CODE, LEDGER_SECTION_MISSING_MESSAGE, parseLedgerDocument, summarizeLedgerStage, validateLedgerRows } from "./ledger.ts";
26
28
  import type { LedgerRow } from "./ledger.ts";
29
+ import { isValidAgentInfraVersion } from "../version.ts";
27
30
  import { equalCounts, parseReviewSummary } from "./review-artifacts.ts";
31
+ import { inspectDecisionDetailDuplicates } from "./decision-details.ts";
28
32
  import { loadVerificationConfig } from "./verification-config.ts";
29
33
  import { snapshotReview } from "../git/review-snapshot.ts";
30
34
  import { OrchestrationStateError, readRun } from "./orchestration.ts";
35
+ import { resolveDeliveryTarget } from "./delivery-target.ts";
36
+ import { readPrDeliveryFact } from "./pr-delivery-fact.ts";
31
37
 
32
38
  const TASK_ENUMS = {
33
39
  type: ["feature", "bugfix", "refactor", "docs", "chore"],
@@ -48,7 +54,6 @@ const DEFAULT_REQUIRED_FIELDS = [
48
54
  ];
49
55
 
50
56
  const DATE_TIME_PATTERN = /^\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}:\d{2}(?:[+-]\d{2}:\d{2})?$/;
51
- const AGENT_INFRA_VERSION_PATTERN = /^v\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/;
52
57
  const ACTIVITY_LOG_PATTERN = /^- (\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}:\d{2}(?:[+-]\d{2}:\d{2})?) — \*\*(.+?)\*\* by (.+?) — (.+)$/;
53
58
  // Start markers (action suffixed with ` [started]`) are excluded from the
54
59
  // "latest action" / freshness computation so a step's in-flight marker never
@@ -108,6 +113,8 @@ function runCheck(type: any, context: any, shared: any): any {
108
113
  return checkTaskMeta(context);
109
114
  case "artifact":
110
115
  return checkArtifact(context);
116
+ case "decision-details":
117
+ return checkDecisionDetails(context);
111
118
  case "implementation-input":
112
119
  return checkImplementationInput(context);
113
120
  case "activity-log":
@@ -192,15 +199,14 @@ function validateCleanCompletionEvidence(run: any): string | null {
192
199
  || !shaPattern.test(evidence.headTree)
193
200
  || !shaPattern.test(evidence.worktreeTree)
194
201
  || !shaPattern.test(evidence.lastReviewedCommit)
195
- || !Number.isInteger(evidence.prNumber)
196
- || evidence.prNumber <= 0
197
- || !shaPattern.test(evidence.prHead)
202
+ || !(evidence.prNumber === null || (Number.isInteger(evidence.prNumber) && evidence.prNumber > 0))
203
+ || !(evidence.prHead === null || shaPattern.test(evidence.prHead))
198
204
  ) {
199
205
  return 'Clean completion evidence has an invalid structure';
200
206
  }
201
207
  if (
202
208
  evidence.head !== evidence.lastReviewedCommit
203
- || evidence.head !== evidence.prHead
209
+ || (evidence.prHead !== null && evidence.head !== evidence.prHead)
204
210
  || evidence.headTree !== evidence.worktreeTree
205
211
  ) {
206
212
  return 'Clean completion evidence does not bind matching commit and tree identities';
@@ -273,12 +279,8 @@ function checkOrchestrationEvidence({ taskDir }: any): any {
273
279
  if (run.completionEvidence != null) {
274
280
  const evidenceError = validateCleanCompletionEvidence(run);
275
281
  if (evidenceError) return failResult('orchestration-evidence', evidenceError);
276
- } else if (run.status === 'completed' && (
277
- run.receipts.length === 0
278
- || run.receipts.at(-1)?.stage !== 'commit'
279
- || run.receipts.at(-1)?.status !== 'consumed'
280
- )) {
281
- return failResult('orchestration-evidence', 'Completed run requires a final consumed commit receipt');
282
+ } else if (run.status === 'completed') {
283
+ return failResult('orchestration-evidence', 'Completed run requires reviewed-head-clean completion evidence');
282
284
  }
283
285
  if (run.pendingDelegation && !['prepared', 'activated', 'stage-completed'].includes(run.pendingDelegation.status)) {
284
286
  return failResult('orchestration-evidence', `Pending receipt has unsafe status '${run.pendingDelegation.status}'`);
@@ -392,23 +394,20 @@ function checkTaskMeta({ taskDir, config }: any): any {
392
394
  const metadata = task.metadata;
393
395
  const requiredFields = config.required_fields || DEFAULT_REQUIRED_FIELDS;
394
396
  const missingFields = requiredFields.filter((field: any) => isBlank(metadata[field]));
395
- const blockingMissingFields = missingFields.filter((field: any) => field !== "agent_infra_version");
397
+ const currentTask = metadata.status === 'active';
398
+ const blockingMissingFields = missingFields.filter((field: any) => field !== "agent_infra_version" || currentTask);
396
399
  const warnings = [];
397
- if (missingFields.includes("agent_infra_version")) {
400
+ if (missingFields.includes("agent_infra_version") && !currentTask) {
398
401
  warnings.push("field 'agent_infra_version' missing — historical task or skipped version stamp");
399
402
  }
400
403
  if (blockingMissingFields.length > 0) {
401
404
  return failResult("task-meta", `Missing required fields: ${blockingMissingFields.join(", ")}`);
402
405
  }
403
406
 
404
- if (
405
- !isBlank(metadata.agent_infra_version) &&
406
- metadata.agent_infra_version !== "unknown" &&
407
- !AGENT_INFRA_VERSION_PATTERN.test(metadata.agent_infra_version)
408
- ) {
407
+ if (currentTask && !isValidAgentInfraVersion(metadata.agent_infra_version)) {
409
408
  return failResult(
410
409
  "task-meta",
411
- `Invalid agent_infra_version: ${metadata.agent_infra_version}`
410
+ `Invalid agent_infra_version: ${String(metadata.agent_infra_version ?? 'missing')}`
412
411
  );
413
412
  }
414
413
 
@@ -500,18 +499,18 @@ function checkRequiredPrDelivery({ taskDir, repositoryRoot, mode }: any): any {
500
499
  return failResult('required-pr-delivery', 'Project configuration is invalid');
501
500
  }
502
501
  const flow = projectConfig.prFlow;
503
- const status = String(task.metadata.pr_status || 'pending');
504
- const prNumber = parsePrNumber(task.metadata.pr_number);
502
+ const factRead = readPrDeliveryFact(task.metadata);
503
+ if (factRead.status === 'invalid') return failResult('required-pr-delivery', factRead.error.message);
504
+ const fact = factRead.status === 'valid' ? factRead.fact : null;
505
+ const status = fact?.state || 'unbound';
506
+ const prNumber = fact?.state === 'bound' ? fact.identity.number : null;
505
507
  if (flow === 'disabled') return passResult('required-pr-delivery', 'Pull request delivery is disabled by project policy');
506
- if (flow === 'required' && (status !== 'created' || prNumber === null)) {
508
+ if (flow === 'required' && (status !== 'bound' || prNumber === null)) {
507
509
  return failResult('required-pr-delivery', 'Project policy requires a bound pull request before completion');
508
510
  }
509
- if (flow !== 'required' && status === 'pending') {
511
+ if (flow !== 'required' && status === 'unbound') {
510
512
  return failResult('required-pr-delivery', 'Pull request delivery is pending; create a PR or explicitly skip it');
511
513
  }
512
- if (status === 'created' && prNumber === null) {
513
- return failResult('required-pr-delivery', 'Task claims a created pull request without a valid pr_number');
514
- }
515
514
  if (flow === 'required') {
516
515
  if (mode === 'gate') {
517
516
  return passResult('required-pr-delivery', 'Bound pull request shape is valid; merged-state inspection is reserved for hard preflight');
@@ -527,6 +526,7 @@ function checkRequiredPrDelivery({ taskDir, repositoryRoot, mode }: any): any {
527
526
  const pullRequest = inspected.pullRequest;
528
527
  const repository = inspected.platform.repository?.toLowerCase() || '';
529
528
  const taskBranch = String(task.metadata.branch || '').trim();
529
+ const deliveryBaseRef = String(task.metadata.delivery_base_ref || projectConfig.delivery?.baseRef || '').trim();
530
530
  if (inspected.status !== 'no-op' || !pullRequest || pullRequest.number !== prNumber) {
531
531
  return failResult('required-pr-delivery', 'Bound pull request identity could not be verified');
532
532
  }
@@ -535,6 +535,7 @@ function checkRequiredPrDelivery({ taskDir, repositoryRoot, mode }: any): any {
535
535
  || pullRequest.repository.toLowerCase() !== repository
536
536
  || pullRequest.base.repository.toLowerCase() !== repository
537
537
  || (taskBranch && pullRequest.head.ref !== taskBranch)
538
+ || (deliveryBaseRef && pullRequest.base.ref !== deliveryBaseRef)
538
539
  ) {
539
540
  return failResult('required-pr-delivery', 'Bound pull request identity does not match the task');
540
541
  }
@@ -622,6 +623,18 @@ function checkArtifact({ taskDir, config, artifactFile }: any): any {
622
623
  );
623
624
  }
624
625
 
626
+ function checkDecisionDetails({ taskDir, artifactFile }: any): any {
627
+ const resolvedArtifact = resolveArtifactPath(taskDir, "", artifactFile);
628
+ if (!resolvedArtifact.ok) return failResult("decision-details", resolvedArtifact.message);
629
+ const artifactPath = resolvedArtifact.path;
630
+ const stat = safeStat(artifactPath);
631
+ if (!stat?.isFile()) return failResult("decision-details", `Artifact not found: ${path.basename(artifactPath)}`);
632
+ if (stat.size === 0) return passResult("decision-details", `${path.basename(artifactPath)} contains no decision details`);
633
+ const inspection = inspectDecisionDetailDuplicates(fs.readFileSync(artifactPath, "utf8"));
634
+ if (!inspection.ok) return failResult("decision-details", `${path.basename(artifactPath)}: ${inspection.message}`);
635
+ return passResult("decision-details", `${path.basename(artifactPath)} has no duplicate decision detail ids`);
636
+ }
637
+
625
638
  function checkReviewSummary({ taskDir, config, artifactFile }: any): any {
626
639
  const stage = String(config.stage || "");
627
640
  if (!["analysis", "plan", "code"].includes(stage)) {
@@ -640,7 +653,9 @@ function checkReviewSummary({ taskDir, config, artifactFile }: any): any {
640
653
  if (!task.ok) return failResult("review-summary", task.message);
641
654
  let rows;
642
655
  try {
643
- rows = parseLedger(task.content);
656
+ const ledger = parseLedgerDocument(task.content);
657
+ if (!ledger.present) return failResult("review-summary", `${LEDGER_SECTION_MISSING_CODE}: ${LEDGER_SECTION_MISSING_MESSAGE}`);
658
+ rows = ledger.rows;
644
659
  } catch (error) {
645
660
  return failResult("review-summary", `Invalid disagreement ledger: ${String(error)}`);
646
661
  }
@@ -946,14 +961,11 @@ function checkReviewLedger({ taskDir, config }: any): any {
946
961
  return failResult("review-ledger", task.message);
947
962
  }
948
963
 
949
- const section = getSectionContent(task.content, LEDGER_SECTION_NAMES);
950
- if (!section.trim()) {
951
- return passResult("review-ledger", "No disagreement ledger section; treated as no open disagreements");
952
- }
953
-
954
964
  let rows;
955
965
  try {
956
- rows = parseLedger(task.content).map((row) => [
966
+ const ledger = parseLedgerDocument(task.content);
967
+ if (!ledger.present) return failResult("review-ledger", `${LEDGER_SECTION_MISSING_CODE}: ${LEDGER_SECTION_MISSING_MESSAGE}`);
968
+ rows = ledger.rows.map((row) => [
957
969
  row.id, row.stage, row.round, row.severity, row.status, row.evidence
958
970
  ]);
959
971
  } catch (error) {
@@ -1099,9 +1111,12 @@ function checkPostReviewCommit({ taskDir, config }: any): any {
1099
1111
 
1100
1112
  const task = loadTask(taskDir);
1101
1113
  if (!task.ok) return failResult("post-review-commit", task.message);
1114
+ const factRead = readPrDeliveryFact(task.metadata);
1115
+ if (factRead.status === 'invalid') return failResult("post-review-commit", factRead.error.message);
1116
+ const fact = factRead.status === 'valid' ? factRead.fact : null;
1102
1117
  const lastReviewedCommit = task.ok ? (task.metadata.last_reviewed_commit || "").trim() : "";
1103
1118
  const globs = resolvePostReviewGlobs(config, loadPostReviewConfig(repoRoot));
1104
- const hasPullRequest = Number(task.metadata.pr_number) > 0;
1119
+ const hasPullRequest = fact?.state === 'bound';
1105
1120
  let inspected: ReturnType<typeof inspectPlatformPullRequest> | null = null;
1106
1121
  if (hasPullRequest) {
1107
1122
  inspected = inspectPlatformPullRequest(task.metadata.id, { cwd: gitRoot });
@@ -1248,7 +1263,11 @@ function resolvePostReviewExemption(content: string):
1248
1263
  | { ok: true; exemptions: readonly LedgerRow[] }
1249
1264
  | { ok: false; code: "POST_REVIEW_EXEMPTION_INVALID"; message: string } {
1250
1265
  try {
1251
- const rows = parseLedger(content);
1266
+ const ledger = parseLedgerDocument(content);
1267
+ if (!ledger.present) {
1268
+ return { ok: false, code: "POST_REVIEW_EXEMPTION_INVALID", message: `${LEDGER_SECTION_MISSING_CODE}: ${LEDGER_SECTION_MISSING_MESSAGE}` };
1269
+ }
1270
+ const rows = ledger.rows;
1252
1271
  const invalid = validateLedgerRows(rows);
1253
1272
  if (invalid) {
1254
1273
  return {
@@ -1287,7 +1306,8 @@ function checkReviewFact({ taskDir, artifactFile }: any): any {
1287
1306
 
1288
1307
  const content = fs.readFileSync(resolvedArtifact.path, "utf8");
1289
1308
  const verdict = parseReviewVerdict(content);
1290
- const reviewBaseline = extractReviewBaseline(content);
1309
+ const reviewTargetHead = extractReviewTargetHead(content);
1310
+ const reviewReviewedHead = extractReviewedHead(content);
1291
1311
  const reviewDiffBase = extractReviewDiffBase(content);
1292
1312
  const reviewedFingerprint = extractReviewDiffFingerprint(content);
1293
1313
  const reviewedTree = extractReviewedSnapshotTree(content);
@@ -1296,30 +1316,42 @@ function checkReviewFact({ taskDir, artifactFile }: any): any {
1296
1316
  return failResult("review-fact", `Unsupported review verdict '${verdict}'`);
1297
1317
  }
1298
1318
 
1319
+ const deliveryRemote = String(task.metadata.delivery_remote || "").trim();
1320
+ const deliveryBaseRef = String(task.metadata.delivery_base_ref || "").trim();
1321
+ if (!deliveryRemote || !deliveryBaseRef) {
1322
+ return failResult("review-fact", "Task delivery target is not bound; re-run task branch preparation before review-code");
1323
+ }
1324
+ if (!reviewTargetHead || !reviewReviewedHead || !reviewDiffBase) {
1325
+ return failResult("review-fact", "Review fact must record target head, reviewed head, and diff base");
1326
+ }
1327
+
1299
1328
  let gitRoot;
1300
1329
  let head;
1301
1330
  let baseline;
1331
+ let targetHead;
1302
1332
  let diffBase;
1303
1333
  try {
1304
1334
  gitRoot = execFileSync("git", ["-C", taskDir, "rev-parse", "--show-toplevel"], { encoding: "utf8" }).trim();
1305
1335
  head = execFileSync("git", ["-C", gitRoot, "rev-parse", "HEAD"], { encoding: "utf8" }).trim();
1306
- baseline = execFileSync("git", ["-C", gitRoot, "rev-parse", `${reviewBaseline}^{commit}`], { encoding: "utf8" }).trim();
1307
- diffBase = execFileSync(
1308
- "git",
1309
- ["-C", gitRoot, "rev-parse", `${reviewDiffBase || reviewBaseline}^{commit}`],
1310
- { encoding: "utf8" }
1311
- ).trim();
1336
+ const target = resolveDeliveryTarget(gitRoot, { remote: deliveryRemote, baseRef: deliveryBaseRef });
1337
+ if (!target.ok) throw new Error(target.message);
1338
+ const reviewedRef = reviewReviewedHead;
1339
+ baseline = execFileSync("git", ["-C", gitRoot, "rev-parse", `${reviewedRef}^{commit}`], { encoding: "utf8" }).trim();
1340
+ targetHead = execFileSync("git", ["-C", gitRoot, "rev-parse", `${reviewTargetHead}^{commit}`], { encoding: "utf8" }).trim();
1341
+ const computed = execFileSync("git", ["-C", gitRoot, "merge-base", baseline, targetHead], { encoding: "utf8" }).trim();
1342
+ diffBase = execFileSync("git", ["-C", gitRoot, "rev-parse", `${reviewDiffBase}^{commit}`], { encoding: "utf8" }).trim();
1343
+ if (diffBase !== computed) throw new Error('saved review diff base does not match merge-base(reviewed head, target head)');
1312
1344
  } catch {
1313
1345
  return blockedResult(
1314
1346
  "review-fact",
1315
- `Unable to resolve review baseline '${reviewBaseline}' in the task repository; re-run review-code`
1347
+ `Unable to resolve saved review commits in the task repository; re-run review-code`
1316
1348
  );
1317
1349
  }
1318
1350
 
1319
1351
  if (baseline !== head) {
1320
1352
  return failResult(
1321
1353
  "review-fact",
1322
- `Review baseline ${baseline.slice(0, 8)} does not match current HEAD ${head.slice(0, 8)}; re-run review-code`
1354
+ `Reviewed head ${baseline.slice(0, 8)} does not match current HEAD ${head.slice(0, 8)}; re-run review-code`
1323
1355
  );
1324
1356
  }
1325
1357
 
@@ -1363,7 +1395,7 @@ function checkReviewFact({ taskDir, artifactFile }: any): any {
1363
1395
  if (cleanSnapshot && lastReviewedCommit !== baseline) {
1364
1396
  return failResult(
1365
1397
  "review-fact",
1366
- `Approved clean review must set task last_reviewed_commit to baseline ${baseline.slice(0, 8)}`
1398
+ `Approved clean review must set task last_reviewed_commit to reviewed head ${baseline.slice(0, 8)}`
1367
1399
  );
1368
1400
  }
1369
1401
  if (!cleanSnapshot && lastReviewedCommit) {
@@ -1,5 +1,7 @@
1
1
  export {
2
2
  extractReviewBaseline,
3
+ extractReviewTargetHead,
4
+ extractReviewedHead,
3
5
  extractReviewDiffBase,
4
6
  extractReviewDiffFingerprint,
5
7
  extractReviewedSnapshotTree,
@@ -8,4 +10,4 @@ export {
8
10
  parseReviewVerdict,
9
11
  resolvePostReviewGlobs
10
12
  } from './review-fingerprint.ts';
11
- export { parseLedger, summarizeLedgerStage, validateLedgerRows } from './ledger.ts';
13
+ export { parseLedgerDocument, summarizeLedgerStage, validateLedgerRows } from './ledger.ts';
@@ -53,13 +53,6 @@ function captureWorktreeTree(repoRoot: string, forcedPath: string | null): strin
53
53
  }
54
54
  }
55
55
 
56
- function captureLegacyWorkspaceSnapshot(repoRoot: string, taskId: string | null): string {
57
- const workspaceRelative = taskId
58
- ? `.agents/workspace/active/${taskId}`
59
- : '.agents/workspace/active';
60
- return captureWorktreeTree(repoRoot, workspaceRelative);
61
- }
62
-
63
56
  function taskFileFingerprints(stateRoot: string, taskId: string | null): TaskFileFingerprint[] {
64
57
  const activeRoot = path.join(stateRoot, '.agents', 'workspace', 'active');
65
58
  const roots = taskId ? [path.join(activeRoot, taskId)] : [activeRoot];
@@ -73,6 +66,7 @@ function taskFileFingerprints(stateRoot: string, taskId: string | null): TaskFil
73
66
  return;
74
67
  }
75
68
  if (!stat.isFile() && !stat.isSymbolicLink()) return;
69
+ if (relative.startsWith('.agents/workspace/active/') && path.posix.basename(relative) === 'orchestration.json') return;
76
70
  const content = stat.isSymbolicLink()
77
71
  ? Buffer.from(fs.readlinkSync(absolute))
78
72
  : fs.readFileSync(absolute);
@@ -108,14 +102,11 @@ function decodeWorkspaceFingerprint(value: string): OrchestrationWorkspaceFinger
108
102
  }
109
103
  }
110
104
 
111
- function captureWorkspaceSnapshot(context: WorkspaceSnapshotContext): string;
112
- function captureWorkspaceSnapshot(repoRoot: string, taskId: string | null): string;
113
- function captureWorkspaceSnapshot(contextOrRoot: WorkspaceSnapshotContext | string, taskId?: string | null): string {
114
- if (typeof contextOrRoot === 'string') return captureLegacyWorkspaceSnapshot(contextOrRoot, taskId ?? null);
105
+ function captureWorkspaceSnapshot(context: WorkspaceSnapshotContext): string {
115
106
  return encodeWorkspaceFingerprint({
116
107
  version: 2,
117
- gitTree: captureWorktreeTree(contextOrRoot.gitRoot, null),
118
- taskFiles: taskFileFingerprints(contextOrRoot.stateRoot, contextOrRoot.taskId)
108
+ gitTree: captureWorktreeTree(context.gitRoot, null),
109
+ taskFiles: taskFileFingerprints(context.stateRoot, context.taskId)
119
110
  });
120
111
  }
121
112
 
package/lib/task/write.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
3
 
4
- import { VERSION } from '../version.ts';
4
+ import { isValidAgentInfraVersion, VERSION } from '../version.ts';
5
5
  import {
6
6
  parseTypedTaskFrontmatter,
7
7
  updateTaskFrontmatter
@@ -15,6 +15,7 @@ import type {
15
15
  import { allowsManualOverride } from './guard-override.ts';
16
16
  import type { ManualOverrideCapability } from './guard-override.ts';
17
17
  import { mutateTableRow, upsertSection } from './sections.ts';
18
+ import { validateCurrentTaskContract } from './current-contract.ts';
18
19
  import type {
19
20
  TableRowDeleteMutation,
20
21
  TableRowUpsertMutation
@@ -84,6 +85,7 @@ type TaskWriteMetadata = {
84
85
  type TaskWriteErrorCode =
85
86
  | ResolveTaskRefErrorCode
86
87
  | 'TASK_STATE_MISMATCH'
88
+ | 'TASK_CURRENT_CONTRACT_INVALID'
87
89
  | 'TASK_READ_FAILED'
88
90
  | 'TASK_DOCUMENT_INVALID'
89
91
  | 'MUTATION_INVALID'
@@ -268,6 +270,13 @@ function writeTask(request: TaskWriteRequest, options: TaskWriteOptions = {}): T
268
270
  );
269
271
  }
270
272
 
273
+ if (resolved.state === 'active') {
274
+ const contract = validateCurrentTaskContract(original);
275
+ if (!contract.ok) {
276
+ return failure(request, identity, contract.code, contract.message);
277
+ }
278
+ }
279
+
271
280
  let candidate = original;
272
281
  const operations: TaskOperationSummary[] = [];
273
282
  try {
@@ -342,6 +351,16 @@ function writeTask(request: TaskWriteRequest, options: TaskWriteOptions = {}): T
342
351
  operations
343
352
  );
344
353
  }
354
+ if (resolved.state === 'active' && !isValidAgentInfraVersion(metadata.agentInfraVersion)) {
355
+ return failure(
356
+ request,
357
+ identity,
358
+ 'TASK_CURRENT_CONTRACT_INVALID',
359
+ `metadata agentInfraVersion must be a valid v-prefixed semver (received ${metadata.agentInfraVersion})`,
360
+ operations,
361
+ metadata
362
+ );
363
+ }
345
364
 
346
365
  if (candidate === original) {
347
366
  return {