@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
@@ -2,13 +2,16 @@ import fs from "node:fs";
2
2
  import path from "node:path";
3
3
  import process from "node:process";
4
4
  import { execFileSync } from "node:child_process";
5
- import { createGitHubClient } from "./github-client.js";
6
- import { resolvePlatformContext } from "./context.js";
5
+ import { resolvePlatformProviderContext } from "./context.js";
7
6
  import { hasCheckedRequirement, resolveRequirementSection } from "./issue-metadata.js";
8
- import { inspectGitHubIssueMetadata, requirementSectionAnchors } from "./issues.js";
9
- import { listRemoteComments } from "./issue-comments.js";
7
+ import { requirementSectionAnchors } from "./issues.js";
10
8
  import { taskTypeLabel } from "./metadata-labels.js";
9
+ import { planInLabelUpdate, validateInLabelMapping, validateRepositoryLabelPayload } from "./in-label-sync.js";
11
10
  import { inspectGitHubPullRequest } from "./pull-requests.js";
11
+ import { createGitHubClient } from "./github-client.js";
12
+ import { readPrDeliveryFact } from "../task/pr-delivery-fact.js";
13
+ import { providerError, providerOperationContext, resourceIdentityNumber, unsupportedProviderOperation } from "./provider-bridge.js";
14
+ import { taskIssueIdentity } from "./task-identities.js";
12
15
  const CHECK_TYPE = "platform-sync";
13
16
  const VERSION_LINE_REGEX = /^[0-9]+\.[0-9]+\.x$/;
