@fitlab-ai/agent-infra 0.9.11 → 0.9.13

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 (356) hide show
  1. package/README.md +13 -13
  2. package/README.zh-CN.md +13 -13
  3. package/dist/bin/internal-cli.js +29 -19
  4. package/dist/lib/agent-clients/adapters/codex-lifecycle/build-identity.js +71 -10
  5. package/dist/lib/agent-clients/next-steps.js +6 -1
  6. package/dist/lib/decide.js +3 -3
  7. package/dist/lib/defaults.json +4 -0
  8. package/dist/lib/git/workflow.js +43 -1
  9. package/dist/lib/init.js +1 -0
  10. package/dist/lib/internal/git-workflow.js +8 -1
  11. package/dist/lib/internal/platform-checks.js +3 -3
  12. package/dist/lib/internal/platform-comment.js +9 -9
  13. package/dist/lib/internal/platform-context.js +2 -2
  14. package/dist/lib/internal/platform-issue.js +8 -9
  15. package/dist/lib/internal/platform-pr-review.js +10 -10
  16. package/dist/lib/internal/platform-pr.js +68 -23
  17. package/dist/lib/internal/platform-release-notes.js +3 -3
  18. package/dist/lib/internal/pr-review-grade.js +11 -9
  19. package/dist/lib/internal/release-workflow.js +4 -4
  20. package/dist/lib/internal/sandbox-control.js +30 -4
  21. package/dist/lib/internal/task-activity.js +3 -3
  22. package/dist/lib/internal/task-artifact.js +76 -24
  23. package/dist/lib/internal/task-create.js +2 -2
  24. package/dist/lib/internal/task-delivery.js +59 -0
  25. package/dist/lib/internal/task-event.js +6 -3
  26. package/dist/lib/internal/task-finalization.js +2 -2
  27. package/dist/lib/internal/task-invalidation.js +60 -0
  28. package/dist/lib/internal/task-ledger.js +9 -6
  29. package/dist/lib/internal/task-lifecycle.js +2 -2
  30. package/dist/lib/internal/task-override.js +4 -4
  31. package/dist/lib/internal/task-review.js +3 -3
  32. package/dist/lib/internal/task-verify.js +6 -6
  33. package/dist/lib/internal/task-warning.js +3 -3
  34. package/dist/lib/platform/adapters.js +1 -3
  35. package/dist/lib/platform/change-request-git-evidence.js +27 -7
  36. package/dist/lib/platform/completion-backfill.js +6 -5
  37. package/dist/lib/platform/context.js +70 -130
  38. package/dist/lib/platform/github-client.js +8 -2
  39. package/dist/lib/platform/github-provider.js +903 -0
  40. package/dist/lib/platform/in-label-sync.js +143 -0
  41. package/dist/lib/platform/issue-comments.js +94 -41
  42. package/dist/lib/platform/issues.js +335 -189
  43. package/dist/lib/platform/metadata-labels.js +6 -2
  44. package/dist/lib/platform/none-provider.js +28 -0
  45. package/dist/lib/platform/pr-change-report.js +551 -0
  46. package/dist/lib/platform/pr-checks.js +124 -106
  47. package/dist/lib/platform/pr-review.js +88 -72
  48. package/dist/lib/platform/pr-summary.js +523 -49
  49. package/dist/lib/platform/provider-bridge.js +45 -0
  50. package/dist/lib/platform/provider-contract.d.ts +487 -0
  51. package/dist/lib/platform/provider-contract.js +95 -0
  52. package/dist/lib/platform/provider-loader.js +251 -0
  53. package/dist/lib/platform/provider-validation.js +504 -0
  54. package/dist/lib/platform/pull-request-metadata.js +8 -6
  55. package/dist/lib/platform/pull-requests.js +1050 -502
  56. package/dist/lib/platform/release-notes.js +78 -55
  57. package/dist/lib/platform/releases.js +79 -50
  58. package/dist/lib/platform/resource-identity.d.ts +26 -0
  59. package/dist/lib/platform/resource-identity.js +92 -0
  60. package/dist/lib/platform/task-identities.js +31 -0
  61. package/dist/lib/platform/verification-required.js +22 -12
  62. package/dist/lib/platform/verification-sync.js +118 -239
  63. package/dist/lib/pr-review/evidence-grading.js +30 -9
  64. package/dist/lib/run/index.js +63 -23
  65. package/dist/lib/sandbox/config.js +5 -0
  66. package/dist/lib/sandbox/control/client.js +96 -15
  67. package/dist/lib/sandbox/control/executor.js +3 -3
  68. package/dist/lib/sandbox/control/lifecycle.js +29 -7
  69. package/dist/lib/sandbox/control/protocol.js +77 -0
  70. package/dist/lib/sandbox/control/server.js +398 -25
  71. package/dist/lib/sandbox/control/state.js +204 -1
  72. package/dist/lib/server/process-state.js +2 -2
  73. package/dist/lib/server/protocol.js +38 -1
  74. package/dist/lib/task/activity-log.js +8 -1
  75. package/dist/lib/task/artifact-lifecycle.js +35 -7
  76. package/dist/lib/task/capabilities.js +239 -0
  77. package/dist/lib/task/commands/status.js +32 -0
  78. package/dist/lib/task/commit-intent.js +100 -0
  79. package/dist/lib/task/commit-operation.js +207 -102
  80. package/dist/lib/task/control-authority.js +8 -5
  81. package/dist/lib/task/create-service.js +8 -8
  82. package/dist/lib/task/create.js +33 -7
  83. package/dist/lib/task/current-contract.js +5 -1
  84. package/dist/lib/task/decision-details.js +3 -48
  85. package/dist/lib/task/delivery-target.js +97 -0
  86. package/dist/lib/task/delivery.js +153 -0
  87. package/dist/lib/task/events.js +297 -17
  88. package/dist/lib/task/finalization.js +37 -13
  89. package/dist/lib/task/human-override.js +61 -36
  90. package/dist/lib/task/invalidation-command.js +86 -0
  91. package/dist/lib/task/invalidation.js +260 -0
  92. package/dist/lib/task/ledger-intents.js +37 -0
  93. package/dist/lib/task/lifecycle.js +106 -9
  94. package/dist/lib/task/local-artifact-finalization.js +374 -0
  95. package/dist/lib/task/orchestration.js +100 -151
  96. package/dist/lib/task/pr-delivery-fact.js +183 -0
  97. package/dist/lib/task/review-finalization.js +4 -8
  98. package/dist/lib/task/review-fingerprint.js +32 -2
  99. package/dist/lib/task/rework-intent.js +138 -0
  100. package/dist/lib/task/skill-scope.js +23 -0
  101. package/dist/lib/task/task-execution-lock.js +27 -17
  102. package/dist/lib/task/verification-engine.js +89 -38
  103. package/dist/lib/task/verification-review.js +1 -1
  104. package/dist/lib/task/verification.js +3 -3
  105. package/dist/lib/task/workflow-warning-intents.js +1 -1
  106. package/dist/lib/task/write.js +11 -0
  107. package/dist/lib/update.js +9 -0
  108. package/dist/lib/version.js +11 -1
  109. package/dist/lifecycle-build-manifest.json +3 -3
  110. package/lib/agent-clients/adapters/codex-lifecycle/build-identity.ts +105 -10
  111. package/lib/agent-clients/adapters/codex-lifecycle/manifest-files.json +12 -1
  112. package/lib/agent-clients/next-steps.ts +6 -1
  113. package/lib/decide.ts +3 -3
  114. package/lib/defaults.json +4 -0
  115. package/lib/git/workflow.ts +38 -1
  116. package/lib/init.ts +4 -1
  117. package/lib/internal/git-workflow.ts +7 -1
  118. package/lib/internal/platform-checks.ts +3 -3
  119. package/lib/internal/platform-comment.ts +8 -8
  120. package/lib/internal/platform-context.ts +2 -2
  121. package/lib/internal/platform-issue.ts +7 -8
  122. package/lib/internal/platform-pr-review.ts +9 -9
  123. package/lib/internal/platform-pr.ts +53 -21
  124. package/lib/internal/platform-release-notes.ts +3 -3
  125. package/lib/internal/pr-review-grade.ts +10 -8
  126. package/lib/internal/release-workflow.ts +4 -4
  127. package/lib/internal/sandbox-control.ts +27 -3
  128. package/lib/internal/task-activity.ts +3 -3
  129. package/lib/internal/task-artifact.ts +65 -22
  130. package/lib/internal/task-create.ts +2 -2
  131. package/lib/internal/task-delivery.ts +53 -0
  132. package/lib/internal/task-event.ts +6 -3
  133. package/lib/internal/task-finalization.ts +3 -3
  134. package/lib/internal/task-invalidation.ts +39 -0
  135. package/lib/internal/task-ledger.ts +11 -8
  136. package/lib/internal/task-lifecycle.ts +2 -2
  137. package/lib/internal/task-override.ts +6 -6
  138. package/lib/internal/task-review.ts +3 -3
  139. package/lib/internal/task-verify.ts +6 -6
  140. package/lib/internal/task-warning.ts +3 -3
  141. package/lib/platform/adapters.ts +3 -2
  142. package/lib/platform/change-request-git-evidence.ts +36 -9
  143. package/lib/platform/completion-backfill.ts +9 -8
  144. package/lib/platform/context.ts +97 -134
  145. package/lib/platform/github-client.ts +10 -3
  146. package/lib/platform/github-provider.ts +910 -0
  147. package/lib/platform/in-label-sync.ts +208 -0
  148. package/lib/platform/issue-comments.ts +97 -47
  149. package/lib/platform/issues.ts +344 -174
  150. package/lib/platform/metadata-labels.ts +5 -2
  151. package/lib/platform/none-provider.ts +35 -0
  152. package/lib/platform/pr-change-report.ts +645 -0
  153. package/lib/platform/pr-checks.ts +129 -107
  154. package/lib/platform/pr-review.ts +88 -85
  155. package/lib/platform/pr-summary.ts +562 -52
  156. package/lib/platform/provider-bridge.ts +82 -0
  157. package/lib/platform/provider-contract.ts +536 -0
  158. package/lib/platform/provider-loader.ts +307 -0
  159. package/lib/platform/provider-validation.ts +529 -0
  160. package/lib/platform/pull-request-metadata.ts +10 -7
  161. package/lib/platform/pull-requests.ts +1126 -505
  162. package/lib/platform/release-notes.ts +76 -59
  163. package/lib/platform/releases.ts +77 -43
  164. package/lib/platform/resource-identity.ts +121 -0
  165. package/lib/platform/task-identities.ts +40 -0
  166. package/lib/platform/types.ts +4 -2
  167. package/lib/platform/verification-required.ts +21 -13
  168. package/lib/platform/verification-sync.ts +118 -293
  169. package/lib/pr-review/evidence-grading.ts +31 -11
  170. package/lib/run/index.ts +57 -22
  171. package/lib/sandbox/config.ts +7 -0
  172. package/lib/sandbox/control/client.ts +105 -15
  173. package/lib/sandbox/control/executor.ts +4 -4
  174. package/lib/sandbox/control/lifecycle.ts +30 -8
  175. package/lib/sandbox/control/protocol.ts +113 -0
  176. package/lib/sandbox/control/server.ts +431 -23
  177. package/lib/sandbox/control/state.ts +251 -1
  178. package/lib/server/process-state.ts +2 -2
  179. package/lib/server/protocol.ts +36 -1
  180. package/lib/task/activity-log.ts +8 -0
  181. package/lib/task/artifact-lifecycle.ts +34 -8
  182. package/lib/task/capabilities.ts +262 -0
  183. package/lib/task/commands/status.ts +38 -0
  184. package/lib/task/commit-intent.ts +137 -0
  185. package/lib/task/commit-operation.ts +186 -88
  186. package/lib/task/control-authority.ts +11 -8
  187. package/lib/task/create-service.ts +8 -8
  188. package/lib/task/create.ts +34 -9
  189. package/lib/task/current-contract.ts +6 -1
  190. package/lib/task/decision-details.ts +8 -64
  191. package/lib/task/delivery-target.ts +130 -0
  192. package/lib/task/delivery.ts +160 -0
  193. package/lib/task/events.ts +296 -16
  194. package/lib/task/finalization.ts +50 -14
  195. package/lib/task/human-override.ts +73 -46
  196. package/lib/task/invalidation-command.ts +86 -0
  197. package/lib/task/invalidation.ts +332 -0
  198. package/lib/task/ledger-intents.ts +35 -1
  199. package/lib/task/lifecycle.ts +93 -4
  200. package/lib/task/local-artifact-finalization.ts +563 -0
  201. package/lib/task/orchestration.ts +110 -146
  202. package/lib/task/pr-delivery-fact.ts +237 -0
  203. package/lib/task/review-finalization.ts +4 -14
  204. package/lib/task/review-fingerprint.ts +33 -2
  205. package/lib/task/rework-intent.ts +165 -0
  206. package/lib/task/skill-scope.ts +24 -0
  207. package/lib/task/task-execution-lock.ts +23 -14
  208. package/lib/task/verification-engine.ts +95 -43
  209. package/lib/task/verification-review.ts +2 -0
  210. package/lib/task/verification.ts +4 -4
  211. package/lib/task/workflow-warning-intents.ts +1 -1
  212. package/lib/task/write.ts +19 -0
  213. package/lib/update.ts +11 -1
  214. package/lib/version.ts +15 -1
  215. package/package.json +7 -1
  216. package/templates/.agents/rules/README.en.md +4 -2
  217. package/templates/.agents/rules/README.zh-CN.md +4 -2
  218. package/templates/.agents/rules/create-issue.en.md +11 -2
  219. package/templates/.agents/rules/create-issue.zh-CN.md +24 -2
  220. package/templates/.agents/rules/issue-pr-commands.en.md +4 -17
  221. package/templates/.agents/rules/issue-pr-commands.zh-CN.md +1 -1
  222. package/templates/.agents/rules/issue-sync.en.md +28 -5
  223. package/templates/.agents/rules/issue-sync.zh-CN.md +64 -5
  224. package/templates/.agents/rules/label-milestone-setup.en.md +15 -2
  225. package/templates/.agents/rules/label-milestone-setup.zh-CN.md +15 -2
  226. package/templates/.agents/rules/local-artifact-repair.en.md +53 -0
  227. package/templates/.agents/rules/local-artifact-repair.zh-CN.md +53 -0
  228. package/templates/.agents/rules/milestone-inference.en.md +12 -2
  229. package/templates/.agents/rules/milestone-inference.zh-CN.md +25 -3
  230. package/templates/.agents/rules/pr-sync.en.md +28 -13
  231. package/templates/.agents/rules/pr-sync.zh-CN.md +39 -5
  232. package/templates/.agents/rules/release-commands.en.md +42 -3
  233. package/templates/.agents/rules/release-commands.zh-CN.md +42 -3
  234. package/templates/.agents/rules/security-alerts.en.md +17 -2
  235. package/templates/.agents/rules/security-alerts.zh-CN.md +17 -2
  236. package/templates/.agents/rules/sync-content-generation.en.md +8 -0
  237. package/templates/.agents/rules/sync-content-generation.zh-CN.md +8 -0
  238. package/templates/.agents/rules/task-management.en.md +1 -1
  239. package/templates/.agents/rules/task-management.zh-CN.md +1 -1
  240. package/templates/.agents/rules/task-short-id.en.md +1 -4
  241. package/templates/.agents/rules/task-short-id.zh-CN.md +1 -3
  242. package/templates/.agents/scripts/security-alerts.github.sh +151 -0
  243. package/templates/.agents/scripts/security-alerts.sh +31 -0
  244. package/templates/.agents/skills/analyze-task/SKILL.en.md +14 -3
  245. package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +11 -3
  246. package/templates/.agents/skills/check-task/SKILL.en.md +16 -37
  247. package/templates/.agents/skills/check-task/SKILL.zh-CN.md +16 -37
  248. package/templates/.agents/skills/close-codescan/SKILL.en.md +2 -2
  249. package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +2 -2
  250. package/templates/.agents/skills/close-dependabot/SKILL.en.md +2 -2
  251. package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +2 -2
  252. package/templates/.agents/skills/code-task/SKILL.en.md +32 -10
  253. package/templates/.agents/skills/code-task/SKILL.zh-CN.md +35 -13
  254. package/templates/.agents/skills/code-task/config/verify.en.json +1 -0
  255. package/templates/.agents/skills/code-task/config/verify.zh-CN.json +1 -0
  256. package/templates/.agents/skills/commit/SKILL.en.md +3 -3
  257. package/templates/.agents/skills/commit/SKILL.zh-CN.md +3 -3
  258. package/templates/.agents/skills/commit/config/verify.json +1 -1
  259. package/templates/.agents/skills/commit/reference/commit-orchestration.en.md +3 -3
  260. package/templates/.agents/skills/commit/reference/commit-orchestration.zh-CN.md +3 -3
  261. package/templates/.agents/skills/commit/reference/issue-metadata-sync.en.md +2 -0
  262. package/templates/.agents/skills/commit/reference/issue-metadata-sync.zh-CN.md +2 -0
  263. package/templates/.agents/skills/commit/reference/pr-summary-sync.en.md +4 -4
  264. package/templates/.agents/skills/commit/reference/pr-summary-sync.zh-CN.md +4 -4
  265. package/templates/.agents/skills/commit/reference/task-status-update.en.md +2 -2
  266. package/templates/.agents/skills/commit/reference/task-status-update.zh-CN.md +2 -2
  267. package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +7 -4
  268. package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +7 -4
  269. package/templates/.agents/skills/complete-manual-validation/config/verify.en.json +1 -1
  270. package/templates/.agents/skills/complete-manual-validation/config/verify.zh-CN.json +1 -1
  271. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.en.md +5 -4
  272. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.zh-CN.md +5 -4
  273. package/templates/.agents/skills/complete-task/SKILL.en.md +31 -13
  274. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +30 -13
  275. package/templates/.agents/skills/complete-task/reference/external-delivery.en.md +2 -2
  276. package/templates/.agents/skills/complete-task/reference/external-delivery.zh-CN.md +2 -2
  277. package/templates/.agents/skills/create-pr/SKILL.en.md +11 -10
  278. package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +11 -11
  279. package/templates/.agents/skills/create-pr/config/verify.json +1 -0
  280. package/templates/.agents/skills/create-pr/reference/change-report.en.md +16 -4
  281. package/templates/.agents/skills/create-pr/reference/change-report.zh-CN.md +16 -4
  282. package/templates/.agents/skills/create-pr/reference/comment-publish.en.md +5 -3
  283. package/templates/.agents/skills/create-pr/reference/comment-publish.zh-CN.md +5 -3
  284. package/templates/.agents/skills/create-pr/reference/pr-body-template.en.md +1 -1
  285. package/templates/.agents/skills/create-pr/reference/pr-body-template.zh-CN.md +1 -1
  286. package/templates/.agents/skills/create-pr/scripts/change-report.mjs +3 -0
  287. package/templates/.agents/skills/create-task/SKILL.en.md +1 -0
  288. package/templates/.agents/skills/create-task/SKILL.zh-CN.md +1 -0
  289. package/templates/.agents/skills/import-codescan/SKILL.en.md +1 -1
  290. package/templates/.agents/skills/import-codescan/SKILL.zh-CN.md +1 -1
  291. package/templates/.agents/skills/import-dependabot/SKILL.en.md +1 -1
  292. package/templates/.agents/skills/import-dependabot/SKILL.zh-CN.md +1 -1
  293. package/templates/.agents/skills/init-labels/SKILL.en.md +9 -10
  294. package/templates/.agents/skills/init-labels/SKILL.zh-CN.md +9 -10
  295. package/templates/.agents/skills/init-labels/scripts/init-labels.github.sh +57 -1
  296. package/templates/.agents/skills/init-labels/scripts/init-labels.sh +27 -1
  297. package/templates/.agents/skills/init-milestones/SKILL.en.md +3 -5
  298. package/templates/.agents/skills/init-milestones/SKILL.zh-CN.md +3 -5
  299. package/templates/.agents/skills/plan-task/SKILL.en.md +14 -3
  300. package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +11 -3
  301. package/templates/.agents/skills/review-analysis/SKILL.en.md +6 -5
  302. package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +6 -5
  303. package/templates/.agents/skills/review-analysis/reference/output-templates.en.md +18 -0
  304. package/templates/.agents/skills/review-analysis/reference/output-templates.zh-CN.md +18 -0
  305. package/templates/.agents/skills/review-code/SKILL.en.md +10 -9
  306. package/templates/.agents/skills/review-code/SKILL.zh-CN.md +10 -9
  307. package/templates/.agents/skills/review-code/reference/output-templates.en.md +19 -1
  308. package/templates/.agents/skills/review-code/reference/output-templates.zh-CN.md +19 -1
  309. package/templates/.agents/skills/review-code/reference/report-template.en.md +4 -2
  310. package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +4 -2
  311. package/templates/.agents/skills/review-plan/SKILL.en.md +6 -5
  312. package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +6 -5
  313. package/templates/.agents/skills/review-plan/reference/output-templates.en.md +18 -0
  314. package/templates/.agents/skills/review-plan/reference/output-templates.zh-CN.md +18 -0
  315. package/templates/.agents/skills/review-pr/SKILL.en.md +1 -0
  316. package/templates/.agents/skills/review-pr/SKILL.zh-CN.md +1 -0
  317. package/templates/.agents/skills/review-pr/reference/evidence-grading.en.md +2 -2
  318. package/templates/.agents/skills/review-pr/reference/evidence-grading.zh-CN.md +2 -2
  319. package/templates/.agents/skills/run-manual-validation/SKILL.en.md +5 -2
  320. package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +5 -2
  321. package/templates/.agents/skills/run-task/SKILL.en.md +1 -1
  322. package/templates/.agents/skills/run-task/SKILL.zh-CN.md +1 -1
  323. package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +4 -0
  324. package/templates/.agents/skills/watch-pr/SKILL.en.md +6 -6
  325. package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +6 -6
  326. package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.en.md +6 -0
  327. package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.zh-CN.md +6 -0
  328. package/templates/.agents/templates/task.en.md +23 -2
  329. package/templates/.agents/templates/task.zh-CN.md +23 -2
  330. package/templates/.agents/workflows/bug-fix.en.yaml +9 -8
  331. package/templates/.agents/workflows/bug-fix.zh-CN.yaml +9 -8
  332. package/templates/.agents/workflows/feature-development.en.yaml +9 -8
  333. package/templates/.agents/workflows/feature-development.zh-CN.yaml +9 -8
  334. package/templates/.agents/workflows/refactoring.en.yaml +9 -8
  335. package/templates/.agents/workflows/refactoring.zh-CN.yaml +9 -8
  336. package/templates/.github/workflows/pr-label.yml +7 -26
  337. package/templates/.agents/rules/create-issue.github.en.md +0 -14
  338. package/templates/.agents/rules/create-issue.github.zh-CN.md +0 -27
  339. package/templates/.agents/rules/issue-pr-commands.github.en.md +0 -34
  340. package/templates/.agents/rules/issue-pr-commands.github.zh-CN.md +0 -49
  341. package/templates/.agents/rules/issue-sync.github.en.md +0 -40
  342. package/templates/.agents/rules/issue-sync.github.zh-CN.md +0 -76
  343. package/templates/.agents/rules/label-milestone-setup.github.en.md +0 -60
  344. package/templates/.agents/rules/label-milestone-setup.github.zh-CN.md +0 -60
  345. package/templates/.agents/rules/milestone-inference.github.en.md +0 -15
  346. package/templates/.agents/rules/milestone-inference.github.zh-CN.md +0 -27
  347. package/templates/.agents/rules/pr-checks-commands.github.en.md +0 -18
  348. package/templates/.agents/rules/pr-checks-commands.github.zh-CN.md +0 -34
  349. package/templates/.agents/rules/pr-sync.github.en.md +0 -43
  350. package/templates/.agents/rules/pr-sync.github.zh-CN.md +0 -69
  351. package/templates/.agents/rules/release-commands.github.en.md +0 -44
  352. package/templates/.agents/rules/release-commands.github.zh-CN.md +0 -44
  353. package/templates/.agents/rules/security-alerts.github.en.md +0 -43
  354. package/templates/.agents/rules/security-alerts.github.zh-CN.md +0 -43
  355. /package/templates/.agents/rules/{issue-fields.github.en.md → issue-fields.en.md} +0 -0
  356. /package/templates/.agents/rules/{issue-fields.github.zh-CN.md → issue-fields.zh-CN.md} +0 -0
