@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
@@ -1,7 +1,7 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import { declareImplementationInput, IMPLEMENTATION_INPUT_ALIASES, parseImplementationInputs, renderImplementationInputs } from './implementation-inputs.js';
4
- import { LEDGER_COLUMNS, LEDGER_HEADINGS, nextHdId, parseLedger, validateLedgerRows } from './ledger.js';
4
+ import { LEDGER_COLUMNS, LEDGER_HEADINGS, LEDGER_SECTION_MISSING_CODE, LEDGER_SECTION_MISSING_MESSAGE, nextHdId, parseLedgerDocument, validateLedgerRows } from './ledger.js';
5
5
  import { resolveTaskRef } from './resolve-ref.js';
6
6
  import { writeTask } from './write.js';
7
7
  import { allowsManualOverride } from './guard-override.js';
@@ -26,6 +26,8 @@ function normalizedEvidence(value) {
26
26
  }
27
27
  function ledgerReadErrorCode(error) {
28
28
  const code = error && typeof error === 'object' && 'code' in error ? String(error.code) : '';
29
+ if (code === LEDGER_SECTION_MISSING_CODE)
30
+ return code;
29
31
  return code === 'TABLE_DUPLICATE_KEY' ? 'LEDGER_DUPLICATE_ID' : 'LEDGER_DOCUMENT_INVALID';
30
32
  }
31
33
  function maxHandshakeRounds(repoRoot) {
@@ -60,13 +62,7 @@ function rowMutation(row) {
60
62
  };
61
63
  }