14
17
  const FRONTMATTER_FIELD_MAP = {
@@ -25,7 +28,6 @@ const OPTION_LOCALIZATION = {
25
28
  };
26
29
  let activeShared = null;
27
30
  let repoRoot = "";
28
- const githubClient = createGitHubClient();
29
31
  export function getDefaults() {
30
32
  return {
31
33
  statusLabels: {
@@ -78,20 +80,14 @@ function blockedResult(...args) {
78
80
  function safeStat(...args) {
79
81
  return getShared().safeStat(...args);
80
82
  }
81
- function parseIssueNumber(...args) {
82
- return getShared().parseIssueNumber(...args);
83
- }
84
- function parsePrNumber(...args) {
85
- return getShared().parsePrNumber(...args);
86
- }
87
- export function check({ taskDir, config, artifactFile }, shared) {
83
+ export async function check({ taskDir, config, artifactFile }, shared) {
88
84
  activeShared = shared;
89
85
  repoRoot = shared.repoRoot;
90
- const context = buildSyncContext({ taskDir, config, artifactFile });
86
+ const context = await buildSyncContext({ taskDir, config, artifactFile });
91
87
  if (context.earlyReturn) {
92
88
  return context.earlyReturn;
93
89
  }
94
- const remoteData = fetchRemoteData(context);
90
+ const remoteData = await fetchRemoteData(context);
95
91
  if (remoteData.earlyReturn) {
96
92
  return remoteData.earlyReturn;
97
93
  }
@@ -119,25 +115,32 @@ export function check({ taskDir, config, artifactFile }, shared) {
119
115
  return result;
120
116
  }
121
117
  }
122
- return passResult(CHECK_TYPE, `GitHub sync checks passed for Issue #${context.issueNumber}`);
118
+ return passResult(CHECK_TYPE, `Platform sync checks passed for Issue ${context.issueNumber || "identity"}`);
123
119
  }
124
- function buildSyncContext({ taskDir, config, artifactFile }) {
120
+ async function buildSyncContext({ taskDir, config, artifactFile }) {
125
121
  const task = loadTask(taskDir);
126
122
  if (!task.ok) {
127
123
  return { earlyReturn: failResult(CHECK_TYPE, task.message) };
128
124
  }
129
- const issueNumber = parseIssueNumber(task.metadata.issue_number);
130
- const prNumber = parsePrNumber(task.metadata.pr_number);
131
- if (config.when === "issue_number_exists" && !issueNumber) {
125
+ const issueIdentity = taskIssueIdentity(task.metadata);
126
+ const issueNumber = resourceIdentityNumber(issueIdentity);
127
+ const fact = readPrDeliveryFact(task.metadata);
128
+ if (fact.status === "invalid") {
129
+ return { earlyReturn: failResult(CHECK_TYPE, fact.error.message, "check_failed") };
130
+ }
131
+ const prIdentity = fact.status === "valid" && fact.fact.state === "bound" ? fact.fact.identity.resource : null;
132
+ const prNumber = resourceIdentityNumber(prIdentity);
133
+ if (config.when === "issue_number_exists" && !issueIdentity) {
132
134
  return { earlyReturn: passResult(CHECK_TYPE, "Skipped: task has no issue_number") };
133
135
  }
134
- if (config.when === "pr_number_exists" && !prNumber) {
135
- return { earlyReturn: passResult(CHECK_TYPE, "Skipped: task has no pr_number") };
136
+ if (config.when === "pr_fact_bound" && !prIdentity) {
137
+ return { earlyReturn: passResult(CHECK_TYPE, "Skipped: task has no verified bound pull request") };
136
138
  }
137
- if (!issueNumber) {
139
+ if (!issueIdentity) {
138
140
  return { earlyReturn: passResult(CHECK_TYPE, "Skipped: platform-sync not required for this task") };
139
141
  }
140
- const platformContext = resolvePlatformContext({ cwd: repoRoot, client: githubClient });
142
+ const loaded = await resolvePlatformProviderContext({ cwd: repoRoot });
143
+ const platformContext = loaded.ok ? loaded.value.context : loaded.context;
141
144
  if (platformContext.status === "failed") {
142
145
  return { earlyReturn: failResult(CHECK_TYPE, platformContext.error?.message || "Platform context failed", "check_failed") };
143
146
  }
@@ -150,7 +153,6 @@ function buildSyncContext({ taskDir, config, artifactFile }) {
150
153
  }
151
154
  return { earlyReturn: passResult(CHECK_TYPE, `Skipped: ${platformContext.error?.message || "platform unavailable"}`) };
152
155
  }
153
- const repoOwnerType = detectRepoOwnerType(platformContext.platform.repository, taskDir);
154
156
  const expectedValues = resolveExpectedValues(config);
155
157
  if (!expectedValues.ok) {
156
158
  return { earlyReturn: failResult(CHECK_TYPE, expectedValues.message, "check_failed") };
@@ -168,15 +170,20 @@ function buildSyncContext({ taskDir, config, artifactFile }) {
168
170
  config,
169
171
  artifactFile,
170
172
  artifactPath,
173
+ issueIdentity,
174
+ prIdentity,
171
175
  issueNumber,
172
176
  prNumber,
173
177
  upstreamRepo: platformContext.platform.repository,
174
- repoOwnerType,
178
+ repoOwnerType: String(loaded.ok ? loaded.value.snapshot.metadata?.ownerType || "unknown" : "unknown"),
175
179
  hasTriage: platformContext.capabilities.triage,
176
180
  hasPush: platformContext.capabilities.push,
177
181
  expectedStatusLabel: expectedValues.statusLabel,
178
182
  marker,
179
- prMarker
183
+ prMarker,
184
+ provider: loaded.ok ? loaded.value.provider : null,
185
+ providerType: loaded.ok ? loaded.value.providerType : null,
186
+ loadedContext: loaded.ok ? loaded.value : null
180
187
  };
181
188
  }
182
189
  function resolveExpectedValues(config) {
@@ -225,139 +232,69 @@ function resolveDefaultValue({ collection, key, value, configKey }) {
225
232
  }
226
233
  return { ok: true, value: resolvedValue };
227
234
  }
228
- function fetchRemoteData(context) {
229
- const sharedIssue = inspectGitHubIssueMetadata(githubClient, context.upstreamRepo, context.issueNumber, context.taskDir);
230
- const issueResult = sharedIssue.ok
231
- ? {
232
- ok: true,
233
- value: {
234
- state: sharedIssue.value.state.toUpperCase(),
235
- labels: sharedIssue.value.labels.map((name) => ({ name })),
236
- body: sharedIssue.value.body,
237
- milestone: sharedIssue.value.milestone ? { title: sharedIssue.value.milestone } : null
238
- }
239
- }
240
- : {
241
- ok: false,
242
- type: sharedIssue.error.retryable ? "network_error" : "check_failed",
243
- message: sharedIssue.error.message
244
- };
245
- if (!issueResult.ok) {
246
- return {
247
- earlyReturn: issueResult.type === "check_failed"
248
- ? failResult(CHECK_TYPE, issueResult.message, issueResult.type)
249
- : blockedResult(CHECK_TYPE, issueResult.message, issueResult.type)
250
- };
251
- }
252
- const issue = issueResult.value;
253
- if (context.config.issue_must_exist !== false && !issue) {
235
+ async function fetchRemoteData(context) {
236
+ const provider = context.provider;
237
+ const operationContext = providerOperationContext(context.loadedContext, context.taskDir);
238
+ const facts = provider?.verification?.fetchRemoteFacts
239
+ ? await provider.verification.fetchRemoteFacts({
240
+ context: operationContext,
241
+ taskId: context.task?.metadata?.id || context.task?.id || "",
242
+ ...(context.issueIdentity ? { issue: context.issueIdentity } : {}),
243
+ ...(context.prIdentity ? { changeRequest: context.prIdentity } : {}),
244
+ includeComments: shouldFetchComments(context.config),
245
+ includeFields: Boolean(context.config.verify_issue_fields)
246
+ })
247
+ : unsupportedProviderOperation(provider, "verification.fetchRemoteFacts");
248
+ if (!facts.ok) {
254
249
  return {
255
- earlyReturn: failResult(CHECK_TYPE, `Issue #${context.issueNumber} not found`, "check_failed")
250
+ earlyReturn: facts.error.retryable
251
+ ? blockedResult(CHECK_TYPE, providerError(facts.error, "PLATFORM_PROVIDER_OPERATION_FAILED").message, "network_error")
252
+ : failResult(CHECK_TYPE, providerError(facts.error, "PLATFORM_PROVIDER_OPERATION_FAILED").message, "check_failed")
256
253
  };
257
254
  }
258
- let comments = null;
259
- if (shouldFetchComments(context.config)) {
260
- const commentsResult = withRetry(() => ghPaginatedJson([
261
- "api",
262
- "--paginate",
263
- "--slurp",
264
- `repos/${context.upstreamRepo}/issues/${context.issueNumber}/comments?per_page=100`
265
- ], context.taskDir));
266
- if (!commentsResult.ok) {
267
- return {
268
- earlyReturn: commentsResult.type === "check_failed"
269
- ? failResult(CHECK_TYPE, commentsResult.message, commentsResult.type)
270
- : blockedResult(CHECK_TYPE, commentsResult.message, commentsResult.type)
271
- };
272
- }
273
- comments = flattenComments(commentsResult.value);
274
- }
275
- let prComments = null;
276
- if (context.prMarker) {
277
- if (!context.prNumber) {
278
- return {
279
- earlyReturn: failResult(CHECK_TYPE, "Expected a valid pr_number for PR comment verification", "check_failed")
280
- };
281
- }
282
- const prCommentsResult = listRemoteComments(githubClient, context.upstreamRepo, context.prNumber, context.taskDir);
283
- if (!prCommentsResult.ok) {
284
- return {
285
- earlyReturn: prCommentsResult.error.retryable
286
- ? blockedResult(CHECK_TYPE, prCommentsResult.error.message, "network_error")
287
- : failResult(CHECK_TYPE, prCommentsResult.error.message, "check_failed")
288
- };
289
- }
290
- prComments = prCommentsResult.value;
291
- }
292
- let issueType;
293
- if (context.config.verify_issue_type && context.hasPush) {
294
- const issueTypeResult = withRetry(() => ghText([
295
- "api",
296
- `repos/${context.upstreamRepo}/issues/${context.issueNumber}`,
297
- "--jq",
298
- ".type.name // empty"
299
- ], context.taskDir));
300
- if (issueTypeResult.ok) {
301
- issueType = issueTypeResult.value || null;
255
+ const issueSnapshot = facts.value.issue;
256
+ const issue = issueSnapshot
257
+ ? {
258
+ state: issueSnapshot.state.toUpperCase(),
259
+ labels: issueSnapshot.labels.map((name) => ({ name })),
260
+ body: issueSnapshot.body,
261
+ milestone: issueSnapshot.milestone ? { title: issueSnapshot.milestone } : null
302
262
  }
303
- }
263
+ : null;
304
264
  let issueFields;
305
- if (context.config.verify_issue_fields && context.hasPush) {
306
- const [owner, name] = context.upstreamRepo.split("/");
307
- const issueFieldsResult = withRetry(() => ghJson([
308
- "api",
309
- "graphql",
310
- "-f",
311
- `query=${ISSUE_FIELDS_QUERY}`,
312
- "-F",
313
- `owner=${owner}`,
314
- "-F",
315
- `name=${name}`,
316
- "-F",
317
- `number=${context.issueNumber}`
318
- ], context.taskDir));
319
- if (issueFieldsResult.ok) {
320
- issueFields = normalizeIssueFields(issueFieldsResult.value);
321
- }
265
+ if (context.config.verify_issue_fields && issueSnapshot?.issueType) {
266
+ const fieldKinds = new Map(issueSnapshot.issueType.fields.map((field) => [field.name, field.kind]));
267
+ issueFields = {
268
+ pinnedNames: new Set(issueSnapshot.issueType.fields.map((field) => field.name)),
269
+ values: new Map(Object.entries(issueSnapshot.fields).map(([name, value]) => [name, { kind: fieldKinds.get(name) || (typeof value === "number" ? "number" : "single-select"), value }]))
270
+ };
322
271
  }
323
- let prLabels = null;
324
- let prMilestone;
325
- let prAssignees;
326
- let prHeadSha;
327
- if (((context.config.verify_in_labels_match_pr && context.hasTriage)
328
- || (context.config.verify_pr_type_label && context.hasTriage)
329
- || (context.config.verify_milestone && context.hasTriage)
330
- || (context.config.verify_pr_assignee && context.hasPush)
331
- || context.config.verify_pr_comment_last_commit_matches_head) && context.prNumber) {
332
- const prResult = inspectGitHubPullRequest(githubClient, context.upstreamRepo, context.prNumber, context.taskDir);
333
- if (!prResult.ok) {
272
+ let prComments = null;
273
+ if (context.prMarker && context.prIdentity && shouldFetchComments(context.config) && provider?.comments?.list) {
274
+ const listed = await provider.comments.list({ context: operationContext, parent: context.prIdentity });
275
+ if (!listed.ok)
334
276
  return {
335
- earlyReturn: prResult.error.retryable
336
- ? blockedResult(CHECK_TYPE, prResult.error.message, "network_error")
337
- : failResult(CHECK_TYPE, prResult.error.message, "check_failed")
277
+ earlyReturn: listed.error.retryable
278
+ ? blockedResult(CHECK_TYPE, listed.error.message, "network_error")
279
+ : failResult(CHECK_TYPE, listed.error.message, "check_failed")
338
280
  };
339
- }
340
- prLabels = (context.config.verify_in_labels_match_pr || context.config.verify_pr_type_label)
341
- ? prResult.value.labels
342
- : null;
343
- prMilestone = context.config.verify_milestone
344
- ? prResult.value.milestone ? { title: prResult.value.milestone } : null
345
- : undefined;
346
- prAssignees = context.config.verify_pr_assignee
347
- ? prResult.value.assignees
348
- : undefined;
349
- prHeadSha = prResult.value.head.sha;
281
+ prComments = listed.value.map((comment) => ({ id: comment.id, body: comment.body }));
350
282
  }
283
+ const changeRequest = facts.value.changeRequest;
351
284
  return {
352
285
  issue,
353
- comments,
286
+ comments: facts.value.comments.map((comment) => ({ id: comment.id, body: comment.body })),
354
287
  prComments,
355
- prLabels,
356
- issueType,
288
+ prLabels: changeRequest?.labels || null,
289
+ issueType: issueSnapshot
290
+ ? (issueSnapshot.issueType ? issueSnapshot.issueType.name : null)
291
+ : undefined,
357
292
  issueFields,
358
- prMilestone,
359
- prAssignees,
360
- prHeadSha
293
+ prMilestone: changeRequest
294
+ ? (changeRequest.milestone ? { title: changeRequest.milestone } : null)
295
+ : undefined,
296
+ prAssignees: changeRequest?.assignees,
297
+ prHeadSha: changeRequest?.headSha
361
298
  };
362
299
  }
363
300
  function mapTaskTypeToLabel(taskType) {
@@ -372,12 +309,6 @@ function shouldFetchComments(config) {
372
309
  || config.verify_comment_content
373
310
  || config.verify_task_comment_content);
374
311
  }
375
- function flattenComments(value) {
376
- if (!Array.isArray(value)) {
377
- return [];
378
- }
379
- return value.flatMap((page) => Array.isArray(page) ? page : []);
380
- }
381
312
  function checkStatusLabel(context, remoteData) {
382
313
  if (!context.expectedStatusLabel || !context.hasTriage) {
383
314
  return null;
@@ -536,7 +467,7 @@ function checkInLabelsComputed(context, remoteData) {
536
467
  if (!context.config.verify_in_labels_computed || !context.hasTriage) {
537
468
  return null;
538
469
  }
539
- const expectedInLabels = computeExpectedInLabels(context.taskDir);
470
+ const expectedInLabels = computeExpectedInLabels(context.taskDir, context.upstreamRepo);
540
471
  if (!expectedInLabels.ok) {
541
472
  return expectedInLabels.type === "check_failed"
542
473
  ? failResult(CHECK_TYPE, expectedInLabels.message, expectedInLabels.type)
@@ -776,7 +707,6 @@ function mapTaskTypeToIssueType(taskType) {
776
707
  };
777
708
  return mapping[taskType] || "Task";
778
709
  }
779
- 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}}}}}}}}`;
780
710
  function normalizeIssueFields(payload) {
781
711
  const issue = payload?.data?.repository?.issue;
782
712
  const pinnedFields = Array.isArray(issue?.issueType?.pinnedFields)
@@ -837,10 +767,18 @@ function arraysEqual(left, right) {
837
767
  function formatLabelList(labels) {
838
768
  return labels.length > 0 ? labels.join(", ") : "none";
839
769
  }
840
- function computeExpectedInLabels(taskDir) {
841
- const changedFilesResult = gitText(["diff", "main...HEAD", "--name-only"], taskDir);
770
+ function computeExpectedInLabels(taskDir, repository) {
771
+ const task = loadTask(taskDir);
772
+ if (!task.ok) {
773
+ return task;
774
+ }
775
+ const baseRef = String(task.metadata?.delivery_base_ref || "").trim();
776
+ if (!baseRef) {
777
+ return { ok: false, type: "check_failed", message: "Task has no delivery_base_ref for in-label evidence" };
778
+ }
779
+ const changedFilesResult = gitText(["diff", `${baseRef}...HEAD`, "--name-only"], taskDir);
842
780
  if (!changedFilesResult.ok) {
843
- return changedFilesResult;
781
+ return { ...changedFilesResult, type: "network_error" };
844
782
  }
845
783
  const changedFiles = String(changedFilesResult.value || "")
846
784
  .split(/\r?\n/)
@@ -850,45 +788,30 @@ function computeExpectedInLabels(taskDir) {
850
788
  if (!mapping.ok) {
851
789
  return mapping;
852
790
  }
853
- if (Object.keys(mapping.value ?? {}).length > 0) {
854
- const labels = new Set();
855
- for (const file of changedFiles) {
856
- for (const [label, prefixes] of Object.entries(mapping.value ?? {})) {
857
- if (prefixes.some((prefix) => file.startsWith(prefix))) {
858
- labels.add(`in: ${label}`);
859
- }
860
- }
861
- }
862
- return { ok: true, labels: Array.from(labels).sort(), mode: "mapped" };
863
- }
864
- const repoLabelsResult = withRetry(() => ghJson([
865
- "label",
866
- "list",
867
- "--limit",
868
- "200",
869
- "--json",
870
- "name"
871
- ], taskDir));
791
+ if (Object.keys(mapping.value ?? {}).length === 0) {
792
+ return { ok: true, labels: [], mode: "mapped" };
793
+ }
794
+ const client = createGitHubClient();
795
+ const repoLabelsResult = withRetry(() => client.json([
796
+ "api", "--paginate", "--slurp", `repos/${repository}/labels?per_page=100`
797
+ ], { cwd: taskDir }));
872
798
  if (!repoLabelsResult.ok) {
873
799
  return repoLabelsResult;
874
800
  }
875
- const repoInLabels = new Set(extractLabelNames(repoLabelsResult.value)
876
- .filter((label) => label.startsWith("in:")));
877
- if (repoInLabels.size === 0) {
878
- return { ok: true, labels: [], mode: "fallback" };
801
+ const repositoryLabels = validateRepositoryLabelPayload(repoLabelsResult.value);
802
+ if (!repositoryLabels.ok) {
803
+ return { ok: false, type: "check_failed", message: repositoryLabels.error.message };
879
804
  }
880
- const labels = new Set();
881
- for (const file of changedFiles) {
882
- const topLevel = file.split("/")[0];
883
- if (!topLevel) {
884
- continue;
885
- }
886
- const candidate = `in: ${topLevel}`;
887
- if (repoInLabels.has(candidate)) {
888
- labels.add(candidate);
889
- }
805
+ const planned = planInLabelUpdate({
806
+ changedFiles,
807
+ currentLabels: [],
808
+ mapping: mapping.value ?? {},
809
+ repositoryLabels: new Set(repositoryLabels.value)
810
+ });
811
+ if (planned.error) {
812
+ return { ok: false, type: "check_failed", message: planned.error.message };
890
813
  }
891
- return { ok: true, labels: Array.from(labels).sort(), mode: "fallback" };
814
+ return { ok: true, labels: planned.target, mode: "mapped" };
892
815
  }
893
816
  function loadInLabelMapping() {
894
817
  const configPath = path.join(repoRoot, ".agents", ".airc.json");
@@ -897,60 +820,16 @@ function loadInLabelMapping() {
897
820
  }
898
821
  try {
899
822
  const config = JSON.parse(fs.readFileSync(configPath, "utf8"));
900
- const mapping = config?.labels?.in;
901
- if (!mapping || typeof mapping !== "object" || Array.isArray(mapping)) {
902
- return { ok: true, value: {} };
903
- }
904
- const normalized = {};
905
- for (const [label, prefixes] of Object.entries(mapping)) {
906
- if (!Array.isArray(prefixes)) {
907
- continue;
908
- }
909
- const cleaned = prefixes
910
- .map((value) => String(value || "").trim())
911
- .filter(Boolean);
912
- if (cleaned.length > 0) {
913
- normalized[label] = cleaned;
914
- }
915
- }
916
- return { ok: true, value: normalized };
823
+ const mapping = validateInLabelMapping(config?.labels?.in);
824
+ return mapping.ok
825
+ ? { ok: true, value: mapping.value }
826
+ : { ok: false, type: "check_failed", message: mapping.error.message };
917
827
  }
918
828
  catch (error) {
919
829
  return { ok: false, type: "check_failed", message: `Unable to parse .agents/.airc.json: ${error.message}` };
920
830
  }
921
831
  }
922
832
  // === GitHub API ===
923
- function detectRepoOwnerType(upstreamRepo, taskDir) {
924
- const ownerTypeResult = withRetry(() => ghText([
925
- "api",
926
- `repos/${upstreamRepo}`,
927
- "--jq",
928
- ".owner.type // empty"
929
- ], taskDir));
930
- if (!ownerTypeResult.ok) {
931
- return "unknown";
932
- }
933
- return ownerTypeResult.value || "unknown";
934
- }
935
- function ghJson(args, cwd) {
936
- return mapClientResult(githubClient.json(args, { cwd }));
937
- }
938
- function ghText(args, cwd) {
939
- return mapClientResult(githubClient.text(args, { cwd }));
940
- }
941
- function ghPaginatedJson(args, cwd) {
942
- return ghJson(args, cwd);
943
- }
944
- function mapClientResult(result) {
945
- if (result.ok)
946
- return result;
947
- const checkFailed = ["RESOURCE_NOT_FOUND", "PERMISSION_DENIED", "PLATFORM_REQUEST_INVALID"].includes(result.error.code);
948
- return {
949
- ok: false,
950
- type: checkFailed ? "check_failed" : "network_error",
951
- message: result.error.message
952
- };
953
- }
954
833
  function gitText(args, cwd) {
955
834
  try {
956
835
  const value = execFileSync("git", args, { cwd, encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] });
@@ -1,6 +1,9 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
- import { parseTaskFrontmatter } from '../task/frontmatter.js';
3
+ import { parseTypedTaskFrontmatter } from '../task/frontmatter.js';
4
+ import { readPrDeliveryFact } from '../task/pr-delivery-fact.js';
5
+ import { resourceIdentityEquals, resourceIdentityNumber } from '../platform/resource-identity.js';
6
+ import { taskIssueIdentity } from '../platform/task-identities.js';
4
7
  export class HostResolutionError extends Error {
5
8
  code;
6
9
  constructor(code, message) {
@@ -38,7 +41,7 @@ export function extractClosingIssueNumbers(body) {
38
41
  /**
39
42
  * Scan `.agents/workspace/active/{task-id}/task.md` files for tasks bound to the
40
43
  * PR number or to one of the PR's closing issues. A task is emitted at most once;
41
- * a direct `pr_number` hit takes priority over the issue-number reverse lookup.
44
+ * a verified fact identity hit takes priority over the issue-number reverse lookup.
42
45
  */
43
46
  export function collectHostCandidates(input) {
44
47
  const activeRoot = path.join(input.workspaceRoot, '.agents', 'workspace', 'active');
@@ -62,14 +65,30 @@ export function collectHostCandidates(input) {
62
65
  catch {
63
66
  continue;
64
67
  }
65
- const frontmatter = parseTaskFrontmatter(content);
66
- const issueNumber = toPositiveNumber(frontmatter.issue_number);
67
- const prNumber = toPositiveNumber(frontmatter.pr_number);
68
- if (prNumber === input.prNumber) {
69
- candidates.push({ taskId: entry.name, taskDir, issueNumber, prNumber });
68
+ let frontmatter;
69
+ try {
70
+ frontmatter = parseTypedTaskFrontmatter(content);
71
+ }
72
+ catch {
73
+ continue;
74
+ }
75
+ const issueIdentity = taskIssueIdentity(frontmatter);
76
+ const issueNumber = resourceIdentityNumber(issueIdentity) ?? toPositiveNumber(frontmatter.issue_number);
77
+ const fact = readPrDeliveryFact(frontmatter);
78
+ const prIdentity = fact.status === 'valid' && fact.fact.state === 'bound' ? fact.fact.identity.resource : null;
79
+ const prNumber = resourceIdentityNumber(prIdentity);
80
+ const matchesPr = input.prIdentity
81
+ ? Boolean(prIdentity && resourceIdentityEquals(prIdentity, input.prIdentity))
82
+ : input.prNumber !== undefined && prNumber === input.prNumber;
83
+ const identityDetails = {
84
+ ...(issueIdentity && issueIdentity.kind !== 'number' ? { issueIdentity } : {}),
85
+ ...(prIdentity && prIdentity.kind !== 'number' ? { prIdentity } : {})
86
+ };
87
+ if (matchesPr) {
88
+ candidates.push({ taskId: entry.name, taskDir, issueNumber, prNumber, ...identityDetails });
70
89
  }
71
90
  else if (issueNumber !== null && input.closingIssues.includes(issueNumber)) {
72
- candidates.push({ taskId: entry.name, taskDir, issueNumber, prNumber });
91
+ candidates.push({ taskId: entry.name, taskDir, issueNumber, prNumber, ...identityDetails });
73
92
  }
74
93
  }
75
94
  return candidates;
@@ -89,7 +108,9 @@ export function resolveHostFromCandidates(candidates) {
89
108
  taskId: candidate.taskId,
90
109
  taskDir: candidate.taskDir,
91
110
  issueNumber: candidate.issueNumber ?? 0,
92
- prNumber: candidate.prNumber ?? 0
111
+ prNumber: candidate.prNumber ?? 0,
112
+ ...(candidate.issueIdentity ? { issueIdentity: candidate.issueIdentity } : {}),
113
+ ...(candidate.prIdentity ? { prIdentity: candidate.prIdentity } : {})
93
114
  };
94
115
  }
95
116
  return {