@@ -3,20 +3,23 @@ import path from 'node:path';
3
3
  import { randomUUID } from 'node:crypto';
4
4
  import { parseTypedTaskFrontmatter } from './frontmatter.js';
5
5
  import { validateCurrentTaskContract } from './current-contract.js';
6
- import { parseReviewSummary, resolveCanonicalVerdict } from './review-artifacts.js';
7
- import { inspectArtifactDirectory } from './artifact-lifecycle.js';
6
+ import { parseReviewSummary } from './review-artifacts.js';
8
7
  import { LEDGER_SECTION_MISSING_CODE, LEDGER_SECTION_MISSING_MESSAGE, parseLedgerDocument, summarizeLedgerStage, validateLedgerRows } from './ledger.js';
9
8
  import { resolveTaskRef } from './resolve-ref.js';
10
9
  import { activateDelegation, abortPreparedDelegation, completeDelegationStage, consumeDelegation, dispatchDelegation, foldBlankToNull, isDelegationReceipt, managedDelegationRole, prepareDelegation, sealDelegation } from './delegation-receipts.js';
11
10
  import { normalizeAgentClients } from '../agent-clients/config.js';
12
11
  import { resolveAgentRuntimeStoreRoot } from '../runtime/agent-runtime.js';
13
- import { inspectPlatformPullRequest } from '../platform/pull-requests.js';
14
12
  import { getAgentClientCapability, getAgentClientDelegationEvidence, getAgentClientModelSelection } from '../agent-clients/registry.js';
