@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
@@ -11,10 +11,13 @@ import { appendActivityEntry, locateActivityLog } from './activity-log.ts';
11
11
  import { captureTaskWriteMetadata, writeTask } from './write.ts';
12
12
  import type { TaskMutation } from './write.ts';
13
13
  import {
14
- commitOrchestrationStageCompletion,
15
- planOrchestrationStageCompletion
16
- } from './orchestration.ts';
17
- import type { OrchestrationStageCompletion } from './orchestration.ts';
14
+ checkpointIntentDigest,
15
+ readCheckpointIntent,
16
+ removeCheckpointIntent,
17
+ sameCheckpointIntent,
18
+ updateCheckpointIntent,
19
+ writeCheckpointIntent
20
+ } from './commit-intent.ts';
18
21
 
19
22
  type CommitExecutionMode = 'direct' | 'orchestrated';
20
23
 
@@ -27,7 +30,13 @@ type CommitOperationInput = Readonly<{
27
30
  taskRef?: string;
28
31
  agent?: string;
29
32
  mode?: CommitExecutionMode;
30
- push: Readonly<{
33
+ round?: number;
34
+ delivery?: Readonly<{
35
+ mode: 'local' | 'push';
36
+ remote: string;
37
+ refs: readonly string[];
38
+ }> | Readonly<{ mode: 'local' }>;
39
+ push?: Readonly<{
31
40
  remote: string;
32
41
  refs: readonly string[];
33
42
  }>;
@@ -55,6 +64,7 @@ type BoundTask = Readonly<{
55
64
  }>;
56
65
 
57
66
  const HEAD_REF = /^refs\/heads\/(.+)$/;
67
+ type CommitDelivery = Readonly<{ mode: 'local' } | { mode: 'push'; remote: string; refs: readonly string[] }>;
58
68
 
59
69
  function isCommitExecutionMode(value: unknown): value is CommitExecutionMode {
60
70
  return value === 'direct' || value === 'orchestrated';
@@ -68,6 +78,12 @@ function gitText(repoRoot: string, args: readonly string[]): string {
68
78
  }).trim();
69
79
  }
70
80
 
81
+ function effectiveDelivery(input: CommitOperationInput): CommitDelivery | null {
82
+ if (input.delivery) return input.delivery;
83
+ if (input.push) return { mode: 'push', remote: input.push.remote, refs: input.push.refs };
84
+ return null;
85
+ }
86
+
71
87
  function canonicalRepositoryRoot(cwd: string): string {
72
88
  return gitText(cwd, ['rev-parse', '--show-toplevel']);
73
89
  }
@@ -156,7 +172,8 @@ function validateCommon(input: CommitOperationInput, branch: string | null): Rea
156
172
  if (!input.message.trim()) return { code: 'GIT_COMMIT_INPUT_INVALID', message: 'Commit message is required' };
157
173
  if (!/^[a-f0-9]{40,64}$/.test(input.expectedHead)) return { code: 'GIT_HEAD_EXPECTATION_REQUIRED', message: 'A valid expected HEAD is required' };
158
174
  if (!/^[a-f0-9]{40,64}$/.test(input.expectedTree)) return { code: 'GIT_TREE_EXPECTATION_REQUIRED', message: 'A valid expected tree is required' };
159
- if (!input.push) return { code: 'GIT_PUSH_INPUT_INVALID', message: 'Commit delivery requires a push policy' };
175
+ const delivery = effectiveDelivery(input);
176
+ if (!delivery) return { code: 'GIT_PUSH_INPUT_INVALID', message: 'Commit delivery requires a delivery policy' };
160
177
  if (input.paths.some((candidate) => !candidate || candidate.startsWith('/') || candidate.split(/[\\/]/).includes('..'))) {
161
178
  return { code: 'GIT_COMMIT_INPUT_INVALID', message: 'Commit paths must stay inside the repository' };
162
179
  }
@@ -164,42 +181,16 @@ function validateCommon(input: CommitOperationInput, branch: string | null): Rea
164
181
  return { code: 'GIT_COMMIT_INPUT_INVALID', message: 'Sensitive paths cannot be committed' };
165
182
  }
166
183
  if (branch !== null && branch === '') return { code: 'GIT_BRANCH_INVALID', message: 'A named branch is required' };
167
- if (!input.push.remote || input.push.refs.length !== 1) return { code: 'GIT_PUSH_INPUT_INVALID', message: 'Push requires one remote and one ref' };
168
- const ref = input.push.refs[0] ?? '';
169
- if (!HEAD_REF.test(ref) || HEAD_REF.exec(ref)?.[1] !== branch) {
170
- return { code: 'GIT_PUSH_INPUT_INVALID', message: 'Push ref must be the current full heads ref' };
184
+ if (delivery.mode === 'push') {
185
+ if (!delivery.remote || delivery.refs.length !== 1) return { code: 'GIT_PUSH_INPUT_INVALID', message: 'Push requires one remote and one ref' };
186
+ const ref = delivery.refs[0] ?? '';
187
+ if (!HEAD_REF.test(ref) || HEAD_REF.exec(ref)?.[1] !== branch) {
188
+ return { code: 'GIT_PUSH_INPUT_INVALID', message: 'Push ref must be the current full heads ref' };
189
+ }
171
190
  }
172
191
  return null;
173
192
  }
