@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,13 +3,16 @@ import path from "node:path";
3
3
  import process from "node:process";
4
4
  import { execFileSync } from "node:child_process";
5
5
 
6
- import { createGitHubClient } from "./github-client.ts";
7
- import { resolvePlatformContext } from "./context.ts";
6
+ import { resolvePlatformProviderContext } from "./context.ts";
8
7
  import { hasCheckedRequirement, resolveRequirementSection } from "./issue-metadata.ts";
9
- import { inspectGitHubIssueMetadata, requirementSectionAnchors } from "./issues.ts";
10
- import { listRemoteComments } from "./issue-comments.ts";
8
+ import { requirementSectionAnchors } from "./issues.ts";
11
9
  import { taskTypeLabel } from "./metadata-labels.ts";
10
+ import { planInLabelUpdate, validateInLabelMapping, validateRepositoryLabelPayload } from "./in-label-sync.ts";
12
11
  import { inspectGitHubPullRequest } from "./pull-requests.ts";
12
+ import { createGitHubClient } from "./github-client.ts";
13
+ import { readPrDeliveryFact } from "../task/pr-delivery-fact.ts";
14
+ import { providerError, providerOperationContext, resourceIdentityNumber, unsupportedProviderOperation } from "./provider-bridge.ts";
15
+ import { taskIssueIdentity } from "./task-identities.ts";
13
16
 
14
17
  const CHECK_TYPE = "platform-sync";
15
18
  const VERSION_LINE_REGEX = /^[0-9]+\.[0-9]+\.x$/;