62
64
  function ledgerSectionMutation(content) {
63
- if (/^##\s+(审查分歧账本|Review Disagreement Ledger)\s*$/m.test(content))
64
- return [];
65
- const english = /^##\s+Activity Log\s*$/m.test(content);
66
- return [{
67
- kind: 'section', aliases: LEDGER_HEADINGS, heading: english ? 'Review Disagreement Ledger' : '审查分歧账本',
68
- body: `| ${LEDGER_COLUMNS.join(' | ')} |\n|----|-------|-------|----------|--------|----------|`
69
- }];
65
+ return [];
70
66
  }
71
67
  function implementationInputMutation(content, rows) {
72
68
  const english = /^##\s+Activity Log\s*$/m.test(content);
@@ -92,7 +88,10 @@ function applyLedgerIntent(intent, options = {}) {
92
88
  let rows;
93
89
  try {
94
90
  content = fs.readFileSync(resolved.taskMdPath, 'utf8');
95
- rows = parseLedger(content);
91
+ const ledger = parseLedgerDocument(content);
92
+ if (!ledger.present)
93
+ return failed(intent, LEDGER_SECTION_MISSING_CODE, LEDGER_SECTION_MISSING_MESSAGE, resolved.taskId);
94
+ rows = ledger.rows;
96
95
  }
97
96
  catch (error) {
98
97
  return failed(intent, ledgerReadErrorCode(error), error instanceof Error ? error.message : String(error), resolved.taskId);
@@ -12,18 +12,25 @@ const LEDGER_TERMINAL = new Set(['confirmed', 'closed', 'human-decided']);
12
12
  // Statuses that represent a decision row awaiting or carrying a human ruling.
13
13
  const HUMAN_DECISION_STATUSES = new Set(['needs-human-decision', 'human-decided']);
14
14
  const FINDING_STATUSES = new Set(['open', 'accepted', 'adjusted', 'refuted', 'cannot-judge', 'confirmed', 'needs-human-decision', 'closed', 'human-decided']);
15
+ const LEDGER_SECTION_MISSING_CODE = 'LEDGER_SECTION_MISSING';
16
+ const LEDGER_SECTION_MISSING_MESSAGE = 'review disagreement ledger section is missing';
15
17
  function isReviewStage(stage) {
16
18
  return stage === 'analysis' || stage === 'plan' || stage === 'code';
17
19
  }
18
20
  // Parse all rows of the disagreement ledger table. Skips the heading, the
19
21
  // header row (`| id | ... |`) and the `|---|` separator; ignores non-`|` lines.
20
22
  // Rows with fewer than 6 columns are skipped (mirrors the JS gate parser).
21
- function parseLedger(content) {
23
+ function parseLedgerDocument(content) {
22
24
  const table = parseTable(content, { sectionAliases: LEDGER_HEADINGS, columns: LEDGER_COLUMNS });
23
- return table?.rows.map(({ values, sourceLine }) => ({
24
- id: values.id, stage: values.stage, round: values.round, severity: values.severity,
25
- status: values.status, evidence: values.evidence, sourceLine
26
- })) ?? [];
25
+ if (!table)
26
+ return { present: false, rows: [] };
27
+ return {
28
+ present: true,
29
+ rows: table.rows.map(({ values, sourceLine }) => ({
30
+ id: values.id, stage: values.stage, round: values.round, severity: values.severity,
31
+ status: values.status, evidence: values.evidence, sourceLine
32
+ }))
33
+ };
27
34
  }
28
35
  function validateLedgerRows(rows) {
29
36
  const seen = new Set();
@@ -104,5 +111,5 @@ function nextHdId(rows) {
104
111
  }
105
112
  return `HD-${max + 1}`;
106
113
  }
107
- export { LEDGER_HEADINGS, LEDGER_COLUMNS, parseLedger, nextHdId, isReviewStage, validateLedgerRows, summarizeLedgerStage, LEDGER_TERMINAL, HUMAN_DECISION_STATUSES };
114
+ export { LEDGER_HEADINGS, LEDGER_COLUMNS, parseLedgerDocument, nextHdId, isReviewStage, validateLedgerRows, summarizeLedgerStage, LEDGER_TERMINAL, HUMAN_DECISION_STATUSES, LEDGER_SECTION_MISSING_CODE, LEDGER_SECTION_MISSING_MESSAGE };
108
115
  //# sourceMappingURL=ledger.js.map
@@ -7,11 +7,14 @@ import { parseTypedTaskFrontmatter } from './frontmatter.js';
7
7
  import { locateHotTaskDirs, resolveTaskRef, TASK_ID_RE } from './resolve-ref.js';
8
8
  import { configuredShortIdLength, executeShortIdCommand, loadShortIdByTaskId, mutateShortIdRegistry } from './short-id.js';
9
9
  import { captureTaskWriteMetadata, writeTask } from './write.js';
10
+ import { validateCurrentTaskContract } from './current-contract.js';
11
+ import { isValidAgentInfraVersion } from '../version.js';
10
12
  const lifecycleIntentCatalog = [
11
13
  'block', 'activate', 'cancel', 'complete', 'close-codescan', 'close-dependabot', 'restore'
12
14
  ];
13
15
  const lifecycleFailureCatalog = [
14
16
  'LIFECYCLE_DIRECTORY_RENAME_FAILED',
17
+ 'LIFECYCLE_DIRECTORY_COPY_FAILED',
15
18
  'LIFECYCLE_DOCUMENT_INVALID',
16
19
  'LIFECYCLE_FINAL_STATE_INVALID',
17
20
  'LIFECYCLE_IDENTITY_CONFLICT',
@@ -57,6 +60,9 @@ function failed(request, error, extra = {}) {
57
60
  error, ...extra
58
61
  };
59
62
  }
63
+ export function taskLifecycleFailure(request, error, taskId = null) {
64
+ return failed(request, error, { taskId });
65
+ }
60
66
  function normalizedRequest(request) {
61
67
  if (!request || typeof request !== 'object' || !lifecycleIntentCatalog.includes(request.intent)) {
62
68
  return { code: 'LIFECYCLE_PAYLOAD_INVALID', message: 'intent is not in the lifecycle catalog' };
@@ -290,6 +296,65 @@ function parseJournal(content) {
290
296
  }
291
297
  return value;
292
298
  }
299
+ function directoryManifest(root) {
300
+ const entries = [];
301
+ const visit = (current, relative = '') => {
302
+ for (const entry of fs.readdirSync(current, { withFileTypes: true }).sort((left, right) => left.name.localeCompare(right.name))) {
303
+ const relativePath = relative ? path.join(relative, entry.name) : entry.name;
304
+ const absolute = path.join(current, entry.name);
305
+ const stat = fs.lstatSync(absolute);
306
+ if (stat.isSymbolicLink())
307
+ throw new Error(`symbolic link is not allowed in lifecycle directory: ${relativePath}`);
308
+ if (entry.isDirectory()) {
309
+ entries.push({ relativePath, kind: 'directory', size: 0, digest: '' });
310
+ visit(absolute, relativePath);
311
+ }
312
+ else if (entry.isFile()) {
313
+ const digest = createHash('sha256').update(fs.readFileSync(absolute)).digest('hex');
314
+ entries.push({ relativePath, kind: 'file', size: stat.size, digest });
315
+ }
316
+ else {
317
+ throw new Error(`unsupported lifecycle directory entry: ${relativePath}`);
318
+ }
319
+ }
320
+ };
321
+ visit(root);
322
+ return entries;
323
+ }
324
+ function copyDirectoryVerified(source, target) {
325
+ if (fs.existsSync(target))
326
+ throw new Error(`target directory already exists: ${target}`);
327
+ const copy = (currentSource, currentTarget) => {
328
+ fs.mkdirSync(currentTarget);
329
+ for (const entry of fs.readdirSync(currentSource, { withFileTypes: true })) {
330
+ const sourcePath = path.join(currentSource, entry.name);
331
+ const targetPath = path.join(currentTarget, entry.name);
332
+ const stat = fs.lstatSync(sourcePath);
333
+ if (stat.isSymbolicLink())
334
+ throw new Error(`symbolic link is not allowed in lifecycle directory: ${entry.name}`);
335
+ if (entry.isDirectory())
336
+ copy(sourcePath, targetPath);
337
+ else if (entry.isFile())
338
+ fs.copyFileSync(sourcePath, targetPath);
339
+ else
340
+ throw new Error(`unsupported lifecycle directory entry: ${entry.name}`);
341
+ }
342
+ };
343
+ copy(source, target);
344
+ const sourceManifest = directoryManifest(source);
345
+ const targetManifest = directoryManifest(target);
346
+ if (JSON.stringify(sourceManifest) !== JSON.stringify(targetManifest)) {
347
+ throw new Error('copied lifecycle directory failed manifest verification');
348
+ }
349
+ }
350
+ function removeCopiedSource(source, target) {
351
+ const sourceManifest = directoryManifest(source);
352
+ const targetManifest = directoryManifest(target);
353
+ if (JSON.stringify(sourceManifest) !== JSON.stringify(targetManifest)) {
354
+ throw new Error('lifecycle source and target diverged before source cleanup');
355
+ }
356
+ fs.rmSync(source, { recursive: true, force: false });
357
+ }
293
358
  function applyTaskLifecycle(requestInput, options = {}) {
294
359
  const normalized = normalizedRequest(requestInput);
295
360
  if ('code' in normalized)
@@ -351,7 +416,7 @@ function applyTaskLifecycle(requestInput, options = {}) {
351
416
  if (!spec.sources.includes(sourceState) && !existingJournal && !allowsManualOverride(options, 'LIFECYCLE_SOURCE_INVALID')) {
352
417
  return failed(request, { code: 'LIFECYCLE_SOURCE_INVALID', message: `${request.intent} is not allowed from ${sourceState}` }, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath });
353
418
  }
354
- if (sourcePath !== targetPath && fs.existsSync(targetPath)) {
419
+ if (sourcePath !== targetPath && fs.existsSync(targetPath) && !existingJournal) {
355
420
  return failed(request, { code: 'LIFECYCLE_TARGET_CONFLICT', message: `target directory already exists: ${targetPath}` }, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath });
356
421
  }
357
422
  const content = io.readFileSync(currentTaskFile);
@@ -366,6 +431,11 @@ function applyTaskLifecycle(requestInput, options = {}) {
366
431
  return failed(request, { code: 'LIFECYCLE_IDENTITY_INVALID', message: 'task.md id does not match its lifecycle task id' }, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath });
367
432
  if (!locateActivityLog(content) && !allowsManualOverride(options, 'LIFECYCLE_LOG_MISSING'))
368
433
  return failed(request, { code: 'LIFECYCLE_LOG_MISSING', message: 'task has no unique Activity Log section' }, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath });
434
+ if (spec.target === 'active') {
435
+ const contract = validateCurrentTaskContract(content);
436
+ if (!contract.ok)
437
+ return failed(request, { code: 'LIFECYCLE_DOCUMENT_INVALID', message: contract.message }, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath });
438
+ }
369
439
  const registryRequest = executeShortIdCommand({
370
440
  operation: 'list', activeDir: path.join(repoRoot, '.agents', 'workspace', 'active'),
371
441
  shortIdLength: configuredShortIdLength(repoRoot)
@@ -394,6 +464,9 @@ function applyTaskLifecycle(requestInput, options = {}) {
394
464
  }
395
465
  if (journal.taskId !== taskId || journal.intent !== request.intent)
396
466
  return failed(request, { code: 'LIFECYCLE_INTENT_CONFLICT', message: 'an in-progress lifecycle journal belongs to a different request' }, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath, journalPath: initialJournalPath });
467
+ if (spec.target === 'active' && !isValidAgentInfraVersion(journal.metadata.agentInfraVersion)) {
468
+ return failed(request, { code: 'LIFECYCLE_DOCUMENT_INVALID', message: `journal metadata agentInfraVersion must be a valid v-prefixed semver (received ${journal.metadata.agentInfraVersion})` }, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath, journalPath: initialJournalPath });
469
+ }
397
470
  if (request.dryRun) {
398
471
  if (!journal.failure)
399
472
  return failed(request, { code: 'LIFECYCLE_JOURNAL_INVALID', message: 'existing lifecycle journal has no durable failure evidence for a read-only probe' }, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath, journalPath: initialJournalPath });
@@ -411,6 +484,9 @@ function applyTaskLifecycle(requestInput, options = {}) {
411
484
  catch (error) {
412
485
  return failed(request, { code: 'LIFECYCLE_METADATA_FAILED', message: String(error) }, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath });
413
486
  }
487
+ if (spec.target === 'active' && !isValidAgentInfraVersion(metadata.agentInfraVersion)) {
488
+ return failed(request, { code: 'LIFECYCLE_DOCUMENT_INVALID', message: `metadata agentInfraVersion must be a valid v-prefixed semver (received ${metadata.agentInfraVersion})` }, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath });
489
+ }
414
490
  journal = { version: 1, taskId, intent: request.intent, intentDigest: digest, sourceState, targetState: spec.target, metadata, completedSteps: [] };
415
491
  if (request.dryRun) {
416
492
  const plannedMutations = mutationsFor(request, content, metadata, restoredFiles, allowsManualOverride(options, 'LIFECYCLE_LOG_MISSING'));
@@ -485,18 +561,55 @@ function applyTaskLifecycle(requestInput, options = {}) {
485
561
  (options.directoryRenameSync ?? io.renameSync)(sourcePath, targetPath);
486
562
  }
487
563
  catch (error) {
488
- const failure = { code: 'LIFECYCLE_DIRECTORY_RENAME_FAILED', message: String(error) };
489
- rememberJournalFailure(journalPath, journal, io, failure);
490
- return failed(request, failure, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath, journalPath, task: { operations: taskOperations }, changed: true, completedSteps: [...completed], timestamp: journal.metadata.timestamp, agentInfraVersion: journal.metadata.agentInfraVersion });
564
+ if (error.code === 'EXDEV') {
565
+ try {
566
+ copyDirectoryVerified(sourcePath, targetPath);
567
+ completed.add('directory-moved');
568
+ journal.completedSteps = [...completed];
569
+ const copiedJournalPath = path.join(targetPath, '.task-lifecycle.json');
570
+ writeJournal(initialJournalPath, journal, io);
571
+ writeJournal(copiedJournalPath, journal, io);
572
+ removeCopiedSource(sourcePath, targetPath);
573
+ journalPath = copiedJournalPath;
574
+ }
575
+ catch (copyError) {
576
+ if (fs.existsSync(targetPath))
577
+ fs.rmSync(targetPath, { recursive: true, force: false });
578
+ const failure = { code: 'LIFECYCLE_DIRECTORY_COPY_FAILED', message: String(copyError) };
579
+ rememberJournalFailure(initialJournalPath, journal, io, failure);
580
+ return failed(request, failure, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath, journalPath: initialJournalPath, task: { operations: taskOperations }, changed: true, completedSteps: [...completed], timestamp: journal.metadata.timestamp, agentInfraVersion: journal.metadata.agentInfraVersion });
581
+ }
582
+ }
583
+ else {
584
+ const failure = { code: 'LIFECYCLE_DIRECTORY_RENAME_FAILED', message: String(error) };
585
+ rememberJournalFailure(journalPath, journal, io, failure);
586
+ return failed(request, failure, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath, journalPath, task: { operations: taskOperations }, changed: true, completedSteps: [...completed], timestamp: journal.metadata.timestamp, agentInfraVersion: journal.metadata.agentInfraVersion });
587
+ }
491
588
  }
492
- completed.add('directory-moved');
493
- journal.completedSteps = [...completed];
494
- journalPath = path.join(targetPath, '.task-lifecycle.json');
589
+ if (completed.has('directory-moved') && !fs.existsSync(sourcePath)) {
590
+ journalPath = path.join(targetPath, '.task-lifecycle.json');
591
+ }
592
+ else if (!completed.has('directory-moved')) {
593
+ completed.add('directory-moved');
594
+ journal.completedSteps = [...completed];
595
+ journalPath = path.join(targetPath, '.task-lifecycle.json');
596
+ try {
597
+ writeJournal(journalPath, journal, io);
598
+ }
599
+ catch (error) {
600
+ return failed(request, { code: 'LIFECYCLE_JOURNAL_WRITE_FAILED', message: String(error) }, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath, journalPath, task: { operations: taskOperations }, directory: { effect: 'move', changed: true }, changed: true, completedSteps: [...completed], timestamp: journal.metadata.timestamp, agentInfraVersion: journal.metadata.agentInfraVersion });
601
+ }
602
+ }
603
+ }
604
+ if (completed.has('directory-moved') && sourcePath !== targetPath && fs.existsSync(sourcePath) && fs.existsSync(targetPath)) {
495
605
  try {
496
- writeJournal(journalPath, journal, io);
606
+ removeCopiedSource(sourcePath, targetPath);
607
+ journalPath = path.join(targetPath, '.task-lifecycle.json');
497
608
  }
498
609
  catch (error) {
499
- return failed(request, { code: 'LIFECYCLE_JOURNAL_WRITE_FAILED', message: String(error) }, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath, journalPath, task: { operations: taskOperations }, directory: { effect: 'move', changed: true }, changed: true, completedSteps: [...completed], timestamp: journal.metadata.timestamp, agentInfraVersion: journal.metadata.agentInfraVersion });
610
+ const failure = { code: 'LIFECYCLE_DIRECTORY_COPY_FAILED', message: String(error) };
611
+ rememberJournalFailure(journalPath, journal, io, failure);
612
+ return failed(request, failure, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath, journalPath, task: { operations: taskOperations }, changed: true, completedSteps: [...completed], timestamp: journal.metadata.timestamp, agentInfraVersion: journal.metadata.agentInfraVersion });
500
613
  }