174
193
 
175
- function validateOrchestrated(
176
- input: CommitOperationInput,
177
- task: BoundTask
178
- ): { plan: OrchestrationStageCompletion } | { error: Readonly<{ code: string; message: string }> } {
179
- if (!input.agent) return { error: { code: 'ORCHESTRATION_PROVENANCE_MISMATCH', message: 'orchestrated commit requires an agent' } };
180
- const planned = planOrchestrationStageCompletion(task.taskId, {
181
- stage: 'commit',
182
- round: 1,
183
- artifact: 'commit',
184
- role: 'executor',
185
- agent: input.agent
186
- }, { repoRoot: task.repoRoot });
187
- const run = planned.result.run;
188
- if (
189
- !planned.plan
190
- || !run
191
- || run.status !== 'running'
192
- || !run.commitAuthorization.issuedAt
193
- || run.commitAuthorization.consumedAt
194
- ) return {
195
- error: {
196
- code: planned.result.error?.code ?? 'ORCHESTRATION_PROVENANCE_MISMATCH',
197
- message: planned.result.error?.message ?? 'orchestrated commit requires one matching activated commit delegation'
198
- }
199
- };
200
- return { plan: planned.plan };
201
- }
202
-
203
194
  function syncTaskCommit(
204
195
  input: CommitOperationInput,
205
196
  task: BoundTask,
@@ -219,7 +210,18 @@ function syncTaskCommit(
219
210
  );
220
211
  const metadata = captureTaskWriteMetadata();
221
212
  const commitNote = gitText(task.repoRoot, ['show', '-s', '--format=%h %s', committedHead]);
222
- const alreadyLogged = activity.entries.some((entry) => entry.step === 'Commit' && entry.note === commitNote);
213
+ let matchingCommitIndex = -1;
214
+ for (let index = activity.entries.length - 1; index >= 0; index -= 1) {
215
+ const entry = activity.entries[index]!;
216
+ if (entry.step === 'Commit' && entry.note === commitNote) {
217
+ matchingCommitIndex = index;
218
+ break;
219
+ }
220
+ }
221
+ const reviewedAfterCommit = matchingCommitIndex >= 0 && activity.entries
222
+ .slice(matchingCommitIndex + 1)
223
+ .some((entry) => /^Review Code \(Round \d+\)$/.test(entry.step));
224
+ const alreadyLogged = matchingCommitIndex >= 0 && !reviewedAfterCommit;
223
225
  const mutations: TaskMutation[] = [];
224
226
  if (!alreadyLogged) mutations.push({
225
227
  kind: 'section',
@@ -227,7 +229,10 @@ function syncTaskCommit(
227
229
  heading: activity.heading,
228
230
  body: appendActivityEntry(activity, { time: metadata.timestamp, step: 'Commit', agent: input.agent!, note: commitNote })
229
231
  });
230
- const frontmatter: Record<string, string> = { assigned_to: input.agent! };
232
+ const frontmatter: Record<string, string> = {
233
+ assigned_to: input.agent!,
234
+ checkpoint_commit: committedHead
235
+ };
231
236
  mutations.push({ kind: 'frontmatter', set: frontmatter });
232
237
  if (mutations.length === 0) return null;
233
238
  const written = writeTask({
@@ -256,29 +261,133 @@ function syncTaskCommit(
256
261
  }
257
262
  }
258
263
 
264
+ function checkpointChildMatches(task: BoundTask, intent: NonNullable<ReturnType<typeof readCheckpointIntent>>, head: string): boolean {
265
+ try {
266
+ const parent = gitText(task.repoRoot, ['rev-parse', `${head}^`]);
267
+ const tree = gitText(task.repoRoot, ['rev-parse', `${head}^{tree}`]);
268
+ const message = gitText(task.repoRoot, ['show', '-s', '--format=%s', head]);
269
+ const changed = gitText(task.repoRoot, ['diff-tree', '--no-commit-id', '--name-only', '-r', head]).split('\n').filter(Boolean).sort();
270
+ return parent === intent.expectedHead
271
+ && tree === intent.expectedTree
272
+ && message === intent.message
273
+ && JSON.stringify(changed) === JSON.stringify([...intent.paths].sort());
274
+ } catch {
275
+ return false;
276
+ }
277
+ }
278
+
279
+ function checkpointIdentity(input: CommitOperationInput, task: BoundTask, delivery: CommitDelivery): Parameters<typeof checkpointIntentDigest>[0] {
280
+ if (delivery.mode !== 'local') throw new Error('checkpoint identity requires local delivery');
281
+ return {
282
+ taskId: task.taskId,
283
+ branch: task.branch,
284
+ mode: 'local',
285
+ expectedHead: input.expectedHead,
286
+ expectedTree: input.expectedTree,
287
+ paths: [...input.paths],
288
+ message: input.message,
289
+ round: input.round ?? 1
290
+ };
291
+ }
292
+
293
+ function checkpointTimestamp(): string {
294
+ return new Date().toISOString();
295
+ }
296
+
259
297
  function executeUnlocked(input: CommitOperationInput, task: BoundTask | null, mode: CommitExecutionMode): CommitOperationResult {
260
298
  const inspected = inspectGitWorkflow(input.cwd);
261
299
  if (!inspected.snapshot) return failure(input, mode, task?.taskId ?? null, 'GIT_INSPECT_FAILED', 'Unable to inspect Git repository');
300
+ if (mode === 'orchestrated') return failure(
301
+ input,
302
+ mode,
303
+ task?.taskId ?? null,
304
+ 'ORCHESTRATED_COMMIT_REMOVED',
305
+ 'Orchestrated commit is no longer a lifecycle stage; use local checkpoint delivery from code-task',
306
+ 'blocked',
307
+ inspected.snapshot
308
+ );
262
309
  const branch = inspected.snapshot.branch;
263
310
  const commonError = validateCommon(input, branch);
264
311
  if (commonError) return failure(input, mode, task?.taskId ?? null, commonError.code, commonError.message, 'blocked', inspected.snapshot);
265
312
  if (task && task.state !== 'active') return failure(input, mode, task.taskId, 'TASK_STATE_INVALID', 'task-bound commit requires an active task', 'blocked', inspected.snapshot);
266
313
  if (task && task.branch !== branch) return failure(input, mode, task.taskId, 'GIT_BRANCH_MISMATCH', `Task branch '${task.branch}' does not match current branch '${branch}'`, 'blocked', inspected.snapshot);
267
314
  if (task && !input.agent) return failure(input, mode, task.taskId, 'COMMIT_AGENT_REQUIRED', 'task-bound commit requires an agent', 'blocked', inspected.snapshot);
268
- const orchestrationValidation = mode === 'orchestrated'
269
- ? (task
270
- ? validateOrchestrated(input, task)
271
- : { error: { code: 'ORCHESTRATION_TASK_REQUIRED', message: 'orchestrated commit requires a task context' } })
272
- : null;
273
- if (orchestrationValidation && 'error' in orchestrationValidation) {
274
- const error = orchestrationValidation.error;
275
- return failure(input, mode, task?.taskId ?? null, error.code, error.message, 'blocked', inspected.snapshot);
276
- }
277
- const orchestrationCompletion = orchestrationValidation && 'plan' in orchestrationValidation
278
- ? orchestrationValidation.plan
279
- : null;
280
-
281
- const committed = input.paths.length === 0
315
+ const delivery = effectiveDelivery(input)!;
316
+ if (delivery.mode === 'local' && !task) return failure(input, mode, null, 'CHECKPOINT_TASK_REQUIRED', 'local checkpoint delivery requires a task context', 'blocked', inspected.snapshot);
317
+ let pendingIntent: NonNullable<ReturnType<typeof readCheckpointIntent>> | null = null;
318
+ let checkpointCommittedHead: string | null = null;
319
+ const committed = delivery.mode === 'local'
320
+ ? (() => {
321
+ const identity = checkpointIdentity(input, task!, delivery);
322
+ try {
323
+ pendingIntent = readCheckpointIntent(task!.repoRoot, task!.taskId);
324
+ } catch (error) {
325
+ return {
326
+ status: 'failed' as const, changed: false, snapshot: inspected.snapshot, operations: [],
327
+ error: { code: 'COMMIT_INTENT_INVALID', message: error instanceof Error ? error.message : String(error) }
328
+ };
329
+ }
330
+ if (pendingIntent && !sameCheckpointIntent(pendingIntent, identity)) return {
331
+ status: 'failed' as const, changed: false, snapshot: inspected.snapshot, operations: [],
332
+ error: { code: 'COMMIT_INTENT_CONFLICT', message: 'A different checkpoint intent is still pending for this task' }
333
+ };
334
+ if (!pendingIntent) {
335
+ const timestamp = checkpointTimestamp();
336
+ pendingIntent = {
337
+ version: 1, ...identity, digest: checkpointIntentDigest(identity), state: 'prepared',
338
+ committedHead: null, createdAt: timestamp, updatedAt: timestamp
339
+ };
340
+ try { writeCheckpointIntent(task!.repoRoot, pendingIntent); }
341
+ catch (error) {
342
+ return {
343
+ status: 'failed' as const, changed: false, snapshot: inspected.snapshot, operations: [],
344
+ error: { code: 'COMMIT_INTENT_WRITE_FAILED', message: error instanceof Error ? error.message : String(error) }
345
+ };
346
+ }
347
+ }
348
+ if (pendingIntent.state !== 'prepared') {
349
+ checkpointCommittedHead = pendingIntent.committedHead;
350
+ return {
351
+ status: 'no-op' as const, changed: false, snapshot: inspected.snapshot,
352
+ operations: [{ name: 'commit', status: 'no-op' as const }], error: null
353
+ };
354
+ }
355
+ if (inspected.snapshot.head !== identity.expectedHead && !checkpointChildMatches(task!, pendingIntent, inspected.snapshot.head)) return {
356
+ status: 'failed' as const, changed: false, snapshot: inspected.snapshot, operations: [],
357
+ error: { code: 'COMMIT_INTENT_HEAD_CONFLICT', message: 'Current HEAD does not match the pending checkpoint intent' }
358
+ };
359
+ if (inspected.snapshot.head !== identity.expectedHead) {
360
+ checkpointCommittedHead = inspected.snapshot.head;
361
+ pendingIntent = updateCheckpointIntent(pendingIntent, {
362
+ state: 'committed', committedHead: checkpointCommittedHead, updatedAt: checkpointTimestamp()
363
+ });
364
+ writeCheckpointIntent(task!.repoRoot, pendingIntent);
365
+ return {
366
+ status: 'no-op' as const, changed: false, snapshot: inspected.snapshot,
367
+ operations: [{ name: 'commit', status: 'no-op' as const }], error: null
368
+ };
369
+ }
370
+ if (input.paths.length === 0) return {
371
+ status: 'failed' as const, changed: false, snapshot: inspected.snapshot, operations: [],
372
+ error: { code: 'GIT_COMMIT_INPUT_INVALID', message: 'Local checkpoint requires explicit paths' }
373
+ };
374
+ const created = commitExplicitPaths({
375
+ cwd: input.cwd, paths: input.paths, message: input.message,
376
+ expectedHead: identity.expectedHead, expectedTree: identity.expectedTree
377
+ });
378
+ if (created.status === 'applied' && created.snapshot?.head) {
379
+ checkpointCommittedHead = created.snapshot.head;
380
+ pendingIntent = updateCheckpointIntent(pendingIntent, {
381
+ state: 'committed', committedHead: checkpointCommittedHead, updatedAt: checkpointTimestamp()
382
+ });
383
+ writeCheckpointIntent(task!.repoRoot, pendingIntent);
384
+ } else if (created.status === 'no-op') {
385
+ removeCheckpointIntent(task!.repoRoot, task!.taskId);
386
+ pendingIntent = null;
387
+ }
388
+ return created;
389
+ })()
390
+ : input.paths.length === 0
282
391
  ? (() => {
283
392
  if (inspected.snapshot.worktree.length > 0 || inspected.snapshot.staged.length > 0) return {
284
393
  status: 'failed' as const,
@@ -327,53 +436,42 @@ function executeUnlocked(input: CommitOperationInput, task: BoundTask | null, mo
327
436
  };
328
437
 
329
438
  const warnings: OperationWarning[] = [];
330
- if (orchestrationCompletion) {
331
- try {
332
- commitOrchestrationStageCompletion(orchestrationCompletion);
333
- } catch (error) {
334
- warnings.push(warning(
335
- 'ORCHESTRATION_COMPLETION_FAILED',
336
- error instanceof Error ? error.message : String(error),
337
- true,
338
- 'orchestration',
339
- task?.taskId ?? 'orchestration',
340
- 'ACTION_REQUIRED'
341
- ));
342
- }
343
- }
344
439
  let status: CommitOperationResult['status'] = committed.status;
345
440
  let result: CommitOperationResult['result'] = committed.status === 'applied' ? 'committed' : 'no_op';
346
- const target = `${input.push.remote}:${input.push.refs[0]}`;
347
- const decision = commitPushDecision({ branch }, target);
348
- if (!decision.shouldPush) {
349
- warnings.push(decision.warning!);
350
- } else {
351
- const pushed = pushGitRefs({
352
- cwd: input.cwd,
353
- remote: input.push.remote,
354
- refs: input.push.refs,
355
- expectedSha: committed.snapshot?.head ?? gitText(input.cwd, ['rev-parse', 'HEAD'])
356
- });
357
- if (pushed.status !== 'applied') warnings.push(warning(
358
- 'COMMIT_PUSH_FAILED',
359
- pushed.error?.message ?? 'Git push failed',
360
- true,
361
- 'push',
362
- target,
363
- 'ACTION_REQUIRED'
364
- ));
441
+ if (delivery.mode === 'push') {
442
+ const target = `${delivery.remote}:${delivery.refs[0]}`;
443
+ const decision = commitPushDecision({ branch }, target);
444
+ if (!decision.shouldPush) {
445
+ warnings.push(decision.warning!);
446
+ } else {
447
+ const pushed = pushGitRefs({
448
+ cwd: input.cwd, remote: delivery.remote, refs: delivery.refs,
449
+ expectedSha: committed.snapshot?.head ?? gitText(input.cwd, ['rev-parse', 'HEAD'])
450
+ });
451
+ if (pushed.status !== 'applied') warnings.push(warning(
452
+ 'COMMIT_PUSH_FAILED', pushed.error?.message ?? 'Git push failed', true, 'push', target, 'ACTION_REQUIRED'
453
+ ));
454
+ }
365
455
  }
366
456
  if (warnings.length > 0) {
367
457
  status = 'applied';
368
458
  result = 'committed_with_warnings';
369
459
  }
370
460
  if (task && (committed.status === 'applied' || committed.status === 'no-op')) {
371
- const taskWarning = syncTaskCommit(input, task, committed.snapshot?.head ?? gitText(task.repoRoot, ['rev-parse', 'HEAD']));
461
+ const committedHead = checkpointCommittedHead ?? committed.snapshot?.head ?? gitText(task.repoRoot, ['rev-parse', 'HEAD']);
462
+ const taskWarning = syncTaskCommit(input, task, committedHead);
372
463
  if (taskWarning) {
373
464
  warnings.push(taskWarning);
374
465
  status = 'applied';
375
466
  result = 'committed_with_warnings';
376
467
  }
468
+ if (delivery.mode === 'local' && !taskWarning && pendingIntent && checkpointCommittedHead) {
469
+ const synced = updateCheckpointIntent(pendingIntent, {
470
+ state: 'synced', committedHead: checkpointCommittedHead, updatedAt: checkpointTimestamp()
471
+ });
472
+ writeCheckpointIntent(task.repoRoot, synced);
473
+ removeCheckpointIntent(task.repoRoot, task.taskId);
474
+ }
377
475
  }
378
476
  return {
379
477
  status,
@@ -206,15 +206,15 @@ function lifecycleTaskId(request: TaskLifecycleControlRequest, repoRoot: string)
206
206
  }
207
207
  }
208
208
 
209
- function applyLifecycleWithAuthority(
209
+ async function applyLifecycleWithAuthority(
210
210
  context: TaskControlExecutionContext,
211
211
  request: TaskLifecycleControlRequest
212
- ): TaskLifecycleResult & { humanOverride?: unknown } {
212
+ ): Promise<TaskLifecycleResult & { humanOverride?: unknown }> {
213
213
  const conflict = overrideDryRunConflict(request as unknown as Record<string, unknown>);
214
214
  if (conflict) {
215
215
  return taskLifecycleFailure(request, { code: 'LIFECYCLE_PAYLOAD_INVALID', message: conflict.message });
216
216
  }
217
- const execute = (): TaskLifecycleResult & { humanOverride?: unknown } => {
217
+ const execute = async (): Promise<TaskLifecycleResult & { humanOverride?: unknown }> => {
218
218
  const lifecycleResult = applyTaskLifecycle(request, { repoRoot: context.repoRoot });
219
219
  if (lifecycleResult.status !== 'failed' || !request.overrideTicket) return lifecycleResult;
220
220
  if (!request.overrideTarget || !request.overrideScope) {
@@ -223,7 +223,7 @@ function applyLifecycleWithAuthority(
223
223
  error: { code: 'OVERRIDE_PAYLOAD_INVALID', message: 'override ticket requires --override-target and --override-scope' }
224
224
  };
225
225
  }
226
- const override = consumeHumanOverride({
226
+ const override = await consumeHumanOverride({
227
227
  taskRef: request.taskRef,
228
228
  ticketId: request.overrideTicket,
229
229
  failureId: failureId('lifecycle.apply', lifecycleResult.error?.code ?? 'LIFECYCLE_FAILED'),
@@ -238,9 +238,9 @@ function applyLifecycleWithAuthority(
238
238
  return { ...lifecycleResult, ...override, humanOverride: override, error: null };
239
239
  };
240
240
  const taskId = lifecycleTaskId(request, context.repoRoot);
241
- if (!taskId) return execute();
241
+ if (!taskId) return await execute();
242
242
  try {
243
- return withTaskExecutionLock(
243
+ return await withTaskExecutionLock(
244
244
  context.repoRoot,
245
245
  taskId,
246
246
  `task-lifecycle.${request.intent}`,
@@ -322,11 +322,11 @@ function orchestrationFailure(error: unknown): OrchestrationResult {
322
322
  export function dispatchTaskControlOperation(
323
323
  context: TaskControlExecutionContext,
324
324
  operation: Extract<TaskControlOperation, { family: 'task-lifecycle' }>
325
- ): TaskLifecycleResult;
325
+ ): Promise<TaskLifecycleResult & { humanOverride?: unknown }>;
326
326
  export function dispatchTaskControlOperation(
327
327
  context: TaskControlExecutionContext,
328
328
  operation: Extract<TaskControlOperation, { family: 'task-finalization' }>
329
- ): TaskFinalizationResult;
329
+ ): Promise<TaskFinalizationResult>;
330
330
  export function dispatchTaskControlOperation(
331
331
  context: TaskControlExecutionContext,
332
332
  operation: Extract<TaskControlOperation, { family: 'task-orchestration' }>
@@ -345,6 +345,9 @@ export function dispatchTaskControlOperation(
345
345
  operationTaskId(context, operation.request.taskRef);
346
346
  return applyTaskFinalization(operation.request, {
347
347
  repoRoot: context.repoRoot,
348
+ ...(context.source === 'sandbox-executor' ? {
349
+ controlBinding: { generation: context.generation, requestId: context.requestId }
350
+ } : {}),
348
351
  preflight: (request, options) => verifyTaskEvent(
349
352
  { ...request, event: 'complete-task.hard-preflight' }, options
350
353
  )
@@ -42,8 +42,8 @@ const DEFAULT_DEPENDENCIES: TaskCreateDependencies = {
42
42
  addWarning: applyWorkflowWarningIntent
43
43
  };
44
44
 
45
- function verifyCreatedTask(repoRoot: string, taskId: string): { operation: TaskCreateOperation; error: TaskCreateResult['error'] } {
46
- const verification = verifyTaskEvent(
45
+ async function verifyCreatedTask(repoRoot: string, taskId: string): Promise<{ operation: TaskCreateOperation; error: TaskCreateResult['error'] }> {
46
+ const verification = await verifyTaskEvent(
47
47
  { taskRef: taskId, event: 'create-task.completed' },
48
48
  { repoRoot }
49
49
  );
@@ -78,7 +78,7 @@ function issueFromTask(repoRoot: string, taskId: string): { number: number; url:
78
78
  return match ? { number: Number(match[1]), url: '' } : null;
79
79
  }
80
80
 
81
- function createTask(value: unknown, options: CreateTaskOptions): TaskCreateResult {
81
+ async function createTask(value: unknown, options: CreateTaskOptions): Promise<TaskCreateResult> {
82
82
  const dependencies = { ...DEFAULT_DEPENDENCIES, ...options.dependencies };
83
83
  let candidate: TaskCreateCandidateV1;
84
84
  let local;
@@ -101,13 +101,13 @@ function createTask(value: unknown, options: CreateTaskOptions): TaskCreateResul
101
101
  name: 'task:local', status: local.status, reasonCode: null
102
102
  }];
103
103
  let issue = issueFromTask(options.repoRoot, local.task.id);
104
- const created = dependencies.createIssue(local.task.id, { cwd: options.repoRoot, agent: candidate.agent });
104
+ const created = await dependencies.createIssue(local.task.id, { cwd: options.repoRoot, agent: candidate.agent });
105
105
  operations.push(...platformOperations('platform-create', created.operations));
106
106
  if (created.issue) issue = { number: created.issue.number, url: created.issue.url };
107
107
 
108
108
  let platformFailure: PlatformResult | null = created.status === 'failed' || created.status === 'blocked' ? created : null;
109
109
  if (!platformFailure && (created.task.issueNumber || issue)) {
110
- const synced = dependencies.syncIssue(local.task.id, {
110
+ const synced = await dependencies.syncIssue(local.task.id, {
111
111
  cwd: options.repoRoot,
112
112
  agent: candidate.agent,
113
113
  status: 'waiting-for-triage',
@@ -120,7 +120,7 @@ function createTask(value: unknown, options: CreateTaskOptions): TaskCreateResul
120
120
  if (synced.issue) issue = { number: synced.issue.number, url: synced.issue.url };
121
121
  if (synced.status === 'failed' || synced.status === 'blocked') platformFailure = synced;
122
122
  if (!platformFailure) {
123
- const commented = dependencies.syncComment(local.task.id, { cwd: options.repoRoot, kind: 'task', agent: candidate.agent });
123
+ const commented = await dependencies.syncComment(local.task.id, { cwd: options.repoRoot, kind: 'task', agent: candidate.agent });
124
124
  operations.push(...platformOperations('platform-comment', commented.operations));
125
125
  if (commented.status === 'failed' || commented.status === 'blocked') platformFailure = commented;
126
126
  }
@@ -133,7 +133,7 @@ function createTask(value: unknown, options: CreateTaskOptions): TaskCreateResul
133
133
  code: 'ISSUE_CREATE_FAILED', target: 'issue', message: `${error.code}: ${error.message}`,
134
134
  action: 'Fix platform authentication or connectivity, then retry Issue creation for this task.'
135
135
  }, { repoRoot: options.repoRoot });
136
- const verified = verifyCreatedTask(options.repoRoot, local.task.id);
136
+ const verified = await verifyCreatedTask(options.repoRoot, local.task.id);
137
137
  operations.push(verified.operation);
138
138
  if (verified.error) return {
139
139
  status: verified.error.retryable ? 'blocked' : 'failed', changed: local.changed,
@@ -150,7 +150,7 @@ function createTask(value: unknown, options: CreateTaskOptions): TaskCreateResul
150
150
  };
151
151
  }
152
152
 
153
- const verified = verifyCreatedTask(options.repoRoot, local.task.id);
153
+ const verified = await verifyCreatedTask(options.repoRoot, local.task.id);
154
154
  operations.push(verified.operation);
155
155
  if (verified.error) return {
156
156
  status: verified.error.retryable ? 'blocked' : 'failed', changed: local.changed,
@@ -5,6 +5,8 @@ import path from 'node:path';
5
5
  import { isValidAgentInfraVersion, VERSION } from '../version.ts';
6
6
  import { loadShortIdByTaskId, mutateShortIdRegistry } from './short-id.ts';
7
7
  import { TaskExecutionLockError, withTaskExecutionLock } from './task-execution-lock.ts';
8
+ import { readDeliveryDefaults, validateBaseRef, validateRemote } from './delivery-target.ts';
9
+ import { buildUnboundFact, encodePrDeliveryFact } from './pr-delivery-fact.ts';
8
10
 
9
11
  const AGENTS = ['claude', 'codex', 'antigravity', 'opencode', 'cursor'] as const;
10
12
  const TYPES = ['feature', 'bugfix', 'refactor', 'docs', 'chore'] as const;
@@ -30,6 +32,8 @@ type TaskCreateCandidateV1 = Readonly<{
30
32
  priority: TaskPriority;
31
33
  effort: TaskEffort;
32
34
  description: string;
35
+ deliveryRemote?: string;
36
+ deliveryBaseRef?: string;
33
37
  taskInput: Readonly<Record<typeof TASK_INPUT_KEYS[number], readonly string[]>>;
34
38
  }>;
35
39
 
@@ -49,12 +53,14 @@ function payloadError(message: string): never {
49
53
  throw new Error(`TASK_CREATE_PAYLOAD_INVALID: ${message}`);
50
54
  }
51
55
 
52
- function exactKeys(value: Record<string, unknown>, expected: readonly string[], label: string): void {
56
+ function exactKeys(value: Record<string, unknown>, expected: readonly string[], label: string, optional: readonly string[] = []): void {
53
57
  const actual = Object.keys(value).sort();
54
58
  const wanted = [...expected].sort();
55
- if (actual.length !== wanted.length || actual.some((key, index) => key !== wanted[index])) {
59
+ const allowed = new Set([...expected, ...optional]);
60
+ if (actual.some((key) => !allowed.has(key))) {
56
61
  payloadError(`${label} has unknown or missing fields`);
57
62
  }
63
+ if (expected.some((key) => !actual.includes(key))) payloadError(`${label} has unknown or missing fields`);
58
64
  }
59
65
 
60
66
  function cleanString(value: unknown, label: string, maxBytes: number, oneLine = false): string {
@@ -78,7 +84,7 @@ function validateTaskCreateCandidate(value: unknown): TaskCreateCandidateV1 {
78
84
  exactKeys(candidate, [
79
85
  'version', 'idempotencyKey', 'agent', 'title', 'type', 'branchSlug',
80
86
  'priority', 'effort', 'description', 'taskInput'
81
- ], 'candidate');
87
+ ], 'candidate', ['deliveryRemote', 'deliveryBaseRef']);
82
88
  if (candidate.version !== 1) payloadError('version must be 1');
83
89
  const idempotencyKey = cleanString(candidate.idempotencyKey, 'idempotencyKey', 64, true);
84
90
  if (!/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(idempotencyKey)) {
@@ -91,6 +97,14 @@ function validateTaskCreateCandidate(value: unknown): TaskCreateCandidateV1 {
91
97
  payloadError('branchSlug must be lowercase ASCII kebab-case with length 3-64');
92
98
  }
93
99
  const description = cleanString(candidate.description, 'description', 8 * 1024);
100
+ const deliveryRemote = candidate.deliveryRemote === undefined
101
+ ? undefined
102
+ : cleanString(candidate.deliveryRemote, 'deliveryRemote', 200, true);
103
+ const deliveryBaseRef = candidate.deliveryBaseRef === undefined
104
+ ? undefined
105
+ : cleanString(candidate.deliveryBaseRef, 'deliveryBaseRef', 200, true);
106
+ if (deliveryRemote !== undefined && !validateRemote(deliveryRemote)) payloadError('deliveryRemote is invalid');
107
+ if (deliveryBaseRef !== undefined && !validateBaseRef(deliveryBaseRef)) payloadError('deliveryBaseRef is invalid');
94
108
  if (!candidate.taskInput || typeof candidate.taskInput !== 'object' || Array.isArray(candidate.taskInput)) {
95
109
  payloadError('taskInput must be an object');
96
110
  }
@@ -111,6 +125,8 @@ function validateTaskCreateCandidate(value: unknown): TaskCreateCandidateV1 {
111
125
  priority: enumValue(candidate.priority, PRIORITIES, 'priority'),
112
126
  effort: enumValue(candidate.effort, EFFORTS, 'effort'),
113
127
  description,
128
+ ...(deliveryRemote === undefined ? {} : { deliveryRemote }),
129
+ ...(deliveryBaseRef === undefined ? {} : { deliveryBaseRef }),
114
130
  taskInput
115
131
  };
116
132
  }
@@ -127,6 +143,8 @@ function canonicalTaskCreateCandidate(candidate: TaskCreateCandidateV1): string
127
143
  priority: valid.priority,
128
144
  effort: valid.effort,
129
145
  description: valid.description,
146
+ deliveryRemote: valid.deliveryRemote ?? null,
147
+ deliveryBaseRef: valid.deliveryBaseRef ?? null,
130
148
  taskInput: Object.fromEntries(TASK_INPUT_KEYS.map((key) => [key, valid.taskInput[key]]))
131
149
  });
132
150
  }
@@ -170,6 +188,7 @@ function renderTask(params: Readonly<{
170
188
  candidate: TaskCreateCandidateV1;
171
189
  taskId: string;
172
190
  project: string;
191
+ delivery: { remote: string; baseRef: string };
173
192
  timestamp: string;
174
193
  agentInfraVersion: string;
175
194
  keyDigest: string;
@@ -194,7 +213,11 @@ function renderTask(params: Readonly<{
194
213
  'target_date:',
195
214
  'current_step: requirement-analysis',
196
215
  `assigned_to: ${candidate.agent}`,
197
- 'pr_status: pending',
216
+ `pr_delivery_fact: ${JSON.stringify(encodePrDeliveryFact(buildUnboundFact()))}`,
217
+ `delivery_remote: ${candidate.deliveryRemote ?? params.delivery.remote}`,
218
+ `delivery_base_ref: ${candidate.deliveryBaseRef ?? params.delivery.baseRef}`,
219
+ 'checkpoint_commit:',
220
+ 'delivery_remote_head:',
198
221
  `task_create_key_digest: ${params.keyDigest}`,
199
222
  `task_create_candidate_digest: ${params.candidateDigest}`,
200
223
  '---'
@@ -219,12 +242,14 @@ function renderTask(params: Readonly<{
219
242
  return `${content.trimEnd()}\n`;
220
243
  }
221
244
 
222
- function readProject(repoRoot: string): string {
223
- const config = JSON.parse(fs.readFileSync(path.join(repoRoot, '.agents', '.airc.json'), 'utf8')) as { project?: unknown };
245
+ function readProject(repoRoot: string): { project: string; delivery: { remote: string; baseRef: string } } {
246
+ const config = JSON.parse(fs.readFileSync(path.join(repoRoot, '.agents', '.airc.json'), 'utf8')) as { project?: unknown; delivery?: { remote?: unknown; baseRef?: unknown } };
224
247
  if (typeof config.project !== 'string' || !/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(config.project)) {
225
248
  throw new Error('TASK_CREATE_CONFIG_INVALID: project is missing or invalid');
226
249
  }
227
- return config.project;
250
+ const delivery = readDeliveryDefaults(repoRoot);
251
+ if (!delivery.ok) throw new Error(`TASK_CREATE_CONFIG_INVALID: ${delivery.message}`);
252
+ return { project: config.project, delivery: delivery.value };
228
253
  }
229
254
 
230
255
  function assertRealDirectory(directory: string, code: string): void {
@@ -305,7 +330,7 @@ function createLocalTask(value: unknown, options: LocalTaskCreateOptions): Local
305
330
  ensureRealDirectory(path.join(repoRoot, '.agents'), 'TASK_CREATE_WORKSPACE_INVALID');
306
331
  ensureRealDirectory(workspaceRoot, 'TASK_CREATE_WORKSPACE_INVALID');
307
332
  ensureRealDirectory(activeRoot, 'TASK_CREATE_WORKSPACE_INVALID');
308
- const project = readProject(repoRoot);
333
+ const projectConfig = readProject(repoRoot);
309
334
  const canonical = canonicalTaskCreateCandidate(candidate);
310
335
  const candidateDigest = sha256(canonical);
311
336
  const keyDigest = sha256(candidate.idempotencyKey);
@@ -346,7 +371,7 @@ function createLocalTask(value: unknown, options: LocalTaskCreateOptions): Local
346
371
  const templateStat = fs.lstatSync(templatePath);
347
372
  if (!templateStat.isFile() || templateStat.isSymbolicLink()) throw new Error('TASK_CREATE_TEMPLATE_INVALID');
348
373
  const rendered = renderTask({
349
- template: fs.readFileSync(templatePath, 'utf8'), candidate, taskId, project, timestamp,
374
+ template: fs.readFileSync(templatePath, 'utf8'), candidate, taskId, project: projectConfig.project, delivery: projectConfig.delivery, timestamp,
350
375
  agentInfraVersion, keyDigest, candidateDigest
351
376
  });
352
377
  const temporary = path.join(workspaceRoot, `.task-create.tmp.${process.pid}.${randomUUID()}`);
@@ -2,6 +2,8 @@ import { isValidAgentInfraVersion } from '../version.ts';
2
2
  import { parseTypedTaskFrontmatter } from './frontmatter.ts';
3
3
  import { LEDGER_SECTION_MISSING_CODE, LEDGER_SECTION_MISSING_MESSAGE, parseLedgerDocument, validateLedgerRows } from './ledger.ts';
4
4
  import type { LedgerDocument } from './ledger.ts';
5
+ import { parseInvalidationDocument } from './invalidation.ts';
6
+ import type { InvalidationDocument } from './invalidation.ts';
5
7
 
6
8
  type CurrentTaskContractErrorCode = 'TASK_DOCUMENT_INVALID' | 'TASK_CURRENT_CONTRACT_INVALID';
7
9
  type CurrentTaskContractResult =
@@ -9,6 +11,7 @@ type CurrentTaskContractResult =
9
11
  ok: true;
10
12
  metadata: ReturnType<typeof parseTypedTaskFrontmatter>;
11
13
  ledger: LedgerDocument;
14
+ invalidation: InvalidationDocument;
12
15
  }
13
16
  | {
14
17
  ok: false;
@@ -42,7 +45,9 @@ function validateCurrentTaskContract(content: string): CurrentTaskContractResult
42
45
  }
43
46
  const invalid = validateLedgerRows(ledger.rows);
44
47
  if (invalid) return { ok: false, code: 'TASK_CURRENT_CONTRACT_INVALID', message: `${invalid.code}: ${invalid.message}` };
45
- return { ok: true, metadata, ledger };
48
+ const invalidation = parseInvalidationDocument(content);
49
+ if (!invalidation.ok) return { ok: false, code: 'TASK_CURRENT_CONTRACT_INVALID', message: `${invalidation.code}: ${invalidation.message}` };
50
+ return { ok: true, metadata, ledger, invalidation: invalidation.document };
46
51
  }
47
52
 
48
53
  export { validateCurrentTaskContract };