@@ -28,7 +31,6 @@ const OPTION_LOCALIZATION: Record<string, string> = {
28
31
 
29
32
  let activeShared: any = null;
30
33
  let repoRoot = "";
31
- const githubClient = createGitHubClient();
32
34
 
33
35
  export function getDefaults(): any {
34
36
  return {
@@ -94,23 +96,15 @@ function safeStat(...args: any[]): any {
94
96
  return getShared().safeStat(...args);
95
97
  }
96
98
 
97
- function parseIssueNumber(...args: any[]): any {
98
- return getShared().parseIssueNumber(...args);
99
- }
100
-
101
- function parsePrNumber(...args: any[]): any {
102
- return getShared().parsePrNumber(...args);
103
- }
104
-
105
- export function check({ taskDir, config, artifactFile }: any, shared: any): any {
99
+ export async function check({ taskDir, config, artifactFile }: any, shared: any): Promise<any> {
106
100
  activeShared = shared;
107
101
  repoRoot = shared.repoRoot;
108
- const context = buildSyncContext({ taskDir, config, artifactFile });
102
+ const context = await buildSyncContext({ taskDir, config, artifactFile });
109
103
  if (context.earlyReturn) {
110
104
  return context.earlyReturn;
111
105
  }
112
106
 
113
- const remoteData = fetchRemoteData(context);
107
+ const remoteData = await fetchRemoteData(context);
114
108
  if (remoteData.earlyReturn) {
115
109
  return remoteData.earlyReturn;
116
110
  }
@@ -141,29 +135,36 @@ export function check({ taskDir, config, artifactFile }: any, shared: any): any
141
135
  }
142
136
  }
143
137
 
144
- return passResult(CHECK_TYPE, `GitHub sync checks passed for Issue #${context.issueNumber}`);
138
+ return passResult(CHECK_TYPE, `Platform sync checks passed for Issue ${context.issueNumber || "identity"}`);
145
139
  }
146
140
 
147
- function buildSyncContext({ taskDir, config, artifactFile }: any): any {
141
+ async function buildSyncContext({ taskDir, config, artifactFile }: any): Promise<any> {
148
142
  const task = loadTask(taskDir);
149
143
  if (!task.ok) {
150
144
  return { earlyReturn: failResult(CHECK_TYPE, task.message) };
151
145
  }
152
146
 
153
- const issueNumber = parseIssueNumber(task.metadata.issue_number);
154
- const prNumber = parsePrNumber(task.metadata.pr_number);
155
- if (config.when === "issue_number_exists" && !issueNumber) {
147
+ const issueIdentity = taskIssueIdentity(task.metadata);
148
+ const issueNumber = resourceIdentityNumber(issueIdentity);
149
+ const fact = readPrDeliveryFact(task.metadata);
150
+ if (fact.status === "invalid") {
151
+ return { earlyReturn: failResult(CHECK_TYPE, fact.error.message, "check_failed") };
152
+ }
153
+ const prIdentity = fact.status === "valid" && fact.fact.state === "bound" ? fact.fact.identity.resource : null;
154
+ const prNumber = resourceIdentityNumber(prIdentity);
155
+ if (config.when === "issue_number_exists" && !issueIdentity) {
156
156
  return { earlyReturn: passResult(CHECK_TYPE, "Skipped: task has no issue_number") };
157
157
  }
158
- if (config.when === "pr_number_exists" && !prNumber) {
159
- return { earlyReturn: passResult(CHECK_TYPE, "Skipped: task has no pr_number") };
158
+ if (config.when === "pr_fact_bound" && !prIdentity) {
159
+ return { earlyReturn: passResult(CHECK_TYPE, "Skipped: task has no verified bound pull request") };
160
160
  }
161
161
 
162
- if (!issueNumber) {
162
+ if (!issueIdentity) {
163
163
  return { earlyReturn: passResult(CHECK_TYPE, "Skipped: platform-sync not required for this task") };
164
164
  }
165
165
 
166
- const platformContext = resolvePlatformContext({ cwd: repoRoot, client: githubClient });
166
+ const loaded = await resolvePlatformProviderContext({ cwd: repoRoot });
167
+ const platformContext = loaded.ok ? loaded.value.context : loaded.context;
167
168
  if (platformContext.status === "failed") {
168
169
  return { earlyReturn: failResult(CHECK_TYPE, platformContext.error?.message || "Platform context failed", "check_failed") };
169
170
  }
@@ -176,7 +177,6 @@ function buildSyncContext({ taskDir, config, artifactFile }: any): any {
176
177
  }
177
178
  return { earlyReturn: passResult(CHECK_TYPE, `Skipped: ${platformContext.error?.message || "platform unavailable"}`) };
178
179
  }
179
- const repoOwnerType = detectRepoOwnerType(platformContext.platform.repository, taskDir);
180
180
  const expectedValues = resolveExpectedValues(config);
181
181
  if (!expectedValues.ok) {
182
182
  return { earlyReturn: failResult(CHECK_TYPE, expectedValues.message, "check_failed") };
@@ -196,15 +196,20 @@ function buildSyncContext({ taskDir, config, artifactFile }: any): any {
196
196
  config,
197
197
  artifactFile,
198
198
  artifactPath,
199
+ issueIdentity,
200
+ prIdentity,
199
201
  issueNumber,
200
202
  prNumber,
201
203
  upstreamRepo: platformContext.platform.repository,
202
- repoOwnerType,
204
+ repoOwnerType: String(loaded.ok ? loaded.value.snapshot.metadata?.ownerType || "unknown" : "unknown"),
203
205
  hasTriage: platformContext.capabilities.triage,
204
206
  hasPush: platformContext.capabilities.push,
205
207
  expectedStatusLabel: expectedValues.statusLabel,
206
208
  marker,
207
- prMarker
209
+ prMarker,
210
+ provider: loaded.ok ? loaded.value.provider : null,
211
+ providerType: loaded.ok ? loaded.value.providerType : null,
212
+ loadedContext: loaded.ok ? loaded.value : null
208
213
  };
209
214
  }
210
215
 
@@ -261,170 +266,68 @@ function resolveDefaultValue({ collection, key, value, configKey }: any): any {
261
266
  return { ok: true, value: resolvedValue };
262
267
  }
263
268
 
264
- function fetchRemoteData(context: any): any {
265
- const sharedIssue = inspectGitHubIssueMetadata(
266
- githubClient,
267
- context.upstreamRepo,
268
- context.issueNumber,
269
- context.taskDir
270
- );
271
- const issueResult = sharedIssue.ok
272
- ? {
273
- ok: true,
274
- value: {
275
- state: sharedIssue.value.state.toUpperCase(),
276
- labels: sharedIssue.value.labels.map((name) => ({ name })),
277
- body: sharedIssue.value.body,
278
- milestone: sharedIssue.value.milestone ? { title: sharedIssue.value.milestone } : null
279
- }
280
- }
281
- : {
282
- ok: false,
283
- type: sharedIssue.error.retryable ? "network_error" : "check_failed",
284
- message: sharedIssue.error.message
285
- };
286
- if (!issueResult.ok) {
287
- return {
288
- earlyReturn: issueResult.type === "check_failed"
289
- ? failResult(CHECK_TYPE, issueResult.message, issueResult.type)
290
- : blockedResult(CHECK_TYPE, issueResult.message, issueResult.type)
291
- };
292
- }
293
-
294
- const issue = issueResult.value;
295
- if (context.config.issue_must_exist !== false && !issue) {
269
+ async function fetchRemoteData(context: any): Promise<any> {
270
+ const provider = context.provider;
271
+ const operationContext = providerOperationContext(context.loadedContext, context.taskDir);
272
+ const facts = provider?.verification?.fetchRemoteFacts
273
+ ? await provider.verification.fetchRemoteFacts({
274
+ context: operationContext,
275
+ taskId: context.task?.metadata?.id || context.task?.id || "",
276
+ ...(context.issueIdentity ? { issue: context.issueIdentity } : {}),
277
+ ...(context.prIdentity ? { changeRequest: context.prIdentity } : {}),
278
+ includeComments: shouldFetchComments(context.config),
279
+ includeFields: Boolean(context.config.verify_issue_fields)
280
+ })
281
+ : unsupportedProviderOperation(provider, "verification.fetchRemoteFacts");
282
+ if (!facts.ok) {
296
283
  return {
297
- earlyReturn: failResult(CHECK_TYPE, `Issue #${context.issueNumber} not found`, "check_failed")
284
+ earlyReturn: facts.error.retryable
285
+ ? blockedResult(CHECK_TYPE, providerError(facts.error, "PLATFORM_PROVIDER_OPERATION_FAILED").message, "network_error")
286
+ : failResult(CHECK_TYPE, providerError(facts.error, "PLATFORM_PROVIDER_OPERATION_FAILED").message, "check_failed")
298
287
  };
299
288
  }
300
-
301
- let comments = null;
302
- if (shouldFetchComments(context.config)) {
303
- const commentsResult = withRetry(() => ghPaginatedJson([
304
- "api",
305
- "--paginate",
306
- "--slurp",
307
- `repos/${context.upstreamRepo}/issues/${context.issueNumber}/comments?per_page=100`
308
- ], context.taskDir));
309
-
310
- if (!commentsResult.ok) {
311
- return {
312
- earlyReturn: commentsResult.type === "check_failed"
313
- ? failResult(CHECK_TYPE, commentsResult.message, commentsResult.type)
314
- : blockedResult(CHECK_TYPE, commentsResult.message, commentsResult.type)
315
- };
289
+ const issueSnapshot = facts.value.issue;
290
+ const issue = issueSnapshot
291
+ ? {
292
+ state: issueSnapshot.state.toUpperCase(),
293
+ labels: issueSnapshot.labels.map((name: string) => ({ name })),
294
+ body: issueSnapshot.body,
295
+ milestone: issueSnapshot.milestone ? { title: issueSnapshot.milestone } : null
316
296
  }
317
-
318
- comments = flattenComments(commentsResult.value);
297
+ : null;
298
+ let issueFields: any;
299
+ if (context.config.verify_issue_fields && issueSnapshot?.issueType) {
300
+ const fieldKinds = new Map(issueSnapshot.issueType.fields.map((field: { name: string; kind: string }) => [field.name, field.kind]));
301
+ issueFields = {
302
+ pinnedNames: new Set(issueSnapshot.issueType.fields.map((field: { name: string }) => field.name)),
303
+ values: new Map(Object.entries(issueSnapshot.fields).map(([name, value]) => [name, { kind: fieldKinds.get(name) || (typeof value === "number" ? "number" : "single-select"), value }]))
304
+ };
319
305
  }
320
-
321
306
  let prComments = null;
322
- if (context.prMarker) {
323
- if (!context.prNumber) {
324
- return {
325
- earlyReturn: failResult(CHECK_TYPE, "Expected a valid pr_number for PR comment verification", "check_failed")
326
- };
327
- }
328
-
329
- const prCommentsResult = listRemoteComments(
330
- githubClient,
331
- context.upstreamRepo,
332
- context.prNumber,
333
- context.taskDir
334
- );
335
-
336
- if (!prCommentsResult.ok) {
337
- return {
338
- earlyReturn: prCommentsResult.error.retryable
339
- ? blockedResult(CHECK_TYPE, prCommentsResult.error.message, "network_error")
340
- : failResult(CHECK_TYPE, prCommentsResult.error.message, "check_failed")
341
- };
342
- }
343
-
344
- prComments = prCommentsResult.value;
345
- }
346
-
347
- let issueType;
348
- if (context.config.verify_issue_type && context.hasPush) {
349
- const issueTypeResult = withRetry(() => ghText([
350
- "api",
351
- `repos/${context.upstreamRepo}/issues/${context.issueNumber}`,
352
- "--jq",
353
- ".type.name // empty"
354
- ], context.taskDir));
355
-
356
- if (issueTypeResult.ok) {
357
- issueType = issueTypeResult.value || null;
358
- }
359
- }
360
-
361
- let issueFields;
362
- if (context.config.verify_issue_fields && context.hasPush) {
363
- const [owner, name] = context.upstreamRepo.split("/");
364
- const issueFieldsResult = withRetry(() => ghJson([
365
- "api",
366
- "graphql",
367
- "-f",
368
- `query=${ISSUE_FIELDS_QUERY}`,
369
- "-F",
370
- `owner=${owner}`,
371
- "-F",
372
- `name=${name}`,
373
- "-F",
374
- `number=${context.issueNumber}`
375
- ], context.taskDir));
376
-
377
- if (issueFieldsResult.ok) {
378
- issueFields = normalizeIssueFields(issueFieldsResult.value);
379
- }
380
- }
381
-
382
- let prLabels = null;
383
- let prMilestone;
384
- let prAssignees;
385
- let prHeadSha;
386
- if (((context.config.verify_in_labels_match_pr && context.hasTriage)
387
- || (context.config.verify_pr_type_label && context.hasTriage)
388
- || (context.config.verify_milestone && context.hasTriage)
389
- || (context.config.verify_pr_assignee && context.hasPush)
390
- || context.config.verify_pr_comment_last_commit_matches_head) && context.prNumber) {
391
- const prResult = inspectGitHubPullRequest(
392
- githubClient,
393
- context.upstreamRepo,
394
- context.prNumber,
395
- context.taskDir
396
- );
397
-
398
- if (!prResult.ok) {
399
- return {
400
- earlyReturn: prResult.error.retryable
401
- ? blockedResult(CHECK_TYPE, prResult.error.message, "network_error")
402
- : failResult(CHECK_TYPE, prResult.error.message, "check_failed")
403
- };
404
- }
405
-
406
- prLabels = (context.config.verify_in_labels_match_pr || context.config.verify_pr_type_label)
407
- ? prResult.value.labels
408
- : null;
409
- prMilestone = context.config.verify_milestone
410
- ? prResult.value.milestone ? { title: prResult.value.milestone } : null
411
- : undefined;
412
- prAssignees = context.config.verify_pr_assignee
413
- ? prResult.value.assignees
414
- : undefined;
415
- prHeadSha = prResult.value.head.sha;
307
+ if (context.prMarker && context.prIdentity && shouldFetchComments(context.config) && provider?.comments?.list) {
308
+ const listed = await provider.comments.list({ context: operationContext, parent: context.prIdentity });
309
+ if (!listed.ok) return {
310
+ earlyReturn: listed.error.retryable
311
+ ? blockedResult(CHECK_TYPE, listed.error.message, "network_error")
312
+ : failResult(CHECK_TYPE, listed.error.message, "check_failed")
313
+ };
314
+ prComments = listed.value.map((comment: { id: string; body: string }) => ({ id: comment.id, body: comment.body }));
416
315
  }
417
-
316
+ const changeRequest = facts.value.changeRequest;
418
317
  return {
419
318
  issue,
420
- comments,
319
+ comments: facts.value.comments.map((comment: { id: string; body: string }) => ({ id: comment.id, body: comment.body })),
421
320
  prComments,
422
- prLabels,
423
- issueType,
321
+ prLabels: changeRequest?.labels || null,
322
+ issueType: issueSnapshot
323
+ ? (issueSnapshot.issueType ? issueSnapshot.issueType.name : null)
324
+ : undefined,
424
325
  issueFields,
425
- prMilestone,
426
- prAssignees,
427
- prHeadSha
326
+ prMilestone: changeRequest
327
+ ? (changeRequest.milestone ? { title: changeRequest.milestone } : null)
328
+ : undefined,
329
+ prAssignees: changeRequest?.assignees,
330
+ prHeadSha: changeRequest?.headSha
428
331
  };
429
332
  }
430
333
 
@@ -444,14 +347,6 @@ function shouldFetchComments(config: any): any {
444
347
  );
445
348
  }
446
349
 
447
- function flattenComments(value: any): any {
448
- if (!Array.isArray(value)) {
449
- return [];
450
- }
451
-
452
- return value.flatMap((page) => Array.isArray(page) ? page : []);
453
- }
454
-
455
350
  function checkStatusLabel(context: any, remoteData: any): any {
456
351
  if (!context.expectedStatusLabel || !context.hasTriage) {
457
352
  return null;
@@ -710,7 +605,7 @@ function checkInLabelsComputed(context: any, remoteData: any): any {
710
605
  return null;
711
606
  }
712
607
 
713
- const expectedInLabels = computeExpectedInLabels(context.taskDir);
608
+ const expectedInLabels = computeExpectedInLabels(context.taskDir, context.upstreamRepo);
714
609
  if (!expectedInLabels.ok) {
715
610
  return expectedInLabels.type === "check_failed"
716
611
  ? failResult(CHECK_TYPE, expectedInLabels.message, expectedInLabels.type)
@@ -1047,8 +942,6 @@ function mapTaskTypeToIssueType(taskType: any): any {
1047
942
  return mapping[taskType] || "Task";
1048
943
  }
1049
944
 
1050
- const ISSUE_FIELDS_QUERY = `query($owner:String!,$name:String!,$number:Int!){repository(owner:$owner,name:$name){issue(number:$number){issueType{name pinnedFields{__typename ... on IssueFieldSingleSelect{id name} ... on IssueFieldDate{id name} ... on IssueFieldText{id name} ... on IssueFieldNumber{id name}}} issueFieldValues(first:50){nodes{__typename ... on IssueFieldSingleSelectValue{name optionId field{... on IssueFieldSingleSelect{name}}} ... on IssueFieldDateValue{value field{... on IssueFieldDate{name}}} ... on IssueFieldTextValue{value field{... on IssueFieldText{name}}} ... on IssueFieldNumberValue{value field{... on IssueFieldNumber{name}}}}}}}}`;
1051
-
1052
945
  function normalizeIssueFields(payload: any): any {
1053
946
  const issue = payload?.data?.repository?.issue;
1054
947
  const pinnedFields = Array.isArray(issue?.issueType?.pinnedFields)
@@ -1122,10 +1015,18 @@ function formatLabelList(labels: any): any {
1122
1015
  return labels.length > 0 ? labels.join(", ") : "none";
1123
1016
  }
1124
1017
 
1125
- function computeExpectedInLabels(taskDir: any): any {
1126
- const changedFilesResult = gitText(["diff", "main...HEAD", "--name-only"], taskDir);
1018
+ function computeExpectedInLabels(taskDir: any, repository: any): any {
1019
+ const task = loadTask(taskDir);
1020
+ if (!task.ok) {
1021
+ return task;
1022
+ }
1023
+ const baseRef = String(task.metadata?.delivery_base_ref || "").trim();
1024
+ if (!baseRef) {
1025
+ return { ok: false, type: "check_failed", message: "Task has no delivery_base_ref for in-label evidence" };
1026
+ }
1027
+ const changedFilesResult = gitText(["diff", `${baseRef}...HEAD`, "--name-only"], taskDir);
1127
1028
  if (!changedFilesResult.ok) {
1128
- return changedFilesResult;
1029
+ return { ...changedFilesResult, type: "network_error" };
1129
1030
  }
1130
1031
 
1131
1032
  const changedFiles = String(changedFilesResult.value || "")
@@ -1138,55 +1039,32 @@ function computeExpectedInLabels(taskDir: any): any {
1138
1039
  return mapping;
1139
1040
  }
1140
1041
 
1141
- if (Object.keys(mapping.value ?? {}).length > 0) {
1142
- const labels = new Set();
1143
-
1144
- for (const file of changedFiles) {
1145
- for (const [label, prefixes] of Object.entries(mapping.value ?? {}) as Array<[string, string[]]>) {
1146
- if (prefixes.some((prefix: any) => file.startsWith(prefix))) {
1147
- labels.add(`in: ${label}`);
1148
- }
1149
- }
1150
- }
1151
-
1152
- return { ok: true, labels: Array.from(labels).sort(), mode: "mapped" };
1042
+ if (Object.keys(mapping.value ?? {}).length === 0) {
1043
+ return { ok: true, labels: [], mode: "mapped" };
1153
1044
  }
1154
1045
 
1155
- const repoLabelsResult = withRetry(() => ghJson([
1156
- "label",
1157
- "list",
1158
- "--limit",
1159
- "200",
1160
- "--json",
1161
- "name"
1162
- ], taskDir));
1046
+ const client = createGitHubClient();
1047
+ const repoLabelsResult = withRetry(() => client.json([
1048
+ "api", "--paginate", "--slurp", `repos/${repository}/labels?per_page=100`
1049
+ ], { cwd: taskDir }));
1163
1050
  if (!repoLabelsResult.ok) {
1164
1051
  return repoLabelsResult;
1165
1052
  }
1166
1053
 
1167
- const repoInLabels = new Set(
1168
- extractLabelNames(repoLabelsResult.value)
1169
- .filter((label: any) => label.startsWith("in:"))
1170
- );
1171
-
1172
- if (repoInLabels.size === 0) {
1173
- return { ok: true, labels: [], mode: "fallback" };
1054
+ const repositoryLabels = validateRepositoryLabelPayload(repoLabelsResult.value);
1055
+ if (!repositoryLabels.ok) {
1056
+ return { ok: false, type: "check_failed", message: repositoryLabels.error.message };
1174
1057
  }
1175
-
1176
- const labels = new Set();
1177
- for (const file of changedFiles) {
1178
- const topLevel = file.split("/")[0];
1179
- if (!topLevel) {
1180
- continue;
1181
- }
1182
-
1183
- const candidate = `in: ${topLevel}`;
1184
- if (repoInLabels.has(candidate)) {
1185
- labels.add(candidate);
1186
- }
1058
+ const planned = planInLabelUpdate({
1059
+ changedFiles,
1060
+ currentLabels: [],
1061
+ mapping: mapping.value ?? {},
1062
+ repositoryLabels: new Set(repositoryLabels.value)
1063
+ });
1064
+ if (planned.error) {
1065
+ return { ok: false, type: "check_failed", message: planned.error.message };
1187
1066
  }
1188
-
1189
- return { ok: true, labels: Array.from(labels).sort(), mode: "fallback" };
1067
+ return { ok: true, labels: planned.target, mode: "mapped" };
1190
1068
  }
1191
1069
 
1192
1070
  function loadInLabelMapping(): any {
@@ -1197,26 +1075,10 @@ function loadInLabelMapping(): any {
1197
1075
 
1198
1076
  try {
1199
1077
  const config = JSON.parse(fs.readFileSync(configPath, "utf8"));
1200
- const mapping = config?.labels?.in;
1201
- if (!mapping || typeof mapping !== "object" || Array.isArray(mapping)) {
1202
- return { ok: true, value: {} };
1203
- }
1204
-
1205
- const normalized: Record<string, string[]> = {};
1206
- for (const [label, prefixes] of Object.entries(mapping)) {
1207
- if (!Array.isArray(prefixes)) {
1208
- continue;
1209
- }
1210
-
1211
- const cleaned = prefixes
1212
- .map((value) => String(value || "").trim())
1213
- .filter(Boolean);
1214
- if (cleaned.length > 0) {
1215
- normalized[label] = cleaned;
1216
- }
1217
- }
1218
-
1219
- return { ok: true, value: normalized };
1078
+ const mapping = validateInLabelMapping(config?.labels?.in);
1079
+ return mapping.ok
1080
+ ? { ok: true, value: mapping.value }
1081
+ : { ok: false, type: "check_failed", message: mapping.error.message };
1220
1082
  } catch (error: any) {
1221
1083
  return { ok: false, type: "check_failed", message: `Unable to parse .agents/.airc.json: ${error.message}` };
1222
1084
  }
@@ -1224,43 +1086,6 @@ function loadInLabelMapping(): any {
1224
1086
 
1225
1087
  // === GitHub API ===
1226
1088
 
1227
- function detectRepoOwnerType(upstreamRepo: any, taskDir: any): any {
1228
- const ownerTypeResult = withRetry(() => ghText([
1229
- "api",
1230
- `repos/${upstreamRepo}`,
1231
- "--jq",
1232
- ".owner.type // empty"
1233
- ], taskDir));
1234
-
1235
- if (!ownerTypeResult.ok) {
1236
- return "unknown";
1237
- }
1238
-
1239
- return ownerTypeResult.value || "unknown";
1240
- }
1241
-
1242
- function ghJson(args: any, cwd: any): any {
1243
- return mapClientResult(githubClient.json(args, { cwd }));
1244
- }
1245
-
1246
- function ghText(args: any, cwd: any): any {
1247
- return mapClientResult(githubClient.text(args, { cwd }));
1248
- }
1249
-
1250
- function ghPaginatedJson(args: any, cwd: any): any {
1251
- return ghJson(args, cwd);
1252
- }
1253
-
1254
- function mapClientResult(result: any): any {
1255
- if (result.ok) return result;
1256
- const checkFailed = ["RESOURCE_NOT_FOUND", "PERMISSION_DENIED", "PLATFORM_REQUEST_INVALID"].includes(result.error.code);
1257
- return {
1258
- ok: false,
1259
- type: checkFailed ? "check_failed" : "network_error",
1260
- message: result.error.message
1261
- };
1262
- }
1263
-
1264
1089
  function gitText(args: any, cwd: any): any {
1265
1090
  try {
1266
1091
  const value = execFileSync("git", args, { cwd, encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] });
@@ -1,7 +1,11 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
3
 
4
- import { parseTaskFrontmatter } from '../task/frontmatter.ts';
4
+ import { parseTypedTaskFrontmatter } from '../task/frontmatter.ts';
5
+ import { readPrDeliveryFact } from '../task/pr-delivery-fact.ts';
6
+ import { resourceIdentityEquals, resourceIdentityNumber } from '../platform/resource-identity.ts';
7
+ import type { ResourceIdentity } from '../platform/resource-identity.ts';
8
+ import { taskIssueIdentity } from '../platform/task-identities.ts';
5
9
 
6
10
  // ---------------------------------------------------------------------------
7
11
  // Host resolution (PL-4): PR -> unique / ambiguous / none.
@@ -12,10 +16,12 @@ export type HostCandidate = {
12
16
  taskDir: string;
13
17
  issueNumber: number | null;
14
18
  prNumber: number | null;
19
+ issueIdentity?: ResourceIdentity;
20
+ prIdentity?: ResourceIdentity;
15
21
  };
16
22
 
17
23
  export type HostResolution =
18
- | { kind: 'unique'; taskId: string; taskDir: string; issueNumber: number; prNumber: number }
24
+ | { kind: 'unique'; taskId: string; taskDir: string; issueNumber: number; prNumber: number; issueIdentity?: ResourceIdentity; prIdentity?: ResourceIdentity }
19
25
  | { kind: 'ambiguous'; candidates: Array<{ taskId: string; issueNumber: number }> }
20
26
  | { kind: 'none' };
21
27
 
@@ -62,10 +68,11 @@ export function extractClosingIssueNumbers(body: string): number[] {
62
68
  /**
63
69
  * Scan `.agents/workspace/active/{task-id}/task.md` files for tasks bound to the
64
70
  * PR number or to one of the PR's closing issues. A task is emitted at most once;
65
- * a direct `pr_number` hit takes priority over the issue-number reverse lookup.
71
+ * a verified fact identity hit takes priority over the issue-number reverse lookup.
66
72
  */
67
73
  export function collectHostCandidates(input: {
68
- prNumber: number;
74
+ prNumber?: number;
75
+ prIdentity?: ResourceIdentity;
69
76
  closingIssues: number[];
70
77
  workspaceRoot: string;
71
78
  }): HostCandidate[] {
@@ -87,13 +94,24 @@ export function collectHostCandidates(input: {
87
94
  } catch {
88
95
  continue;
89
96
  }
90
- const frontmatter = parseTaskFrontmatter(content);
91
- const issueNumber = toPositiveNumber(frontmatter.issue_number);
92
- const prNumber = toPositiveNumber(frontmatter.pr_number);
93
- if (prNumber === input.prNumber) {
94
- candidates.push({ taskId: entry.name, taskDir, issueNumber, prNumber });
97
+ let frontmatter: ReturnType<typeof parseTypedTaskFrontmatter>;
98
+ try { frontmatter = parseTypedTaskFrontmatter(content); } catch { continue; }
99
+ const issueIdentity = taskIssueIdentity(frontmatter);
100
+ const issueNumber = resourceIdentityNumber(issueIdentity) ?? toPositiveNumber(frontmatter.issue_number);
101
+ const fact = readPrDeliveryFact(frontmatter);
102
+ const prIdentity = fact.status === 'valid' && fact.fact.state === 'bound' ? fact.fact.identity.resource : null;
103
+ const prNumber = resourceIdentityNumber(prIdentity);
104
+ const matchesPr = input.prIdentity
105
+ ? Boolean(prIdentity && resourceIdentityEquals(prIdentity, input.prIdentity))
106
+ : input.prNumber !== undefined && prNumber === input.prNumber;
107
+ const identityDetails = {
108
+ ...(issueIdentity && issueIdentity.kind !== 'number' ? { issueIdentity } : {}),
109
+ ...(prIdentity && prIdentity.kind !== 'number' ? { prIdentity } : {})
110
+ };
111
+ if (matchesPr) {
112
+ candidates.push({ taskId: entry.name, taskDir, issueNumber, prNumber, ...identityDetails });
95
113
  } else if (issueNumber !== null && input.closingIssues.includes(issueNumber)) {
96
- candidates.push({ taskId: entry.name, taskDir, issueNumber, prNumber });
114
+ candidates.push({ taskId: entry.name, taskDir, issueNumber, prNumber, ...identityDetails });
97
115
  }
98
116
  }
99
117
  return candidates;
@@ -113,7 +131,9 @@ export function resolveHostFromCandidates(candidates: HostCandidate[]): HostReso
113
131
  taskId: candidate.taskId,
114
132
  taskDir: candidate.taskDir,
115
133
  issueNumber: candidate.issueNumber ?? 0,
116
- prNumber: candidate.prNumber ?? 0
134
+ prNumber: candidate.prNumber ?? 0,
135
+ ...(candidate.issueIdentity ? { issueIdentity: candidate.issueIdentity } : {}),
136
+ ...(candidate.prIdentity ? { prIdentity: candidate.prIdentity } : {})
117
137
  };
118
138
  }
119
139
  return {