501
614
  }
502
615
  let shortId = { effect: 'unchanged', shortId: null, changed: false };
@@ -2,20 +2,22 @@ import fs from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import { randomUUID } from 'node:crypto';
4
4
  import { parseTypedTaskFrontmatter } from './frontmatter.js';
5
- import { parseReviewSummary } from './review-artifacts.js';
5
+ import { validateCurrentTaskContract } from './current-contract.js';
6
+ import { parseReviewSummary, resolveCanonicalVerdict } from './review-artifacts.js';
6
7
  import { inspectArtifactDirectory } from './artifact-lifecycle.js';
7
- import { parseLedger, summarizeLedgerStage, validateLedgerRows } from './ledger.js';
8
+ import { LEDGER_SECTION_MISSING_CODE, LEDGER_SECTION_MISSING_MESSAGE, parseLedgerDocument, summarizeLedgerStage, validateLedgerRows } from './ledger.js';
8
9
  import { resolveTaskRef } from './resolve-ref.js';
9
10
  import { activateDelegation, abortPreparedDelegation, completeDelegationStage, consumeDelegation, dispatchDelegation, foldBlankToNull, isDelegationReceipt, managedDelegationRole, prepareDelegation, sealDelegation } from './delegation-receipts.js';
10
11
  import { normalizeAgentClients } from '../agent-clients/config.js';