15
13
  import { isAgentClientId } from '../agent-clients/types.js';
16
14
  import { captureRepositorySnapshot, captureWorkspaceSnapshot, diffWorkspaceSnapshots } from './workspace-snapshot.js';
17
15
  import { assertGitRepositoryBinding } from '../git/worktree-identity.js';
18
16
  import { TaskExecutionLockError, withTaskExecutionLock } from './task-execution-lock.js';
19
17
  import { hasActiveCodexLifecycleEvidence } from '../agent-clients/adapters/codex-lifecycle/store.js';
18
+ import { extractReviewBaseline, extractReviewDiffBase, extractReviewTargetHead, extractReviewedHead } from './review-fingerprint.js';
19
+ import { resolveDeliveryTarget, resolveDiffBase, resolveTargetHead } from './delivery-target.js';
20
+ import { buildLifecycleFacts, canStart, recommendNext } from './capabilities.js';
21
+ import { hasOpenLifecycleExecution } from './activity-log.js';
22
+ import { invalidationBlocks } from './invalidation.js';
20
23
  function supportsLifecycleDelegation(client) {
21
24
  return getAgentClientCapability(client, 'subagents').level !== 'unsupported'
22
25
  && getAgentClientCapability(client, 'orchestration').level !== 'unsupported'
@@ -106,9 +109,8 @@ function isCompletionEvidence(value) {
106
109
  && exactText(value.headTree)
107
110
  && exactText(value.worktreeTree)
108
111
  && exactText(value.lastReviewedCommit)
109
- && Number.isSafeInteger(value.prNumber)
110
- && value.prNumber > 0
111
- && exactText(value.prHead);
112
+ && (value.prNumber === null || (Number.isSafeInteger(value.prNumber) && value.prNumber > 0))
113
+ && (value.prHead === null || exactText(value.prHead));
112
114
  }
113
115
  const ORCHESTRATION_RUN_KEYS = [
114
116
  'taskId', 'runId', 'status', 'nextStage', 'stepCount', 'maxSteps', 'modelPolicy',
@@ -340,86 +342,58 @@ function highestRound(taskDir, family) {
340
342
  function artifactName(family, round) {
341
343
  return round === 1 ? `${family}.md` : `${family}-r${round}.md`;
342
344
  }
343
- function hasReviewAfterArtifact(taskDir, family, reviewFamily) {
344
- const artifact = inspectArtifactDirectory(taskDir, family);
345
- const review = inspectArtifactDirectory(taskDir, reviewFamily);
346
- return artifact.status === 'ready'
347
- && review.status === 'ready'
348
- && artifact.latest !== null
349
- && review.reviewedInput?.name === artifact.latest.name;
350
- }
351
- function latestReviewApproved(taskDir, family) {
352
- const round = highestRound(taskDir, family);
353
- if (round === 0)
354
- return { approved: false, error: null };
355
- let content;
356
- try {
357
- content = fs.readFileSync(path.join(taskDir, artifactName(family, round)), 'utf8');
358
- }
359
- catch (error) {
360
- return { approved: false, error: String(error) };
361
- }
362
- const parsed = parseReviewSummary(content);
363
- if (!parsed.ok)
364
- return { approved: false, error: `${parsed.code}: ${parsed.message}` };
365
- const verdict = resolveCanonicalVerdict(parsed.summary);
366
- if (!verdict.ok)
367
- return { approved: false, error: `${verdict.code}: ${verdict.message}` };
368
- return { approved: verdict.verdict === 'Approved', error: null };
369
- }
370
- function routeFromFacts(taskDir) {
371
- const analysisRound = highestRound(taskDir, 'analysis');
372
- const analysisReviewRound = highestRound(taskDir, 'review-analysis');
373
- const planRound = highestRound(taskDir, 'plan');
374
- const planReviewRound = highestRound(taskDir, 'review-plan');
375
- const codeRound = highestRound(taskDir, 'code');
376
- const codeReviewRound = highestRound(taskDir, 'review-code');
377
- if (analysisRound === 0) {
378
- const round = analysisRound + 1;
379
- return { action: 'analyze-task', role: 'executor', stage: 'analysis', round, artifact: artifactName('analysis', round) };
380
- }
381
- if (!hasReviewAfterArtifact(taskDir, 'analysis', 'review-analysis')) {
382
- const round = analysisReviewRound + 1;
383
- return { action: 'review-analysis', role: 'reviewer', stage: 'review-analysis', round, artifact: artifactName('review-analysis', round) };
384
- }
385
- const analysisVerdict = latestReviewApproved(taskDir, 'review-analysis');
386
- if (analysisVerdict.error)
387
- return { error: analysisVerdict.error };
388
- if (!analysisVerdict.approved) {
389
- const round = analysisRound + 1;
390
- return { action: 'analyze-task', role: 'executor', stage: 'analysis', round, artifact: artifactName('analysis', round) };
391
- }
392
- if (planRound === 0) {
393
- const round = planRound + 1;
394
- return { action: 'plan-task', role: 'executor', stage: 'plan', round, artifact: artifactName('plan', round) };
395
- }
396
- if (!hasReviewAfterArtifact(taskDir, 'plan', 'review-plan')) {
397
- const round = planReviewRound + 1;
398
- return { action: 'review-plan', role: 'reviewer', stage: 'review-plan', round, artifact: artifactName('review-plan', round) };
399
- }
400
- const planVerdict = latestReviewApproved(taskDir, 'review-plan');
401
- if (planVerdict.error)
402
- return { error: planVerdict.error };
403
- if (!planVerdict.approved) {
404
- const round = planRound + 1;
405
- return { action: 'plan-task', role: 'executor', stage: 'plan', round, artifact: artifactName('plan', round) };
345
+ function validateSavedCurrentDiffBase(repoRoot, metadata, taskDir, reviewedHead) {
346
+ const reviewRound = highestRound(taskDir, 'review-code');
347
+ const reviewContent = fs.readFileSync(path.join(taskDir, artifactName('review-code', reviewRound)), 'utf8');
348
+ const savedTargetHead = extractReviewTargetHead(reviewContent);
349
+ const savedDiffBase = extractReviewDiffBase(reviewContent);
350
+ const savedReviewedHead = extractReviewedHead(reviewContent) || extractReviewBaseline(reviewContent);
351
+ if (!savedTargetHead && !savedDiffBase && !savedReviewedHead)
352
+ return null;
353
+ if (savedReviewedHead && savedReviewedHead !== reviewedHead) {
354
+ return { code: 'ORCHESTRATION_REVIEWED_HEAD_MISMATCH', message: 'review-code saved reviewed head does not match last_reviewed_commit' };
406
355
  }
407
- if (codeRound === 0) {
408
- const round = codeRound + 1;
409
- return { action: 'code-task', role: 'executor', stage: 'code', round, artifact: artifactName('code', round) };
356
+ if (!savedTargetHead || !savedDiffBase) {
357
+ return { code: 'ORCHESTRATION_REVIEW_TARGET_INVALID', message: 'review-code must save target head and diff base together' };
410
358
  }
411
- if (!hasReviewAfterArtifact(taskDir, 'code', 'review-code')) {
412
- const round = codeReviewRound + 1;
413
- return { action: 'review-code', role: 'reviewer', stage: 'review-code', round, artifact: artifactName('review-code', round) };
359
+ const savedBase = resolveDiffBase(repoRoot, reviewedHead, savedTargetHead);
360
+ if (!savedBase.ok)
361
+ return { code: 'ORCHESTRATION_REVIEW_TARGET_INVALID', message: savedBase.message };
362
+ if (savedBase.diffBase !== savedDiffBase) {
363
+ return { code: 'ORCHESTRATION_REVIEW_DIFF_BASE_MISMATCH', message: 'saved review diff base does not match reviewed head and saved target head' };
414
364
  }
415
- const codeVerdict = latestReviewApproved(taskDir, 'review-code');
416
- if (codeVerdict.error)
417
- return { error: codeVerdict.error };
418
- if (!codeVerdict.approved) {
419
- const round = codeRound + 1;
420
- return { action: 'code-task', role: 'executor', stage: 'code', round, artifact: artifactName('code', round) };
365
+ const target = resolveDeliveryTarget(repoRoot, {
366
+ remote: typeof metadata.delivery_remote === 'string' ? metadata.delivery_remote : undefined,
367
+ baseRef: typeof metadata.delivery_base_ref === 'string' ? metadata.delivery_base_ref : undefined
368
+ });
369
+ if (!target.ok)
370
+ return { code: target.code, message: target.message };
371
+ const currentTarget = resolveTargetHead(repoRoot, target.value);
372
+ if (!currentTarget.ok)
373
+ return { code: currentTarget.code, message: currentTarget.message };
374
+ const currentBase = resolveDiffBase(repoRoot, reviewedHead, currentTarget.head);
375
+ if (!currentBase.ok)
376
+ return { code: currentBase.code, message: currentBase.message };
377
+ if (currentBase.diffBase !== savedDiffBase) {
378
+ return { code: 'ORCHESTRATION_REVIEW_TARGET_CHANGED', message: 'current delivery target changed the reviewed diff base; re-run review-code' };
421
379
  }
422
- return { action: 'commit', role: 'executor', stage: 'commit', round: 1, artifact: 'commit' };
380
+ return null;
381
+ }
382
+ function routeFromFacts(facts) {
383
+ const recommendation = recommendNext(facts);
384
+ if (!recommendation.action)
385
+ return { completion: true };
386
+ const action = recommendation.action;
387
+ if (action === 'manual-validation' || action === 'validation-run')
388
+ return null;
389
+ const family = action === 'analysis' ? 'analysis' : action;
390
+ const round = Math.max(0, ...[...(facts.artifacts[family] ?? []), ...(facts.staleArtifacts?.[family] ?? [])].map((name) => {
391
+ const match = /-r(\d+)\.md$/.exec(name);
392
+ return match ? Number(match[1]) : 1;
393
+ })) + 1;
394
+ const command = action === 'analysis' ? 'analyze-task' : action === 'plan' ? 'plan-task' : action === 'code' ? 'code-task' : action;
395
+ const role = action.startsWith('review-') ? 'reviewer' : 'executor';
396
+ return { action: command, role, stage: action, round, artifact: artifactName(family, round) };
423
397
  }
424
398
  function routeOrchestration(taskRef, options = {}) {
425
399
  const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
@@ -441,12 +415,14 @@ function routeOrchestration(taskRef, options = {}) {
441
415
  }
442
416
  let metadata;
443
417
  let currentLedger = null;
418
+ let currentInvalidation = { operations: [], targets: [] };
444
419
  if (resolved.state === 'active') {
445
420
  const contract = validateCurrentTaskContract(content);
446
421
  if (!contract.ok)
447
422
  return failed('ORCHESTRATION_CURRENT_CONTRACT_INVALID', contract.message, resolved.taskId);
448
423
  metadata = contract.metadata;
449
424
  currentLedger = contract.ledger;
425
+ currentInvalidation = contract.invalidation;
450
426
  }
451
427
  else {
452
428
  try {
@@ -456,19 +432,23 @@ function routeOrchestration(taskRef, options = {}) {
456
432
  return failed('ORCHESTRATION_TASK_INVALID', error instanceof Error ? error.message : String(error), resolved.taskId);
457
433
  }
458
434
  }
459
- const routed = routeFromFacts(resolved.taskDir);
460
- if (routed && 'error' in routed)
461
- return failed('ORCHESTRATION_REVIEW_INVALID', routed.error, resolved.taskId);
435
+ if (invalidationBlocks(currentInvalidation)) {
436
+ return failed('ORCHESTRATION_INVALIDATION_INCOMPLETE', 'artifact invalidation is incomplete; reconcile task.md before routing downstream work', resolved.taskId);
437
+ }
438
+ let run;
439
+ try {
440
+ run = readRun(resolved.taskDir);
441
+ }
442
+ catch (error) {
443
+ return failed('ORCHESTRATION_STATE_INVALID', error instanceof Error ? error.message : String(error), resolved.taskId);
444
+ }
445
+ const facts = buildLifecycleFacts(resolved.taskDir, content, resolved.state, hasOpenLifecycleExecution(content) || Boolean(run?.pendingDelegation));
446
+ if (!facts.ok)
447
+ return failed('ORCHESTRATION_CAPABILITY_FACTS_INVALID', facts.message, resolved.taskId);
448
+ const routed = routeFromFacts(facts.facts);
462
449
  if (!routed)
463
450
  return failed('ORCHESTRATION_ROUTE_UNKNOWN', 'cannot determine a unique lifecycle action', resolved.taskId);
464
- const run = readRun(resolved.taskDir);
465
- const policy = run ? rolePolicy(run, routed.role) : null;
466
- const next = {
467
- ...routed,
468
- requestedModel: policy?.model ?? null,
469
- requestedReasoningEffort: policy?.reasoningEffort ?? null
470
- };
471
- if (next.stage === 'commit') {
451
+ if ('completion' in routed) {
472
452
  const reviewRound = highestRound(resolved.taskDir, 'review-code');
473
453
  const review = parseReviewSummary(fs.readFileSync(path.join(resolved.taskDir, artifactName('review-code', reviewRound)), 'utf8'));
474
454
  if (!review.ok || review.summary.manualValidation === null) {
@@ -487,29 +467,8 @@ function routeOrchestration(taskRef, options = {}) {
487
467
  if (run?.status === 'completed') {
488
468
  return { status: 'completed', changed: false, taskId: resolved.taskId, run, next: null, error: null };
489
469
  }
490
- let config;
491
- try {
492
- config = JSON.parse(fs.readFileSync(path.join(resolved.repoRoot, '.agents', '.airc.json'), 'utf8'));
493
- }
494
- catch (error) {
495
- return failed('ORCHESTRATION_CONFIG_INVALID', error instanceof Error ? error.message : String(error), resolved.taskId);
496
- }
497
- if (!config || typeof config !== 'object' || Array.isArray(config)) {
498
- return failed('ORCHESTRATION_CONFIG_INVALID', 'project configuration must be an object', resolved.taskId);
499
- }
500
- const projectConfig = config;
501
- if (!Object.hasOwn(projectConfig, 'prFlow') || projectConfig.prFlow === 'disabled') {
502
- return { status: 'running', changed: false, taskId: resolved.taskId, run, next, error: null };
503
- }
504
- if (projectConfig.prFlow !== 'required') {
505
- return failed('ORCHESTRATION_CONFIG_INVALID', "project configuration 'prFlow' must be 'required' or 'disabled'", resolved.taskId);
506
- }
507
- const prNumber = Number(metadata.pr_number);
508
- if (!Number.isInteger(prNumber) || prNumber <= 0) {
509
- return { status: 'running', changed: false, taskId: resolved.taskId, run, next, error: null };
510
- }
511
470
  if (!run) {
512
- return { status: 'running', changed: false, taskId: resolved.taskId, run, next, error: null };
471
+ return { status: 'running', changed: false, taskId: resolved.taskId, run, next: null, error: null };
513
472
  }
514
473
  if (run.status !== 'running' || run.pendingDelegation !== null) {
515
474
  return failed('ORCHESTRATION_RUN_NOT_RUNNING', 'clean completion requires an idle running orchestration', resolved.taskId);
@@ -523,34 +482,15 @@ function routeOrchestration(taskRef, options = {}) {
523
482
  return failed('ORCHESTRATION_SNAPSHOT_FAILED', error instanceof Error ? error.message : String(error), resolved.taskId);
524
483
  }
525
484
  if (before.headTree !== before.worktreeTree) {
526
- return { status: 'running', changed: false, taskId: resolved.taskId, run, next, error: null };
485
+ return { status: 'running', changed: false, taskId: resolved.taskId, run, next: null, error: null };
527
486
  }
528
487
  const lastReviewedCommit = String(metadata.last_reviewed_commit ?? '');
529
488
  if (before.head !== lastReviewedCommit) {
530
489
  return failed('ORCHESTRATION_REVIEWED_HEAD_MISMATCH', 'local HEAD does not match last_reviewed_commit', resolved.taskId);
531
490
  }
532
- const inspect = options.inspectPullRequest ?? inspectPlatformPullRequest;
533
- let inspection;
534
- try {
535
- inspection = inspect(resolved.taskId, { cwd: gitRoot });
536
- }
537
- catch (error) {
538
- return failed('ORCHESTRATION_PR_INSPECTION_FAILED', error instanceof Error ? error.message : String(error), resolved.taskId);
539
- }
540
- if (inspection.status === 'blocked') {
541
- return failed('ORCHESTRATION_PR_INSPECTION_BLOCKED', inspection.error?.message ?? 'pull request inspection is blocked', resolved.taskId);
542
- }
543
- if (inspection.status === 'failed') {
544
- return failed('ORCHESTRATION_PR_INSPECTION_FAILED', inspection.error?.message ?? 'pull request inspection failed', resolved.taskId);
545
- }
546
- const inspectedPrNumber = inspection.task?.prNumber ?? inspection.prNumber;
547
- const prHead = inspection.pullRequest?.head?.sha;
548
- if (typeof prHead !== 'string') {
549
- return failed('ORCHESTRATION_PR_INSPECTION_INVALID', 'pull request inspection returned incomplete identity evidence', resolved.taskId);
550
- }
551
- if (inspectedPrNumber !== prNumber || prHead !== before.head) {
552
- return failed('ORCHESTRATION_PR_HEAD_MISMATCH', 'pull request identity does not match the reviewed local HEAD', resolved.taskId);
553
- }
491
+ const diffBaseError = validateSavedCurrentDiffBase(resolved.repoRoot, metadata, resolved.taskDir, lastReviewedCommit);
492
+ if (diffBaseError)
493
+ return failed(diffBaseError.code, diffBaseError.message, resolved.taskId);
554
494
  let after;
555
495
  try {
556
496
  after = capture(gitRoot);
@@ -559,7 +499,7 @@ function routeOrchestration(taskRef, options = {}) {
559
499
  return failed('ORCHESTRATION_SNAPSHOT_FAILED', error instanceof Error ? error.message : String(error), resolved.taskId);
560
500
  }
561
501
  if (after.headTree !== after.worktreeTree) {
562
- return { status: 'running', changed: false, taskId: resolved.taskId, run, next, error: null };
502
+ return { status: 'running', changed: false, taskId: resolved.taskId, run, next: null, error: null };
563
503
  }
564
504
  if (after.head !== before.head || after.headTree !== before.headTree) {
565
505
  return failed('ORCHESTRATION_REPOSITORY_CHANGED', 'repository changed while clean completion evidence was collected', resolved.taskId);
@@ -572,8 +512,8 @@ function routeOrchestration(taskRef, options = {}) {
572
512
  headTree: after.headTree,
573
513
  worktreeTree: after.worktreeTree,
574
514
  lastReviewedCommit,
575
- prNumber,
576
- prHead
515
+ prNumber: null,
516
+ prHead: null
577
517
  };
578
518
  const completed = withUpdatedRun(run, {
579
519
  status: 'completed',
@@ -584,6 +524,19 @@ function routeOrchestration(taskRef, options = {}) {
584
524
  saveRun(resolved.taskDir, completed);
585
525
  return { status: 'completed', changed: true, taskId: resolved.taskId, run: completed, next: null, error: null };
586
526
  }
527
+ const action = routed.stage;
528
+ const capability = canStart(action, facts.facts, {
529
+ initiator: 'orchestrator', requestId: `orchestration:${resolved.taskId}:${routed.stage}:${routed.round}`,
530
+ requestedAction: action, reasonCode: 'user-request', explicitRequest: true
531
+ });
532
+ if (!capability.allowed)
533
+ return failed('ORCHESTRATION_CAPABILITY_DENIED', `${capability.reasonCode}: ${capability.evidence.join(', ')}`, resolved.taskId);
534
+ const policy = run ? rolePolicy(run, routed.role) : null;
535
+ const next = {
536
+ ...routed,
537
+ requestedModel: policy?.model ?? null,
538
+ requestedReasoningEffort: policy?.reasoningEffort ?? null
539
+ };
587
540
  return { status: 'running', changed: false, taskId: resolved.taskId, run, next, error: null };
588
541
  }
589
542
  function statusOrchestration(taskRef, options = {}) {
@@ -668,10 +621,7 @@ function prepareOrchestrationDelegationUnlocked(taskRef, input, options = {}) {
668
621
  }
669
622
  const updated = withUpdatedRun(run, {
670
623
  nextStage: next.stage,
671
- pendingDelegation: receipt,
672
- commitAuthorization: next.stage === 'commit' && run.commitAuthorization.issuedAt === null
673
- ? { issuedAt: (options.now ?? (() => new Date().toISOString()))(), consumedAt: null }
674
- : run.commitAuthorization
624
+ pendingDelegation: receipt
675
625
  });
676
626
  saveRun(resolved.taskDir, updated);
677
627
  return { status: 'running', changed: true, taskId: resolved.taskId, run: updated, next, error: null };
@@ -1093,16 +1043,15 @@ function advanceOrchestration(taskRef, options = {}) {
1093
1043
  const result = consumeDelegation(run.pendingDelegation, { now: options.now });
1094
1044
  if (!result.ok)
1095
1045
  return pauseOrchestration(taskRef, result.code, result.message, true, options);
1096
- const completed = result.receipt.stage === 'commit';
1046
+ if (result.receipt.stage === 'commit') {
1047
+ return failed('ORCHESTRATED_COMMIT_REMOVED', 'Orchestrated commit is no longer a lifecycle stage; use local checkpoint delivery from code-task', resolved.taskId);
1048
+ }
1097
1049
  const updated = withUpdatedRun(run, {
1098
- status: completed ? 'completed' : 'running',
1050
+ status: 'running',
1099
1051
  nextStage: null,
1100
1052
  stepCount: run.stepCount + 1,
1101
1053
  pendingDelegation: null,
1102
- receipts: Object.freeze([...run.receipts, result.receipt]),
1103
- commitAuthorization: completed
1104
- ? { ...run.commitAuthorization, consumedAt: (options.now ?? (() => new Date().toISOString()))() }
1105
- : run.commitAuthorization
1054
+ receipts: Object.freeze([...run.receipts, result.receipt])
1106
1055
  });
1107
1056
  saveRun(resolved.taskDir, updated);
1108
1057
  return { status: updated.status, changed: true, taskId: resolved.taskId, run: updated, next: null, error: null };
@@ -0,0 +1,183 @@
1
+ import { isLegacyCompatibilityEnabled, legacyCompatibilityError, VERSION } from '../version.js';
2
+ import { isResourceIdentity, parseResourceIdentity, resourceIdentityNumber, serializeResourceIdentity } from '../platform/resource-identity.js';
3
+ const PR_DELIVERY_FACT_KEY = 'pr_delivery_fact';
4
+ function factError(message) {
5
+ return Object.assign(new Error(`PR_DELIVERY_FACT_INVALID: ${message}`), { code: 'PR_DELIVERY_FACT_INVALID' });
6
+ }
7
+ function asFactError(error) {
8
+ if (error instanceof Error && typeof error.code === 'string')
9
+ return error;
10
+ return factError(error instanceof Error ? error.message : String(error));
11
+ }
12
+ function isRecord(value) { return Boolean(value) && typeof value === 'object' && !Array.isArray(value); }
13
+ function exactKeys(value, keys, label) {
14
+ const actual = Object.keys(value).sort();
15
+ const expected = [...keys].sort();
16
+ if (actual.length !== expected.length || actual.some((key, index) => key !== expected[index]))
17
+ throw factError(`${label} has an invalid field set`);
18
+ }
19
+ function text(value, label, allowEmpty = false) {
20
+ if (typeof value !== 'string' || (!allowEmpty && !value) || value.trim() !== value)
21
+ throw factError(`${label} must be a string`);
22
+ return value;
23
+ }
24
+ function nullableText(value, label) { return value === null ? null : text(value, label); }
25
+ function timestamp(value, label) {
26
+ const result = text(value, label);
27
+ if (Number.isNaN(Date.parse(result)))
28
+ throw factError(`${label} must be a valid timestamp`);
29
+ return new Date(result).toISOString();
30
+ }
31
+ function positiveNumber(value, label) {
32
+ if (!Number.isSafeInteger(value) || value <= 0)
33
+ throw factError(`${label} must be a positive integer`);
34
+ return value;
35
+ }
36
+ function parseRef(value, label) {
37
+ if (!isRecord(value))
38
+ throw factError(`${label} must be an object`);
39
+ exactKeys(value, ['repository', 'ref', 'sha'], label);
40
+ return { repository: text(value.repository, `${label}.repository`), ref: text(value.ref, `${label}.ref`), sha: text(value.sha, `${label}.sha`) };
41
+ }
42
+ function canonicalIdentity(value) {
43
+ if (!isRecord(value))
44
+ throw factError('identity must be an object');
45
+ exactKeys(value, ['resource', 'repository', 'url', 'head', 'base'], 'identity');
46
+ return {
47
+ resource: parseResourceIdentity(value.resource, 'identity.resource'),
48
+ repository: text(value.repository, 'identity.repository'),
49
+ url: text(value.url, 'identity.url'),
50
+ head: parseRef(value.head, 'identity.head'),
51
+ base: parseRef(value.base, 'identity.base')
52
+ };
53
+ }
54
+ function decodeLegacyIdentity(value) {
55
+ if (!isRecord(value))
56
+ throw factError('legacy identity must be an object');
57
+ // TODO(compat): Remove the v1 identity decoder before the first stable v1.0.0 release.
58
+ exactKeys(value, ['repository', 'number', 'nodeId', 'url', 'head', 'base'], 'legacy identity');
59
+ const legacy = {
60
+ number: positiveNumber(value.number, 'legacy identity.number'),
61
+ repository: text(value.repository, 'identity.repository'),
62
+ nodeId: text(value.nodeId, 'legacy identity.nodeId'),
63
+ url: text(value.url, 'identity.url'),
64
+ head: parseRef(value.head, 'identity.head'),
65
+ base: parseRef(value.base, 'identity.base')
66
+ };
67
+ return {
68
+ resource: { kind: 'number', value: legacy.number },
69
+ repository: legacy.repository,
70
+ url: legacy.url,
71
+ head: legacy.head,
72
+ base: legacy.base
73
+ };
74
+ }
75
+ function provenanceForSource(source) { return source === 'created' ? 'create-post' : source === 'reused' ? 'reuse' : source; }
76
+ function parseFact(value, runtimeVersion = VERSION) {
77
+ if (!isRecord(value))
78
+ throw factError('fact must be an object');
79
+ if ((value.version !== 1 && value.version !== 2) || typeof value.state !== 'string')
80
+ throw factError('version or state is invalid');
81
+ if (value.version === 1 && !isLegacyCompatibilityEnabled(runtimeVersion)) {
82
+ throw legacyCompatibilityError('v1 PR delivery fact', runtimeVersion);
83
+ }
84
+ if (value.state === 'unbound') {
85
+ exactKeys(value, ['version', 'state', 'reason'], 'unbound fact');
86
+ if (value.reason !== 'initial')
87
+ throw factError('unbound reason is invalid');
88
+ return { version: 2, state: 'unbound', reason: 'initial' };
89
+ }
90
+ if (value.state === 'skipped') {
91
+ exactKeys(value, ['version', 'state', 'reason', 'decidedAt'], 'skipped fact');
92
+ if (value.reason !== 'explicit')
93
+ throw factError('skipped reason is invalid');
94
+ return { version: 2, state: 'skipped', reason: 'explicit', decidedAt: timestamp(value.decidedAt, 'skipped.decidedAt') };
95
+ }
96
+ if (value.state !== 'bound')
97
+ throw factError('state is invalid');
98
+ if (value.version === 1) {
99
+ // TODO(compat): Remove this v1-to-v2 shape conversion before the first stable v1.0.0 release.
100
+ exactKeys(value, ['version', 'state', 'identity', 'binding', 'provenance'], 'legacy bound fact');
101
+ if (!isRecord(value.binding) || !isRecord(value.provenance))
102
+ throw factError('legacy binding or provenance is invalid');
103
+ exactKeys(value.binding, ['status', 'source', 'verifiedAt', 'issueNumber', 'remoteState', 'mergedAt', 'mergeCommitSha'], 'legacy binding');
104
+ const issueNumber = value.binding.issueNumber === null ? null : positiveNumber(value.binding.issueNumber, 'legacy binding.issueNumber');
105
+ return parseFact({
106
+ version: 2,
107
+ state: 'bound',
108
+ identity: decodeLegacyIdentity(value.identity),
109
+ binding: {
110
+ status: 'verified', source: value.binding.source, verifiedAt: timestamp(value.binding.verifiedAt, 'binding.verifiedAt'),
111
+ issueIdentity: issueNumber === null ? null : { kind: 'number', value: issueNumber }, remoteState: value.binding.remoteState,
112
+ mergedAt: value.binding.mergedAt === null ? null : timestamp(value.binding.mergedAt, 'binding.mergedAt'), mergeCommitSha: nullableText(value.binding.mergeCommitSha, 'binding.mergeCommitSha')
113
+ },
114
+ provenance: value.provenance
115
+ }, runtimeVersion);
116
+ }
117
+ exactKeys(value, ['version', 'state', 'identity', 'binding', 'provenance'], 'bound fact');
118
+ if (!isRecord(value.binding) || !isRecord(value.provenance))
119
+ throw factError('binding or provenance is invalid');
120
+ exactKeys(value.binding, ['status', 'source', 'verifiedAt', 'issueIdentity', 'remoteState', 'mergedAt', 'mergeCommitSha'], 'binding');
121
+ if (value.binding.status !== 'verified')
122
+ throw factError('binding status is invalid');
123
+ const sources = ['created', 'reused', 'explicit-bind', 'external-unique', 'external-explicit'];
124
+ if (!sources.includes(value.binding.source))
125
+ throw factError('binding source is invalid');
126
+ const issueIdentity = value.binding.issueIdentity === null ? null : parseResourceIdentity(value.binding.issueIdentity, 'binding.issueIdentity');
127
+ if (value.binding.remoteState !== 'open' && value.binding.remoteState !== 'closed')
128
+ throw factError('binding.remoteState is invalid');
129
+ const mergedAt = value.binding.mergedAt === null ? null : timestamp(value.binding.mergedAt, 'binding.mergedAt');
130
+ const mergeCommitSha = nullableText(value.binding.mergeCommitSha, 'binding.mergeCommitSha');
131
+ if (Boolean(mergedAt) !== Boolean(mergeCommitSha))
132
+ throw factError('mergedAt and mergeCommitSha must be paired');
133
+ exactKeys(value.provenance, ['establishedBy'], 'provenance');
134
+ const provenances = ['create-post', 'reuse', 'explicit-bind', 'external-unique', 'external-explicit'];
135
+ if (!provenances.includes(value.provenance.establishedBy))
136
+ throw factError('provenance.establishedBy is invalid');
137
+ if (value.provenance.establishedBy !== provenanceForSource(value.binding.source))
138
+ throw factError('binding.source and provenance.establishedBy do not match');
139
+ return {
140
+ version: 2, state: 'bound', identity: canonicalIdentity(value.identity),
141
+ binding: { status: 'verified', source: value.binding.source, verifiedAt: timestamp(value.binding.verifiedAt, 'binding.verifiedAt'), issueIdentity, remoteState: value.binding.remoteState, mergedAt, mergeCommitSha },
142
+ provenance: { establishedBy: value.provenance.establishedBy }
143
+ };
144
+ }
145
+ function decodePrDeliveryFact(value, runtimeVersion = VERSION) {
146
+ if (typeof value === 'string') {
147
+ let decoded;
148
+ try {
149
+ decoded = JSON.parse(value);
150
+ }
151
+ catch {
152
+ throw factError('value is not valid JSON');
153
+ }
154
+ return parseFact(decoded, runtimeVersion);
155
+ }
156
+ return parseFact(value, runtimeVersion);
157
+ }
158
+ function encodePrDeliveryFact(fact) { return JSON.stringify(parseFact(fact)); }
159
+ function readPrDeliveryFact(metadata, runtimeVersion = VERSION) {
160
+ if (!Object.hasOwn(metadata, PR_DELIVERY_FACT_KEY) || metadata[PR_DELIVERY_FACT_KEY] === '')
161
+ return { status: 'missing', fact: null };
162
+ try {
163
+ return { status: 'valid', fact: decodePrDeliveryFact(metadata[PR_DELIVERY_FACT_KEY], runtimeVersion) };
164
+ }
165
+ catch (error) {
166
+ return { status: 'invalid', fact: null, error: asFactError(error) };
167
+ }
168
+ }
169
+ function buildUnboundFact() { return { version: 2, state: 'unbound', reason: 'initial' }; }
170
+ function buildSkippedFact(decidedAt) { return parseFact({ version: 2, state: 'skipped', reason: 'explicit', decidedAt }); }
171
+ function buildBoundFact(input) {
172
+ return parseFact({ version: 2, state: 'bound', identity: input.identity, binding: {
173
+ status: 'verified', source: input.source, verifiedAt: input.verifiedAt,
174
+ issueIdentity: input.issueIdentity === undefined ? null : input.issueIdentity,
175
+ remoteState: input.remoteState, mergedAt: input.mergedAt ?? null, mergeCommitSha: input.mergeCommitSha ?? null
176
+ }, provenance: { establishedBy: provenanceForSource(input.source) } });
177
+ }
178
+ function factFrontmatterMutation(fact) { return { set: { [PR_DELIVERY_FACT_KEY]: encodePrDeliveryFact(fact) } }; }
179
+ function identityFromPullRequest(pullRequest) {
180
+ return canonicalIdentity(pullRequest);
181
+ }
182
+ export { PR_DELIVERY_FACT_KEY, buildBoundFact, buildSkippedFact, buildUnboundFact, decodePrDeliveryFact, encodePrDeliveryFact, factFrontmatterMutation, identityFromPullRequest, readPrDeliveryFact, serializeResourceIdentity };
183
+ //# sourceMappingURL=pr-delivery-fact.js.map
@@ -5,7 +5,7 @@ import { locateActivityLog, pairEntries, startedBackedRows } from './activity-lo
5
5
  import { parseArtifactName, validateCompletedArtifact } from './artifact-lifecycle.js';
6
6
  import { LEDGER_SECTION_MISSING_CODE, LEDGER_SECTION_MISSING_MESSAGE, parseLedgerDocument, summarizeLedgerStage, validateLedgerRows } from './ledger.js';
7
7
  import { finalizeReviewSummaryContent } from './review-artifacts.js';
8
- import { inspectDecisionDetailDuplicates, repairDecisionDetailDuplicates } from './decision-details.js';
8
+ import { inspectDecisionDetailDuplicates } from './decision-details.js';
9
9
  import { resolveTaskRef } from './resolve-ref.js';
10
10
  import { validateLifecycleExecution } from './lifecycle-execution.js';
11
11
  import { TaskExecutionLockError, withTaskExecutionLock } from './task-execution-lock.js';
@@ -118,19 +118,15 @@ function finalizeReviewSummaryUnlocked(request, options = {}) {
118
118
  return failed(request, 'REVIEW_LEDGER_INVALID', `${ledgerError.code}: ${ledgerError.message}`, resolved.taskId);
119
119
  }
120
120
  const stageStatus = summarizeLedgerStage(rows, stage);
121
- const repaired = repairDecisionDetailDuplicates(artifactContent);
122
- if (!repaired.ok) {
123
- return failed(request, 'REVIEW_DECISION_DETAIL_INVALID', `${repaired.code}: ${repaired.message}`, resolved.taskId, stageStatus);
124
- }
125
- const detailInspection = inspectDecisionDetailDuplicates(repaired.content);
121
+ const detailInspection = inspectDecisionDetailDuplicates(artifactContent);
126
122
  if (!detailInspection.ok) {
127
123
  return failed(request, 'REVIEW_DECISION_DETAIL_INVALID', `${detailInspection.code}: ${detailInspection.message}`, resolved.taskId, stageStatus);
128
124
  }
129
- const transformed = finalizeReviewSummaryContent(repaired.content, stageStatus.unresolvedFindingCounts);
125
+ const transformed = finalizeReviewSummaryContent(artifactContent, stageStatus.unresolvedFindingCounts);
130
126
  if (!transformed.ok) {
131
127
  return failed(request, transformed.code, transformed.message, resolved.taskId, stageStatus);
132
128
  }
133
- if (!repaired.changed && !transformed.changed) {
129
+ if (!transformed.changed) {
134
130
  return {
135
131
  ...failed(request, 'REVIEW_ARTIFACT_CONFLICT', '', resolved.taskId, stageStatus),
136
132
  status: 'no-op',
@@ -1,6 +1,7 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
3
  import { artifactName, maxRound } from "./review-artifacts.js";
4
+ import { invalidationBlocks, isArtifactInvalidated, parseInvalidationDocument } from "./invalidation.js";
4
5
  const REVIEWED_TREE_RE = /^[a-f0-9]{40,64}$/;
5
6
  export function parseReviewedGitTree(value) {
6
7
  return REVIEWED_TREE_RE.test(value) ? value : null;
@@ -30,20 +31,49 @@ export function findAuthoritativeReviewCodeArtifact(taskDir) {
30
31
  const entries = fs.existsSync(taskDir) ? fs.readdirSync(taskDir) : [];
31
32
  const round = maxRound(entries, "review-code");
32
33
  if (round === 0) {
33
- return { ok: false, round: 0, fileName: null, path: null };
34
+ return { ok: false, round: 0, fileName: null, path: null, error: null };
34
35
  }
35
36
  const fileName = artifactName("review-code", round);
37
+ let taskContent;
38
+ try {
39
+ taskContent = fs.readFileSync(path.join(taskDir, "task.md"), "utf8");
40
+ }
41
+ catch (error) {
42
+ return {
43
+ ok: false, round, fileName, path: null,
44
+ error: error instanceof Error ? error.message : String(error)
45
+ };
46
+ }
47
+ const invalidation = parseInvalidationDocument(taskContent);
48
+ if (!invalidation.ok) {
49
+ return { ok: false, round, fileName, path: null, error: invalidation.message };
50
+ }
51
+ if (invalidationBlocks(invalidation.document)) {
52
+ return { ok: false, round, fileName, path: null, error: "artifact invalidation is incomplete; reconcile task.md before using review-code" };
53
+ }
54
+ if (isArtifactInvalidated(invalidation.document, "review-code", fileName)) {
55
+ return { ok: false, round, fileName, path: null, error: `${fileName} is invalidated; re-run review-code` };
56
+ }
36
57
  return {
37
58
  ok: true,
38
59
  round,
39
60
  fileName,
40
- path: path.join(taskDir, fileName)
61
+ path: path.join(taskDir, fileName),
62
+ error: null
41
63
  };
42
64
  }
43
65
  export function extractReviewBaseline(content) {
44
66
  const match = String(content).match(/^[-*]?\s*\*\*(?:审查基线提交|Review Baseline Commit)\*\*[::]\s*(.*?)\s*$/m);
45
67
  return match ? match[1].trim().replace(/`/g, "") : "";
46
68
  }
69
+ export function extractReviewTargetHead(content) {
70
+ const match = String(content).match(/^[-*]?\s*\*\*(?:审查目标提交|审查目标 HEAD|Review Target Commit|Review Target Head)\*\*[::]\s*(.*?)\s*$/m);
71
+ return match ? match[1].trim().replace(/`/g, "") : "";
72
+ }
73
+ export function extractReviewedHead(content) {
74
+ const match = String(content).match(/^[-*]?\s*\*\*(?:审查已检视提交|已检视提交|Reviewed Head Commit|Reviewed Head)\*\*[::]\s*(.*?)\s*$/m);
75
+ return match ? match[1].trim().replace(/`/g, "") : "";
76
+ }
47
77
  export function extractReviewDiffBase(content) {
48
78
  const match = String(content).match(/^[-*]?\s*\*\*(?:审查差异基线|Reviewed Diff Base)\*\*[::]\s*(.*?)\s*$/m);
49
79
  return match ? match[1].trim().replace(/`/g, "") : "";