11
12
  import { resolveAgentRuntimeStoreRoot } from '../runtime/agent-runtime.js';
12
- import { inspectPlatformPullRequest } from '../platform/pull-requests.js';
13
13
  import { getAgentClientCapability, getAgentClientDelegationEvidence, getAgentClientModelSelection } from '../agent-clients/registry.js';
14
14
  import { isAgentClientId } from '../agent-clients/types.js';
15
15
  import { captureRepositorySnapshot, captureWorkspaceSnapshot, diffWorkspaceSnapshots } from './workspace-snapshot.js';
16
16
  import { assertGitRepositoryBinding } from '../git/worktree-identity.js';
17
17
  import { TaskExecutionLockError, withTaskExecutionLock } from './task-execution-lock.js';
18
18
  import { hasActiveCodexLifecycleEvidence } from '../agent-clients/adapters/codex-lifecycle/store.js';
19
+ import { extractReviewBaseline, extractReviewDiffBase, extractReviewTargetHead, extractReviewedHead } from './review-fingerprint.js';
20
+ import { resolveDeliveryTarget, resolveDiffBase, resolveTargetHead } from './delivery-target.js';
19
21
  function supportsLifecycleDelegation(client) {
20
22
  return getAgentClientCapability(client, 'subagents').level !== 'unsupported'
21
23
  && getAgentClientCapability(client, 'orchestration').level !== 'unsupported'
@@ -105,9 +107,8 @@ function isCompletionEvidence(value) {
105
107
  && exactText(value.headTree)
106
108
  && exactText(value.worktreeTree)
107
109
  && exactText(value.lastReviewedCommit)
108
- && Number.isSafeInteger(value.prNumber)
109
- && value.prNumber > 0
110
- && exactText(value.prHead);
110
+ && (value.prNumber === null || (Number.isSafeInteger(value.prNumber) && value.prNumber > 0))
111
+ && (value.prHead === null || exactText(value.prHead));
111
112
  }
112
113
  const ORCHESTRATION_RUN_KEYS = [
113
114
  'taskId', 'runId', 'status', 'nextStage', 'stepCount', 'maxSteps', 'modelPolicy',
@@ -339,6 +340,43 @@ function highestRound(taskDir, family) {
339
340
  function artifactName(family, round) {
340
341
  return round === 1 ? `${family}.md` : `${family}-r${round}.md`;
341
342
  }
343
+ function validateSavedCurrentDiffBase(repoRoot, metadata, taskDir, reviewedHead) {
344
+ const reviewRound = highestRound(taskDir, 'review-code');
345
+ const reviewContent = fs.readFileSync(path.join(taskDir, artifactName('review-code', reviewRound)), 'utf8');
346
+ const savedTargetHead = extractReviewTargetHead(reviewContent);
347
+ const savedDiffBase = extractReviewDiffBase(reviewContent);
348
+ const savedReviewedHead = extractReviewedHead(reviewContent) || extractReviewBaseline(reviewContent);
349
+ if (!savedTargetHead && !savedDiffBase && !savedReviewedHead)
350
+ return null;
351
+ if (savedReviewedHead && savedReviewedHead !== reviewedHead) {
352
+ return { code: 'ORCHESTRATION_REVIEWED_HEAD_MISMATCH', message: 'review-code saved reviewed head does not match last_reviewed_commit' };
353
+ }
354
+ if (!savedTargetHead || !savedDiffBase) {
355
+ return { code: 'ORCHESTRATION_REVIEW_TARGET_INVALID', message: 'review-code must save target head and diff base together' };
356
+ }
357
+ const savedBase = resolveDiffBase(repoRoot, reviewedHead, savedTargetHead);
358
+ if (!savedBase.ok)
359
+ return { code: 'ORCHESTRATION_REVIEW_TARGET_INVALID', message: savedBase.message };
360
+ if (savedBase.diffBase !== savedDiffBase) {
361
+ return { code: 'ORCHESTRATION_REVIEW_DIFF_BASE_MISMATCH', message: 'saved review diff base does not match reviewed head and saved target head' };
362
+ }
363
+ const target = resolveDeliveryTarget(repoRoot, {
364
+ remote: typeof metadata.delivery_remote === 'string' ? metadata.delivery_remote : undefined,
365
+ baseRef: typeof metadata.delivery_base_ref === 'string' ? metadata.delivery_base_ref : undefined
366
+ });
367
+ if (!target.ok)
368
+ return { code: target.code, message: target.message };
369
+ const currentTarget = resolveTargetHead(repoRoot, target.value);
370
+ if (!currentTarget.ok)
371
+ return { code: currentTarget.code, message: currentTarget.message };
372
+ const currentBase = resolveDiffBase(repoRoot, reviewedHead, currentTarget.head);
373
+ if (!currentBase.ok)
374
+ return { code: currentBase.code, message: currentBase.message };
375
+ if (currentBase.diffBase !== savedDiffBase) {
376
+ return { code: 'ORCHESTRATION_REVIEW_TARGET_CHANGED', message: 'current delivery target changed the reviewed diff base; re-run review-code' };
377
+ }
378
+ return null;
379
+ }
342
380
  function hasReviewAfterArtifact(taskDir, family, reviewFamily) {
343
381
  const artifact = inspectArtifactDirectory(taskDir, family);
344
382
  const review = inspectArtifactDirectory(taskDir, reviewFamily);
@@ -350,9 +388,21 @@ function hasReviewAfterArtifact(taskDir, family, reviewFamily) {
350
388
  function latestReviewApproved(taskDir, family) {
351
389
  const round = highestRound(taskDir, family);
352
390
  if (round === 0)
353
- return false;
354
- const parsed = parseReviewSummary(fs.readFileSync(path.join(taskDir, artifactName(family, round)), 'utf8'));
355
- return parsed.ok && parsed.summary.verdict === 'Approved' && parsed.summary.counts !== null;
391
+ return { approved: false, error: null };
392
+ let content;
393
+ try {
394
+ content = fs.readFileSync(path.join(taskDir, artifactName(family, round)), 'utf8');
395
+ }
396
+ catch (error) {
397
+ return { approved: false, error: String(error) };
398
+ }
399
+ const parsed = parseReviewSummary(content);
400
+ if (!parsed.ok)
401
+ return { approved: false, error: `${parsed.code}: ${parsed.message}` };
402
+ const verdict = resolveCanonicalVerdict(parsed.summary);
403
+ if (!verdict.ok)
404
+ return { approved: false, error: `${verdict.code}: ${verdict.message}` };
405
+ return { approved: verdict.verdict === 'Approved', error: null };
356
406
  }
357
407
  function routeFromFacts(taskDir) {
358
408
  const analysisRound = highestRound(taskDir, 'analysis');
@@ -369,7 +419,10 @@ function routeFromFacts(taskDir) {
369
419
  const round = analysisReviewRound + 1;
370
420
  return { action: 'review-analysis', role: 'reviewer', stage: 'review-analysis', round, artifact: artifactName('review-analysis', round) };
371
421
  }
372
- if (!latestReviewApproved(taskDir, 'review-analysis')) {
422
+ const analysisVerdict = latestReviewApproved(taskDir, 'review-analysis');
423
+ if (analysisVerdict.error)
424
+ return { error: analysisVerdict.error };
425
+ if (!analysisVerdict.approved) {
373
426
  const round = analysisRound + 1;
374
427
  return { action: 'analyze-task', role: 'executor', stage: 'analysis', round, artifact: artifactName('analysis', round) };
375
428
  }
@@ -381,7 +434,10 @@ function routeFromFacts(taskDir) {
381
434
  const round = planReviewRound + 1;
382
435
  return { action: 'review-plan', role: 'reviewer', stage: 'review-plan', round, artifact: artifactName('review-plan', round) };
383
436
  }
384
- if (!latestReviewApproved(taskDir, 'review-plan')) {
437
+ const planVerdict = latestReviewApproved(taskDir, 'review-plan');
438
+ if (planVerdict.error)
439
+ return { error: planVerdict.error };
440
+ if (!planVerdict.approved) {
385
441
  const round = planRound + 1;
386
442
  return { action: 'plan-task', role: 'executor', stage: 'plan', round, artifact: artifactName('plan', round) };
387
443
  }
@@ -393,7 +449,10 @@ function routeFromFacts(taskDir) {
393
449
  const round = codeReviewRound + 1;
394
450
  return { action: 'review-code', role: 'reviewer', stage: 'review-code', round, artifact: artifactName('review-code', round) };
395
451
  }
396
- if (!latestReviewApproved(taskDir, 'review-code')) {
452
+ const codeVerdict = latestReviewApproved(taskDir, 'review-code');
453
+ if (codeVerdict.error)
454
+ return { error: codeVerdict.error };
455
+ if (!codeVerdict.approved) {
397
456
  const round = codeRound + 1;
398
457
  return { action: 'code-task', role: 'executor', stage: 'code', round, artifact: artifactName('code', round) };
399
458
  }
@@ -418,13 +477,25 @@ function routeOrchestration(taskRef, options = {}) {
418
477
  return failed('ORCHESTRATION_TASK_READ_FAILED', String(error), resolved.taskId);
419
478
  }
420
479
  let metadata;
421
- try {
422
- metadata = parseTypedTaskFrontmatter(content);
480
+ let currentLedger = null;
481
+ if (resolved.state === 'active') {
482
+ const contract = validateCurrentTaskContract(content);
483
+ if (!contract.ok)
484
+ return failed('ORCHESTRATION_CURRENT_CONTRACT_INVALID', contract.message, resolved.taskId);
485
+ metadata = contract.metadata;
486
+ currentLedger = contract.ledger;
423
487
  }
424
- catch (error) {
425
- return failed('ORCHESTRATION_TASK_INVALID', error instanceof Error ? error.message : String(error), resolved.taskId);
488
+ else {
489
+ try {
490
+ metadata = parseTypedTaskFrontmatter(content);
491
+ }
492
+ catch (error) {
493
+ return failed('ORCHESTRATION_TASK_INVALID', error instanceof Error ? error.message : String(error), resolved.taskId);
494
+ }
426
495
  }
427
496
  const routed = routeFromFacts(resolved.taskDir);
497
+ if (routed && 'error' in routed)
498
+ return failed('ORCHESTRATION_REVIEW_INVALID', routed.error, resolved.taskId);
428
499
  if (!routed)
429
500
  return failed('ORCHESTRATION_ROUTE_UNKNOWN', 'cannot determine a unique lifecycle action', resolved.taskId);
430
501
  const run = readRun(resolved.taskDir);
@@ -440,7 +511,10 @@ function routeOrchestration(taskRef, options = {}) {
440
511
  if (!review.ok || review.summary.manualValidation === null) {
441
512
  return failed('ORCHESTRATION_REVIEW_INVALID', 'latest code review has no numeric manual-validation count', resolved.taskId);
442
513
  }
443
- const rows = parseLedger(content);
514
+ const ledger = currentLedger ?? parseLedgerDocument(content);
515
+ if (!ledger.present)
516
+ return failed('ORCHESTRATION_CURRENT_CONTRACT_INVALID', `${LEDGER_SECTION_MISSING_CODE}: ${LEDGER_SECTION_MISSING_MESSAGE}`, resolved.taskId);
517
+ const rows = ledger.rows;
444
518
  const ledgerError = validateLedgerRows(rows);
445
519
  if (ledgerError)
446
520
  return failed('ORCHESTRATION_LEDGER_INVALID', ledgerError.message, resolved.taskId);
@@ -450,29 +524,8 @@ function routeOrchestration(taskRef, options = {}) {
450
524
  if (run?.status === 'completed') {
451
525
  return { status: 'completed', changed: false, taskId: resolved.taskId, run, next: null, error: null };
452
526
  }
453
- let config;
454
- try {
455
- config = JSON.parse(fs.readFileSync(path.join(resolved.repoRoot, '.agents', '.airc.json'), 'utf8'));
456
- }
457
- catch (error) {
458
- return failed('ORCHESTRATION_CONFIG_INVALID', error instanceof Error ? error.message : String(error), resolved.taskId);
459
- }
460
- if (!config || typeof config !== 'object' || Array.isArray(config)) {
461
- return failed('ORCHESTRATION_CONFIG_INVALID', 'project configuration must be an object', resolved.taskId);
462
- }
463
- const projectConfig = config;
464
- if (!Object.hasOwn(projectConfig, 'prFlow') || projectConfig.prFlow === 'disabled') {
465
- return { status: 'running', changed: false, taskId: resolved.taskId, run, next, error: null };
466
- }
467
- if (projectConfig.prFlow !== 'required') {
468
- return failed('ORCHESTRATION_CONFIG_INVALID', "project configuration 'prFlow' must be 'required' or 'disabled'", resolved.taskId);
469
- }
470
- const prNumber = Number(metadata.pr_number);
471
- if (!Number.isInteger(prNumber) || prNumber <= 0) {
472
- return { status: 'running', changed: false, taskId: resolved.taskId, run, next, error: null };
473
- }
474
527
  if (!run) {
475
- return { status: 'running', changed: false, taskId: resolved.taskId, run, next, error: null };
528
+ return { status: 'running', changed: false, taskId: resolved.taskId, run, next: null, error: null };
476
529
  }
477
530
  if (run.status !== 'running' || run.pendingDelegation !== null) {
478
531
  return failed('ORCHESTRATION_RUN_NOT_RUNNING', 'clean completion requires an idle running orchestration', resolved.taskId);
@@ -486,34 +539,15 @@ function routeOrchestration(taskRef, options = {}) {
486
539
  return failed('ORCHESTRATION_SNAPSHOT_FAILED', error instanceof Error ? error.message : String(error), resolved.taskId);
487
540
  }
488
541
  if (before.headTree !== before.worktreeTree) {
489
- return { status: 'running', changed: false, taskId: resolved.taskId, run, next, error: null };
542
+ return { status: 'running', changed: false, taskId: resolved.taskId, run, next: null, error: null };
490
543
  }
491
544
  const lastReviewedCommit = String(metadata.last_reviewed_commit ?? '');
492
545
  if (before.head !== lastReviewedCommit) {
493
546
  return failed('ORCHESTRATION_REVIEWED_HEAD_MISMATCH', 'local HEAD does not match last_reviewed_commit', resolved.taskId);
494
547
  }
495
- const inspect = options.inspectPullRequest ?? inspectPlatformPullRequest;
496
- let inspection;
497
- try {
498
- inspection = inspect(resolved.taskId, { cwd: gitRoot });
499
- }
500
- catch (error) {
501
- return failed('ORCHESTRATION_PR_INSPECTION_FAILED', error instanceof Error ? error.message : String(error), resolved.taskId);
502
- }
503
- if (inspection.status === 'blocked') {
504
- return failed('ORCHESTRATION_PR_INSPECTION_BLOCKED', inspection.error?.message ?? 'pull request inspection is blocked', resolved.taskId);
505
- }
506
- if (inspection.status === 'failed') {
507
- return failed('ORCHESTRATION_PR_INSPECTION_FAILED', inspection.error?.message ?? 'pull request inspection failed', resolved.taskId);
508
- }
509
- const inspectedPrNumber = inspection.task?.prNumber ?? inspection.prNumber;
510
- const prHead = inspection.pullRequest?.head?.sha;
511
- if (typeof prHead !== 'string') {
512
- return failed('ORCHESTRATION_PR_INSPECTION_INVALID', 'pull request inspection returned incomplete identity evidence', resolved.taskId);
513
- }
514
- if (inspectedPrNumber !== prNumber || prHead !== before.head) {
515
- return failed('ORCHESTRATION_PR_HEAD_MISMATCH', 'pull request identity does not match the reviewed local HEAD', resolved.taskId);
516
- }
548
+ const diffBaseError = validateSavedCurrentDiffBase(resolved.repoRoot, metadata, resolved.taskDir, lastReviewedCommit);
549
+ if (diffBaseError)
550
+ return failed(diffBaseError.code, diffBaseError.message, resolved.taskId);
517
551
  let after;
518
552
  try {
519
553
  after = capture(gitRoot);
@@ -522,7 +556,7 @@ function routeOrchestration(taskRef, options = {}) {
522
556
  return failed('ORCHESTRATION_SNAPSHOT_FAILED', error instanceof Error ? error.message : String(error), resolved.taskId);
523
557
  }
524
558
  if (after.headTree !== after.worktreeTree) {
525
- return { status: 'running', changed: false, taskId: resolved.taskId, run, next, error: null };
559
+ return { status: 'running', changed: false, taskId: resolved.taskId, run, next: null, error: null };
526
560
  }
527
561
  if (after.head !== before.head || after.headTree !== before.headTree) {
528
562
  return failed('ORCHESTRATION_REPOSITORY_CHANGED', 'repository changed while clean completion evidence was collected', resolved.taskId);
@@ -535,8 +569,8 @@ function routeOrchestration(taskRef, options = {}) {
535
569
  headTree: after.headTree,
536
570
  worktreeTree: after.worktreeTree,
537
571
  lastReviewedCommit,
538
- prNumber,
539
- prHead
572
+ prNumber: null,
573
+ prHead: null
540
574
  };
541
575
  const completed = withUpdatedRun(run, {
542
576
  status: 'completed',
@@ -631,10 +665,7 @@ function prepareOrchestrationDelegationUnlocked(taskRef, input, options = {}) {
631
665
  }
632
666
  const updated = withUpdatedRun(run, {
633
667
  nextStage: next.stage,
634
- pendingDelegation: receipt,
635
- commitAuthorization: next.stage === 'commit' && run.commitAuthorization.issuedAt === null
636
- ? { issuedAt: (options.now ?? (() => new Date().toISOString()))(), consumedAt: null }
637
- : run.commitAuthorization
668
+ pendingDelegation: receipt
638
669
  });
639
670
  saveRun(resolved.taskDir, updated);
640
671
  return { status: 'running', changed: true, taskId: resolved.taskId, run: updated, next, error: null };
@@ -1056,16 +1087,15 @@ function advanceOrchestration(taskRef, options = {}) {
1056
1087
  const result = consumeDelegation(run.pendingDelegation, { now: options.now });
1057
1088
  if (!result.ok)
1058
1089
  return pauseOrchestration(taskRef, result.code, result.message, true, options);
1059
- const completed = result.receipt.stage === 'commit';
1090
+ if (result.receipt.stage === 'commit') {
1091
+ return failed('ORCHESTRATED_COMMIT_REMOVED', 'Orchestrated commit is no longer a lifecycle stage; use local checkpoint delivery from code-task', resolved.taskId);
1092
+ }
1060
1093
  const updated = withUpdatedRun(run, {
1061
- status: completed ? 'completed' : 'running',
1094
+ status: 'running',
1062
1095
  nextStage: null,
1063
1096
  stepCount: run.stepCount + 1,
1064
1097
  pendingDelegation: null,
1065
- receipts: Object.freeze([...run.receipts, result.receipt]),
1066
- commitAuthorization: completed
1067
- ? { ...run.commitAuthorization, consumedAt: (options.now ?? (() => new Date().toISOString()))() }
1068
- : run.commitAuthorization
1098
+ receipts: Object.freeze([...run.receipts, result.receipt])
1069
1099
  });
1070
1100
  saveRun(resolved.taskDir, updated);
1071
1101
  return { status: updated.status, changed: true, taskId: resolved.taskId, run: updated, next: null, error: null };