@fitlab-ai/agent-infra 0.9.13 → 0.9.15

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 (428) hide show
  1. package/README.md +10 -0
  2. package/README.zh-CN.md +10 -0
  3. package/bin/cli.ts +26 -9
  4. package/bin/internal-cli.sh +45 -0
  5. package/dist/bin/cli.js +173 -157
  6. package/dist/bin/internal-cli.js +126 -42
  7. package/dist/lib/agent-client.js +5 -0
  8. package/dist/lib/agent-clients/adapters/codex-lifecycle/capability-store.js +165 -27
  9. package/dist/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.js +3 -2
  10. package/dist/lib/defaults.json +103 -103
  11. package/dist/lib/fs/durable-write.js +37 -0
  12. package/dist/lib/fs/file-lock.js +111 -0
  13. package/dist/lib/host-control/audit.js +19 -0
  14. package/dist/lib/host-control/client.js +142 -0
  15. package/dist/lib/host-control/command.js +49 -0
  16. package/dist/lib/host-control/path.js +160 -0
  17. package/dist/lib/host-control/secure-fs.js +140 -0
  18. package/dist/lib/host-control/server.js +159 -0
  19. package/dist/lib/host-control/service.js +112 -0
  20. package/dist/lib/init.js +11 -2
  21. package/dist/lib/internal/agent-client.js +5 -2
  22. package/dist/lib/internal/cli-route-inventory.js +154 -0
  23. package/dist/lib/internal/codex-lifecycle.js +15 -12
  24. package/dist/lib/internal/codex-sandbox-controller.js +8 -2
  25. package/dist/lib/internal/demo-promotion.js +322 -0
  26. package/dist/lib/internal/demo-transcript.js +454 -0
  27. package/dist/lib/internal/git-workflow.js +10 -6
  28. package/dist/lib/internal/host-control.js +28 -0
  29. package/dist/lib/internal/manual-validation.js +322 -0
  30. package/dist/lib/internal/platform-checks.js +16 -4
  31. package/dist/lib/internal/platform-comment.js +16 -4
  32. package/dist/lib/internal/platform-context.js +4 -1
  33. package/dist/lib/internal/platform-issue.js +12 -4
  34. package/dist/lib/internal/platform-metadata.js +88 -0
  35. package/dist/lib/internal/platform-pr-review.js +16 -3
  36. package/dist/lib/internal/platform-pr.js +42 -14
  37. package/dist/lib/internal/platform-release-notes.js +6 -3
  38. package/dist/lib/internal/platform-security.js +93 -0
  39. package/dist/lib/internal/pr-review-grade.js +14 -3
  40. package/dist/lib/internal/release-workflow.js +82 -89
  41. package/dist/lib/internal/sandbox-control.js +171 -7
  42. package/dist/lib/internal/task-activity.js +13 -4
  43. package/dist/lib/internal/task-artifact.js +19 -107
  44. package/dist/lib/internal/task-context.js +4 -1
  45. package/dist/lib/internal/task-create.js +48 -14
  46. package/dist/lib/internal/task-delivery.js +4 -1
  47. package/dist/lib/internal/task-event.js +38 -35
  48. package/dist/lib/internal/task-finalization.js +4 -1
  49. package/dist/lib/internal/task-invalidation.js +4 -1
  50. package/dist/lib/internal/task-ledger.js +12 -1
  51. package/dist/lib/internal/task-lifecycle.js +4 -1
  52. package/dist/lib/internal/task-operation-registry.js +519 -0
  53. package/dist/lib/internal/task-orchestration.js +12 -0
  54. package/dist/lib/internal/task-override.js +10 -3
  55. package/dist/lib/internal/task-qualification.js +90 -0
  56. package/dist/lib/internal/task-review.js +10 -52
  57. package/dist/lib/internal/task-short-id.js +11 -1
  58. package/dist/lib/internal/task-snapshot.js +4 -1
  59. package/dist/lib/internal/task-validate.js +37 -12
  60. package/dist/lib/internal/task-verify.js +5 -1
  61. package/dist/lib/internal/task-warning.js +8 -1
  62. package/dist/lib/owned-path.js +33 -0
  63. package/dist/lib/platform/check-status.js +11 -0
  64. package/dist/lib/platform/comment-safety.js +229 -0
  65. package/dist/lib/platform/completion-backfill.js +1 -1
  66. package/dist/lib/platform/github-data.js +241 -0
  67. package/dist/lib/platform/github-provider.js +248 -26
  68. package/dist/lib/platform/issue-comments.js +146 -34
  69. package/dist/lib/platform/issues.js +1 -179
  70. package/dist/lib/platform/pr-change-report.js +6 -12
  71. package/dist/lib/platform/pr-checks.js +12 -65
  72. package/dist/lib/platform/pr-summary.js +83 -10
  73. package/dist/lib/platform/provider-contract.d.ts +67 -1
  74. package/dist/lib/platform/provider-contract.js +6 -2
  75. package/dist/lib/platform/provider-validation.js +76 -3
  76. package/dist/lib/platform/pull-requests.js +117 -275
  77. package/dist/lib/platform/repository-metadata.js +377 -0
  78. package/dist/lib/platform/security-alerts.js +155 -0
  79. package/dist/lib/platform/snapshots.js +2 -0
  80. package/dist/lib/platform/verification-sync.js +154 -162
  81. package/dist/lib/process-data/index.js +7 -0
  82. package/dist/lib/release/workflow.js +1 -1
  83. package/dist/lib/run/index.js +5 -0
  84. package/dist/lib/sandbox/commands/control-status.js +22 -0
  85. package/dist/lib/sandbox/commands/create.js +39 -8
  86. package/dist/lib/sandbox/commands/ls.js +5 -0
  87. package/dist/lib/sandbox/commands/rm.js +3 -807
  88. package/dist/lib/sandbox/commands/show.js +16 -0
  89. package/dist/lib/sandbox/control/audit.js +203 -0
  90. package/dist/lib/sandbox/control/client.js +125 -12
  91. package/dist/lib/sandbox/control/completed-reentry.js +85 -0
  92. package/dist/lib/sandbox/control/container-identity.js +28 -31
  93. package/dist/lib/sandbox/control/executor.js +233 -34
  94. package/dist/lib/sandbox/control/finalization-response.js +27 -0
  95. package/dist/lib/sandbox/control/identity-sentinel.js +107 -0
  96. package/dist/lib/sandbox/control/lifecycle.js +656 -128
  97. package/dist/lib/sandbox/control/native-file-lock.js +85 -0
  98. package/dist/lib/sandbox/control/protocol.js +55 -4
  99. package/dist/lib/sandbox/control/server.js +720 -79
  100. package/dist/lib/sandbox/control/state.js +63 -3
  101. package/dist/lib/sandbox/control/task-view.js +183 -0
  102. package/dist/lib/sandbox/control/task-workflow.js +74 -0
  103. package/dist/lib/sandbox/control/workflow-executor.js +99 -0
  104. package/dist/lib/sandbox/engines/authority.js +289 -0
  105. package/dist/lib/sandbox/engines/docker-exact-discovery.js +97 -0
  106. package/dist/lib/sandbox/index.js +7 -2
  107. package/dist/lib/sandbox/recovery.js +49 -5
  108. package/dist/lib/sandbox/removal.js +1717 -0
  109. package/dist/lib/sandbox/task-cleanup.js +217 -0
  110. package/dist/lib/sandbox/workspace-view.js +15 -15
  111. package/dist/lib/sandbox/worktree-safety.js +4 -1
  112. package/dist/lib/server/index.js +8 -0
  113. package/dist/lib/task/activity-intent.js +2 -1
  114. package/dist/lib/task/activity-log.js +30 -24
  115. package/dist/lib/task/artifact-command.js +101 -0
  116. package/dist/lib/task/artifact-lifecycle.js +71 -40
  117. package/dist/lib/task/artifact-name.js +36 -0
  118. package/dist/lib/task/artifact-operations.js +474 -0
  119. package/dist/lib/task/artifact-receipts.js +3 -15
  120. package/dist/lib/task/artifact-repair-intent.js +130 -0
  121. package/dist/lib/task/artifact-schema.js +127 -0
  122. package/dist/lib/task/capabilities.js +83 -19
  123. package/dist/lib/task/codex-orchestration.js +71 -25
  124. package/dist/lib/task/commit-identity.js +28 -0
  125. package/dist/lib/task/commit-operation.js +2 -16
  126. package/dist/lib/task/control-authority.js +375 -5
  127. package/dist/lib/task/control-recovery.js +128 -0
  128. package/dist/lib/task/create-service.js +103 -4
  129. package/dist/lib/task/create.js +80 -2
  130. package/dist/lib/task/decision-details.js +2 -46
  131. package/dist/lib/task/events.js +306 -76
  132. package/dist/lib/task/finalization-state.js +9 -0
  133. package/dist/lib/task/human-override.js +3 -6
  134. package/dist/lib/task/implementation-inputs.js +3 -3
  135. package/dist/lib/task/index.js +11 -3
  136. package/dist/lib/task/intermediate-cleanup.js +383 -0
  137. package/dist/lib/task/invalidation.js +18 -65
  138. package/dist/lib/task/ledger-intents.js +7 -22
  139. package/dist/lib/task/ledger.js +1 -3
  140. package/dist/lib/task/lifecycle.js +19 -1
  141. package/dist/lib/task/local-artifact-finalization.js +190 -290
  142. package/dist/lib/task/manual-validation-completion.js +26 -0
  143. package/dist/lib/task/manual-validation-evidence.js +130 -0
  144. package/dist/lib/task/manual-validation-receipt.js +101 -0
  145. package/dist/lib/task/manual-validation-shared.js +40 -0
  146. package/dist/lib/task/manual-validation-transaction.js +243 -0
  147. package/dist/lib/task/markdown.js +47 -0
  148. package/dist/lib/task/orchestration.js +206 -69
  149. package/dist/lib/task/qualification-audit.js +476 -0
  150. package/dist/lib/task/qualification-confirmation.js +109 -0
  151. package/dist/lib/task/qualification-intents.js +148 -0
  152. package/dist/lib/task/review-artifacts.js +23 -40
  153. package/dist/lib/task/review-command.js +25 -0
  154. package/dist/lib/task/review-finalization.js +146 -97
  155. package/dist/lib/task/review-fingerprint.js +2 -2
  156. package/dist/lib/task/sections.js +34 -54
  157. package/dist/lib/task/verification-config.js +11 -0
  158. package/dist/lib/task/verification-engine.js +170 -327
  159. package/dist/lib/task/verification.js +1 -1
  160. package/dist/lib/task/workflow-command.js +17 -0
  161. package/dist/lib/task/workflow-warning-intents.js +0 -16
  162. package/dist/lib/task/workflow-warnings.js +28 -2
  163. package/dist/lib/update.js +2 -0
  164. package/dist/lifecycle-build-manifest.json +3 -3
  165. package/lib/agent-client.ts +9 -0
  166. package/lib/agent-clients/adapters/codex-lifecycle/capability-store.ts +223 -33
  167. package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.ts +3 -2
  168. package/lib/fs/durable-write.ts +29 -0
  169. package/lib/fs/file-lock.ts +113 -0
  170. package/lib/host-control/audit.ts +22 -0
  171. package/lib/host-control/client.ts +172 -0
  172. package/lib/host-control/command.ts +60 -0
  173. package/lib/host-control/path.ts +167 -0
  174. package/lib/host-control/secure-fs.ts +135 -0
  175. package/lib/host-control/server.ts +180 -0
  176. package/lib/host-control/service.ts +133 -0
  177. package/lib/init.ts +15 -2
  178. package/lib/internal/agent-client.ts +4 -2
  179. package/lib/internal/cli-route-inventory.ts +142 -0
  180. package/lib/internal/codex-lifecycle.ts +14 -12
  181. package/lib/internal/codex-sandbox-controller.ts +7 -2
  182. package/lib/internal/demo-promotion.ts +354 -0
  183. package/lib/internal/demo-transcript.ts +419 -0
  184. package/lib/internal/git-workflow.ts +9 -6
  185. package/lib/internal/host-control.ts +28 -0
  186. package/lib/internal/manual-validation.ts +306 -0
  187. package/lib/internal/platform-checks.ts +12 -4
  188. package/lib/internal/platform-comment.ts +12 -4
  189. package/lib/internal/platform-context.ts +3 -1
  190. package/lib/internal/platform-issue.ts +11 -4
  191. package/lib/internal/platform-metadata.ts +70 -0
  192. package/lib/internal/platform-pr-review.ts +14 -3
  193. package/lib/internal/platform-pr.ts +38 -14
  194. package/lib/internal/platform-release-notes.ts +5 -3
  195. package/lib/internal/platform-security.ts +83 -0
  196. package/lib/internal/pr-review-grade.ts +10 -3
  197. package/lib/internal/release-workflow.ts +91 -87
  198. package/lib/internal/sandbox-control.ts +181 -6
  199. package/lib/internal/task-activity.ts +12 -4
  200. package/lib/internal/task-artifact.ts +15 -79
  201. package/lib/internal/task-context.ts +3 -1
  202. package/lib/internal/task-create.ts +76 -15
  203. package/lib/internal/task-delivery.ts +3 -1
  204. package/lib/internal/task-event.ts +21 -10
  205. package/lib/internal/task-finalization.ts +3 -1
  206. package/lib/internal/task-invalidation.ts +3 -1
  207. package/lib/internal/task-ledger.ts +11 -1
  208. package/lib/internal/task-lifecycle.ts +3 -1
  209. package/lib/internal/task-operation-registry.ts +590 -0
  210. package/lib/internal/task-orchestration.ts +12 -0
  211. package/lib/internal/task-override.ts +9 -3
  212. package/lib/internal/task-qualification.ts +61 -0
  213. package/lib/internal/task-review.ts +7 -50
  214. package/lib/internal/task-short-id.ts +10 -1
  215. package/lib/internal/task-snapshot.ts +3 -1
  216. package/lib/internal/task-validate.ts +32 -12
  217. package/lib/internal/task-verify.ts +4 -1
  218. package/lib/internal/task-warning.ts +7 -1
  219. package/lib/owned-path.ts +26 -0
  220. package/lib/platform/check-status.ts +9 -0
  221. package/lib/platform/comment-safety.ts +255 -0
  222. package/lib/platform/completion-backfill.ts +1 -1
  223. package/lib/platform/github-data.ts +322 -0
  224. package/lib/platform/github-provider.ts +244 -27
  225. package/lib/platform/issue-comments.ts +159 -35
  226. package/lib/platform/issues.ts +4 -245
  227. package/lib/platform/pr-change-report.ts +6 -12
  228. package/lib/platform/pr-checks.ts +13 -77
  229. package/lib/platform/pr-summary.ts +88 -11
  230. package/lib/platform/provider-contract.ts +78 -3
  231. package/lib/platform/provider-validation.ts +85 -3
  232. package/lib/platform/pull-requests.ts +129 -348
  233. package/lib/platform/repository-metadata.ts +453 -0
  234. package/lib/platform/security-alerts.ts +191 -0
  235. package/lib/platform/snapshots.ts +53 -0
  236. package/lib/platform/verification-sync.ts +171 -177
  237. package/lib/process-data/index.ts +7 -0
  238. package/lib/release/workflow.ts +3 -3
  239. package/lib/run/index.ts +5 -0
  240. package/lib/sandbox/commands/control-status.ts +26 -0
  241. package/lib/sandbox/commands/create.ts +53 -12
  242. package/lib/sandbox/commands/ls.ts +7 -0
  243. package/lib/sandbox/commands/rm.ts +3 -996
  244. package/lib/sandbox/commands/show.ts +16 -0
  245. package/lib/sandbox/control/audit.ts +253 -0
  246. package/lib/sandbox/control/client.ts +150 -13
  247. package/lib/sandbox/control/completed-reentry.ts +98 -0
  248. package/lib/sandbox/control/container-identity.ts +27 -43
  249. package/lib/sandbox/control/executor.ts +262 -37
  250. package/lib/sandbox/control/finalization-response.ts +29 -0
  251. package/lib/sandbox/control/identity-sentinel.ts +137 -0
  252. package/lib/sandbox/control/lifecycle.ts +672 -18
  253. package/lib/sandbox/control/native-file-lock.ts +108 -0
  254. package/lib/sandbox/control/protocol.ts +80 -8
  255. package/lib/sandbox/control/server.ts +779 -82
  256. package/lib/sandbox/control/state.ts +84 -3
  257. package/lib/sandbox/control/task-view.ts +240 -0
  258. package/lib/sandbox/control/task-workflow.ts +95 -0
  259. package/lib/sandbox/control/workflow-executor.ts +102 -0
  260. package/lib/sandbox/engines/authority.ts +365 -0
  261. package/lib/sandbox/engines/docker-exact-discovery.ts +132 -0
  262. package/lib/sandbox/index.ts +9 -2
  263. package/lib/sandbox/recovery.ts +49 -6
  264. package/lib/sandbox/removal.ts +2144 -0
  265. package/lib/sandbox/task-cleanup.ts +209 -0
  266. package/lib/sandbox/workspace-view.ts +26 -18
  267. package/lib/sandbox/worktree-safety.ts +4 -1
  268. package/lib/server/index.ts +10 -0
  269. package/lib/task/activity-intent.ts +2 -2
  270. package/lib/task/activity-log.ts +27 -19
  271. package/lib/task/artifact-command.ts +104 -0
  272. package/lib/task/artifact-lifecycle.ts +66 -39
  273. package/lib/task/artifact-name.ts +55 -0
  274. package/lib/task/artifact-operations.ts +580 -0
  275. package/lib/task/artifact-receipts.ts +3 -14
  276. package/lib/task/artifact-repair-intent.ts +152 -0
  277. package/lib/task/artifact-schema.ts +163 -0
  278. package/lib/task/capabilities.ts +82 -22
  279. package/lib/task/codex-orchestration.ts +85 -24
  280. package/lib/task/commit-identity.ts +31 -0
  281. package/lib/task/commit-operation.ts +2 -16
  282. package/lib/task/control-authority.ts +513 -6
  283. package/lib/task/control-recovery.ts +174 -0
  284. package/lib/task/create-service.ts +131 -6
  285. package/lib/task/create.ts +83 -1
  286. package/lib/task/decision-details.ts +3 -71
  287. package/lib/task/events.ts +292 -75
  288. package/lib/task/finalization-state.ts +11 -0
  289. package/lib/task/human-override.ts +3 -8
  290. package/lib/task/implementation-inputs.ts +3 -3
  291. package/lib/task/index.ts +16 -3
  292. package/lib/task/intermediate-cleanup.ts +455 -0
  293. package/lib/task/invalidation.ts +18 -62
  294. package/lib/task/ledger-intents.ts +7 -24
  295. package/lib/task/ledger.ts +1 -3
  296. package/lib/task/lifecycle.ts +17 -1
  297. package/lib/task/local-artifact-finalization.ts +223 -388
  298. package/lib/task/manual-validation-completion.ts +40 -0
  299. package/lib/task/manual-validation-evidence.ts +209 -0
  300. package/lib/task/manual-validation-receipt.ts +152 -0
  301. package/lib/task/manual-validation-shared.ts +50 -0
  302. package/lib/task/manual-validation-transaction.ts +304 -0
  303. package/lib/task/markdown.ts +70 -0
  304. package/lib/task/orchestration.ts +191 -68
  305. package/lib/task/qualification-audit.ts +588 -0
  306. package/lib/task/qualification-confirmation.ts +117 -0
  307. package/lib/task/qualification-intents.ts +150 -0
  308. package/lib/task/review-artifacts.ts +21 -41
  309. package/lib/task/review-command.ts +23 -0
  310. package/lib/task/review-finalization.ts +154 -153
  311. package/lib/task/review-fingerprint.ts +2 -2
  312. package/lib/task/sections.ts +33 -52
  313. package/lib/task/verification-config.ts +12 -0
  314. package/lib/task/verification-engine.ts +165 -370
  315. package/lib/task/verification-types.ts +17 -0
  316. package/lib/task/verification.ts +1 -1
  317. package/lib/task/workflow-command.ts +18 -0
  318. package/lib/task/workflow-warning-intents.ts +0 -13
  319. package/lib/task/workflow-warnings.ts +21 -2
  320. package/lib/update.ts +6 -0
  321. package/package.json +7 -2
  322. package/templates/.agents/hooks/lifecycle-delegation.js +6 -6
  323. package/templates/.agents/rules/README.en.md +2 -0
  324. package/templates/.agents/rules/README.zh-CN.md +2 -0
  325. package/templates/.agents/rules/decision-qualification.en.md +22 -0
  326. package/templates/.agents/rules/decision-qualification.zh-CN.md +22 -0
  327. package/templates/.agents/rules/evidence-reporting.en.md +28 -0
  328. package/templates/.agents/rules/evidence-reporting.zh-CN.md +28 -0
  329. package/templates/.agents/rules/label-milestone-setup.en.md +7 -7
  330. package/templates/.agents/rules/label-milestone-setup.zh-CN.md +7 -7
  331. package/templates/.agents/rules/local-artifact-repair.en.md +5 -1
  332. package/templates/.agents/rules/local-artifact-repair.zh-CN.md +5 -1
  333. package/templates/.agents/rules/security-alerts.en.md +8 -8
  334. package/templates/.agents/rules/security-alerts.zh-CN.md +8 -8
  335. package/templates/.agents/rules/testing-discipline.en.md +2 -0
  336. package/templates/.agents/rules/testing-discipline.zh-CN.md +2 -0
  337. package/templates/.agents/scripts/package.json +3 -0
  338. package/templates/.agents/skills/analyze-task/SKILL.en.md +15 -2
  339. package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +19 -2
  340. package/templates/.agents/skills/analyze-task/config/verify.en.json +1 -12
  341. package/templates/.agents/skills/analyze-task/config/verify.zh-CN.json +1 -12
  342. package/templates/.agents/skills/close-codescan/SKILL.en.md +2 -2
  343. package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +2 -2
  344. package/templates/.agents/skills/close-dependabot/SKILL.en.md +2 -2
  345. package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +2 -2
  346. package/templates/.agents/skills/code-task/SKILL.en.md +14 -1
  347. package/templates/.agents/skills/code-task/SKILL.zh-CN.md +14 -1
  348. package/templates/.agents/skills/code-task/config/verify.en.json +5 -14
  349. package/templates/.agents/skills/code-task/config/verify.zh-CN.json +5 -14
  350. package/templates/.agents/skills/code-task/reference/code-rules.en.md +5 -4
  351. package/templates/.agents/skills/code-task/reference/code-rules.zh-CN.md +5 -4
  352. package/templates/.agents/skills/code-task/reference/dual-mode.en.md +1 -1
  353. package/templates/.agents/skills/code-task/reference/dual-mode.zh-CN.md +1 -1
  354. package/templates/.agents/skills/code-task/reference/fix-mode.en.md +3 -1
  355. package/templates/.agents/skills/code-task/reference/fix-mode.zh-CN.md +3 -1
  356. package/templates/.agents/skills/code-task/reference/report-template.en.md +42 -15
  357. package/templates/.agents/skills/code-task/reference/report-template.zh-CN.md +39 -12
  358. package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +37 -21
  359. package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +37 -21
  360. package/templates/.agents/skills/complete-manual-validation/config/verify.en.json +1 -1
  361. package/templates/.agents/skills/complete-manual-validation/config/verify.zh-CN.json +1 -1
  362. package/templates/.agents/skills/complete-manual-validation/reference/report-template.en.md +4 -0
  363. package/templates/.agents/skills/complete-manual-validation/reference/report-template.zh-CN.md +4 -0
  364. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.en.md +16 -7
  365. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.zh-CN.md +16 -7
  366. package/templates/.agents/skills/complete-task/SKILL.en.md +10 -23
  367. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +10 -23
  368. package/templates/.agents/skills/create-task/SKILL.en.md +8 -0
  369. package/templates/.agents/skills/create-task/SKILL.zh-CN.md +7 -0
  370. package/templates/.agents/skills/import-codescan/SKILL.en.md +1 -1
  371. package/templates/.agents/skills/import-codescan/SKILL.zh-CN.md +1 -1
  372. package/templates/.agents/skills/import-dependabot/SKILL.en.md +1 -1
  373. package/templates/.agents/skills/import-dependabot/SKILL.zh-CN.md +1 -1
  374. package/templates/.agents/skills/init-labels/SKILL.en.md +8 -8
  375. package/templates/.agents/skills/init-labels/SKILL.zh-CN.md +8 -8
  376. package/templates/.agents/skills/init-milestones/SKILL.en.md +9 -11
  377. package/templates/.agents/skills/init-milestones/SKILL.zh-CN.md +9 -11
  378. package/templates/.agents/skills/plan-task/SKILL.en.md +14 -1
  379. package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +16 -1
  380. package/templates/.agents/skills/plan-task/config/verify.en.json +4 -14
  381. package/templates/.agents/skills/plan-task/config/verify.zh-CN.json +4 -14
  382. package/templates/.agents/skills/release/SKILL.en.md +7 -3
  383. package/templates/.agents/skills/release/SKILL.zh-CN.md +7 -3
  384. package/templates/.agents/skills/review-analysis/SKILL.en.md +14 -1
  385. package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +14 -1
  386. package/templates/.agents/skills/review-analysis/config/verify.en.json +1 -18
  387. package/templates/.agents/skills/review-analysis/config/verify.zh-CN.json +1 -18
  388. package/templates/.agents/skills/review-analysis/reference/report-template.en.md +50 -23
  389. package/templates/.agents/skills/review-analysis/reference/report-template.zh-CN.md +45 -18
  390. package/templates/.agents/skills/review-code/SKILL.en.md +14 -1
  391. package/templates/.agents/skills/review-code/SKILL.zh-CN.md +14 -1
  392. package/templates/.agents/skills/review-code/config/verify.en.json +1 -23
  393. package/templates/.agents/skills/review-code/config/verify.zh-CN.json +1 -23
  394. package/templates/.agents/skills/review-code/reference/report-template.en.md +49 -22
  395. package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +45 -18
  396. package/templates/.agents/skills/review-plan/SKILL.en.md +14 -1
  397. package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +14 -1
  398. package/templates/.agents/skills/review-plan/config/verify.en.json +1 -18
  399. package/templates/.agents/skills/review-plan/config/verify.zh-CN.json +1 -18
  400. package/templates/.agents/skills/review-plan/reference/report-template.en.md +49 -22
  401. package/templates/.agents/skills/review-plan/reference/report-template.zh-CN.md +45 -18
  402. package/templates/.agents/skills/review-pr/SKILL.en.md +5 -1
  403. package/templates/.agents/skills/review-pr/SKILL.zh-CN.md +5 -1
  404. package/templates/.agents/skills/review-pr/reference/report-template.en.md +4 -3
  405. package/templates/.agents/skills/review-pr/reference/report-template.zh-CN.md +4 -3
  406. package/templates/.agents/skills/run-manual-validation/SKILL.en.md +25 -4
  407. package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +25 -4
  408. package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.en.md +4 -1
  409. package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.zh-CN.md +4 -1
  410. package/templates/.agents/skills/run-manual-validation/reference/report-template.en.md +5 -1
  411. package/templates/.agents/skills/run-manual-validation/reference/report-template.zh-CN.md +5 -1
  412. package/templates/.agents/skills/run-task/SKILL.en.md +1 -1
  413. package/templates/.agents/skills/run-task/SKILL.zh-CN.md +1 -1
  414. package/templates/.agents/skills/test/SKILL.en.md +29 -7
  415. package/templates/.agents/skills/test/SKILL.zh-CN.md +29 -7
  416. package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +14 -0
  417. package/templates/.agents/templates/task.en.md +6 -0
  418. package/templates/.agents/templates/task.zh-CN.md +6 -0
  419. package/templates/.claude/commands/run-manual-validation.en.md +1 -1
  420. package/templates/.claude/commands/run-manual-validation.zh-CN.md +1 -1
  421. package/dist/lib/platform/adapters.js +0 -56
  422. package/lib/platform/adapters.ts +0 -197
  423. package/templates/.agents/scripts/security-alerts.github.sh +0 -151
  424. package/templates/.agents/scripts/security-alerts.sh +0 -31
  425. package/templates/.agents/skills/init-labels/scripts/init-labels.github.sh +0 -146
  426. package/templates/.agents/skills/init-labels/scripts/init-labels.sh +0 -32
  427. package/templates/.agents/skills/init-milestones/scripts/init-milestones.github.sh +0 -294
  428. package/templates/.agents/skills/init-milestones/scripts/init-milestones.sh +0 -6
@@ -6,12 +6,11 @@ import { resolvePlatformProviderContext } from "./context.js";
6
6
  import { hasCheckedRequirement, resolveRequirementSection } from "./issue-metadata.js";
7
7
  import { requirementSectionAnchors } from "./issues.js";
8
8
  import { taskTypeLabel } from "./metadata-labels.js";
9
- import { planInLabelUpdate, validateInLabelMapping, validateRepositoryLabelPayload } from "./in-label-sync.js";
10
- import { inspectGitHubPullRequest } from "./pull-requests.js";
11
- import { createGitHubClient } from "./github-client.js";
9
+ import { planInLabelUpdate, validateInLabelMapping } from "./in-label-sync.js";
12
10
  import { readPrDeliveryFact } from "../task/pr-delivery-fact.js";
13
11
  import { providerError, providerOperationContext, resourceIdentityNumber, unsupportedProviderOperation } from "./provider-bridge.js";
14
12
  import { taskIssueIdentity } from "./task-identities.js";
13
+ import { CONTROL_MARKER_PATTERN, canonicalizeCommentBody, renderSafeCodeFence, sanitizeMarkdownDocument } from "./comment-safety.js";
15
14
  const CHECK_TYPE = "platform-sync";
16
15
  const VERSION_LINE_REGEX = /^[0-9]+\.[0-9]+\.x$/;
17
16
  const FRONTMATTER_FIELD_MAP = {
@@ -26,8 +25,6 @@ const OPTION_LOCALIZATION = {
26
25
  "中": "Medium",
27
26
  "低": "Low"
28
27
  };
29
- let activeShared = null;
30
- let repoRoot = "";
31
28
  export function getDefaults() {
32
29
  return {
33
30
  statusLabels: {
@@ -47,47 +44,16 @@ export function getDefaults() {
47
44
  }
48
45
  };
49
46
  }
50
- function getShared() {
51
- if (!activeShared) {
52
- throw new Error("platform-sync adapter shared utilities are unavailable");
53
- }
54
- return activeShared;
55
- }
56
- function loadTask(...args) {
57
- return getShared().loadTask(...args);
58
- }
59
- function getCheckedRequirements(...args) {
60
- return getShared().getCheckedRequirements(...args);
61
- }
62
- function normalizeContent(...args) {
63
- return getShared().normalizeContent(...args);
64
- }
65
- function isBlank(...args) {
66
- return getShared().isBlank(...args);
67
- }
68
- function escapeRegExp(...args) {
69
- return getShared().escapeRegExp(...args);
70
- }
71
- function passResult(...args) {
72
- return getShared().passResult(...args);
73
- }
74
- function failResult(...args) {
75
- return getShared().failResult(...args);
76
- }
77
- function blockedResult(...args) {
78
- return getShared().blockedResult(...args);
79
- }
80
- function safeStat(...args) {
81
- return getShared().safeStat(...args);
47
+ function sanitizeCommentContent(content) {
48
+ const result = sanitizeMarkdownDocument(content, { reservedMarkers: [CONTROL_MARKER_PATTERN] });
49
+ return result.ok ? result.value : null;
82
50
  }
83
51
  export async function check({ taskDir, config, artifactFile }, shared) {
84
- activeShared = shared;
85
- repoRoot = shared.repoRoot;
86
- const context = await buildSyncContext({ taskDir, config, artifactFile });
52
+ const context = await buildSyncContext({ taskDir, config, artifactFile }, shared);
87
53
  if (context.earlyReturn) {
88
54
  return context.earlyReturn;
89
55
  }
90
- const remoteData = await fetchRemoteData(context);
56
+ const remoteData = await fetchRemoteData(context, shared);
91
57
  if (remoteData.earlyReturn) {
92
58
  return remoteData.earlyReturn;
93
59
  }
@@ -110,52 +76,52 @@ export async function check({ taskDir, config, artifactFile }, shared) {
110
76
  checkMilestone
111
77
  ];
112
78
  for (const subCheck of subChecks) {
113
- const result = subCheck(context, remoteData);
79
+ const result = subCheck(context, remoteData, shared);
114
80
  if (result) {
115
81
  return result;
116
82
  }
117
83
  }
118
- return passResult(CHECK_TYPE, `Platform sync checks passed for Issue ${context.issueNumber || "identity"}`);
84
+ return shared.passResult(CHECK_TYPE, `Platform sync checks passed for Issue ${context.issueNumber || "identity"}`);
119
85
  }
120
- async function buildSyncContext({ taskDir, config, artifactFile }) {
121
- const task = loadTask(taskDir);
86
+ async function buildSyncContext({ taskDir, config, artifactFile }, shared) {
87
+ const task = shared.loadTask(taskDir);
122
88
  if (!task.ok) {
123
- return { earlyReturn: failResult(CHECK_TYPE, task.message) };
89
+ return { earlyReturn: shared.failResult(CHECK_TYPE, task.message) };
124
90
  }
125
91
  const issueIdentity = taskIssueIdentity(task.metadata);
126
92
  const issueNumber = resourceIdentityNumber(issueIdentity);
127
93
  const fact = readPrDeliveryFact(task.metadata);
128
94
  if (fact.status === "invalid") {
129
- return { earlyReturn: failResult(CHECK_TYPE, fact.error.message, "check_failed") };
95
+ return { earlyReturn: shared.failResult(CHECK_TYPE, fact.error.message, "check_failed") };
130
96
  }
131
97
  const prIdentity = fact.status === "valid" && fact.fact.state === "bound" ? fact.fact.identity.resource : null;
132
98
  const prNumber = resourceIdentityNumber(prIdentity);
133
99
  if (config.when === "issue_number_exists" && !issueIdentity) {
134
- return { earlyReturn: passResult(CHECK_TYPE, "Skipped: task has no issue_number") };
100
+ return { earlyReturn: shared.passResult(CHECK_TYPE, "Skipped: task has no issue_number") };
135
101
  }
136
102
  if (config.when === "pr_fact_bound" && !prIdentity) {
137
- return { earlyReturn: passResult(CHECK_TYPE, "Skipped: task has no verified bound pull request") };
103
+ return { earlyReturn: shared.passResult(CHECK_TYPE, "Skipped: task has no verified bound pull request") };
138
104
  }
139
105
  if (!issueIdentity) {
140
- return { earlyReturn: passResult(CHECK_TYPE, "Skipped: platform-sync not required for this task") };
106
+ return { earlyReturn: shared.passResult(CHECK_TYPE, "Skipped: platform-sync not required for this task") };
141
107
  }
142
- const loaded = await resolvePlatformProviderContext({ cwd: repoRoot });
108
+ const loaded = await resolvePlatformProviderContext({ cwd: shared.repoRoot });
143
109
  const platformContext = loaded.ok ? loaded.value.context : loaded.context;
144
110
  if (platformContext.status === "failed") {
145
- return { earlyReturn: failResult(CHECK_TYPE, platformContext.error?.message || "Platform context failed", "check_failed") };
111
+ return { earlyReturn: shared.failResult(CHECK_TYPE, platformContext.error?.message || "Platform context failed", "check_failed") };
146
112
  }
147
113
  if (platformContext.status === "blocked") {
148
- return { earlyReturn: blockedResult(CHECK_TYPE, platformContext.error?.message || "Platform context blocked", "network_error") };
114
+ return { earlyReturn: shared.blockedResult(CHECK_TYPE, platformContext.error?.message || "Platform context blocked", "network_error") };
149
115
  }
150
116
  if (!platformContext.platform.repository) {
151
117
  if (platformContext.error?.code === "REMOTE_MISSING" || platformContext.error?.code === "REMOTE_INVALID") {
152
- return { earlyReturn: blockedResult(CHECK_TYPE, platformContext.error.message, "network_error") };
118
+ return { earlyReturn: shared.blockedResult(CHECK_TYPE, platformContext.error.message, "network_error") };
153
119
  }
154
- return { earlyReturn: passResult(CHECK_TYPE, `Skipped: ${platformContext.error?.message || "platform unavailable"}`) };
120
+ return { earlyReturn: shared.passResult(CHECK_TYPE, `Skipped: ${platformContext.error?.message || "platform unavailable"}`) };
155
121
  }
156
122
  const expectedValues = resolveExpectedValues(config);
157
123
  if (!expectedValues.ok) {
158
- return { earlyReturn: failResult(CHECK_TYPE, expectedValues.message, "check_failed") };
124
+ return { earlyReturn: shared.failResult(CHECK_TYPE, expectedValues.message, "check_failed") };
159
125
  }
160
126
  const marker = expectedValues.commentMarker
161
127
  ? interpolate(expectedValues.commentMarker, taskDir, artifactFile)
@@ -174,7 +140,6 @@ async function buildSyncContext({ taskDir, config, artifactFile }) {
174
140
  prIdentity,
175
141
  issueNumber,
176
142
  prNumber,
177
- upstreamRepo: platformContext.platform.repository,
178
143
  repoOwnerType: String(loaded.ok ? loaded.value.snapshot.metadata?.ownerType || "unknown" : "unknown"),
179
144
  hasTriage: platformContext.capabilities.triage,
180
145
  hasPush: platformContext.capabilities.push,
@@ -232,7 +197,7 @@ function resolveDefaultValue({ collection, key, value, configKey }) {
232
197
  }
233
198
  return { ok: true, value: resolvedValue };
234
199
  }
235
- async function fetchRemoteData(context) {
200
+ async function fetchRemoteData(context, shared) {
236
201
  const provider = context.provider;
237
202
  const operationContext = providerOperationContext(context.loadedContext, context.taskDir);
238
203
  const facts = provider?.verification?.fetchRemoteFacts
@@ -248,8 +213,8 @@ async function fetchRemoteData(context) {
248
213
  if (!facts.ok) {
249
214
  return {
250
215
  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")
216
+ ? shared.blockedResult(CHECK_TYPE, providerError(facts.error, "PLATFORM_PROVIDER_OPERATION_FAILED").message, "network_error")
217
+ : shared.failResult(CHECK_TYPE, providerError(facts.error, "PLATFORM_PROVIDER_OPERATION_FAILED").message, "check_failed")
253
218
  };
254
219
  }
255
220
  const issueSnapshot = facts.value.issue;
@@ -275,12 +240,34 @@ async function fetchRemoteData(context) {
275
240
  if (!listed.ok)
276
241
  return {
277
242
  earlyReturn: listed.error.retryable
278
- ? blockedResult(CHECK_TYPE, listed.error.message, "network_error")
279
- : failResult(CHECK_TYPE, listed.error.message, "check_failed")
243
+ ? shared.blockedResult(CHECK_TYPE, listed.error.message, "network_error")
244
+ : shared.failResult(CHECK_TYPE, listed.error.message, "check_failed")
280
245
  };
281
246
  prComments = listed.value.map((comment) => ({ id: comment.id, body: comment.body }));
282
247
  }
283
248
  const changeRequest = facts.value.changeRequest;
249
+ let inLabelMapping = {};
250
+ let repositoryLabels = [];
251
+ if (context.config.verify_in_labels_computed && context.hasTriage) {
252
+ const mapping = loadInLabelMapping(shared);
253
+ if (!mapping.ok)
254
+ return { earlyReturn: shared.failResult(CHECK_TYPE, mapping.message, "check_failed") };
255
+ inLabelMapping = mapping.value;
256
+ if (Object.keys(inLabelMapping).length > 0) {
257
+ const labels = provider?.issues?.listLabels
258
+ ? await provider.issues.listLabels({ context: operationContext })
259
+ : unsupportedProviderOperation(provider, "issues.listLabels");
260
+ if (!labels.ok) {
261
+ const error = providerError(labels.error, "PLATFORM_PROVIDER_OPERATION_FAILED");
262
+ return {
263
+ earlyReturn: error.retryable
264
+ ? shared.blockedResult(CHECK_TYPE, `${error.code}: ${error.message}`, "network_error")
265
+ : shared.failResult(CHECK_TYPE, `${error.code}: ${error.message}`, "check_failed")
266
+ };
267
+ }
268
+ repositoryLabels = labels.value;
269
+ }
270
+ }
284
271
  return {
285
272
  issue,
286
273
  comments: facts.value.comments.map((comment) => ({ id: comment.id, body: comment.body })),
@@ -294,7 +281,9 @@ async function fetchRemoteData(context) {
294
281
  ? (changeRequest.milestone ? { title: changeRequest.milestone } : null)
295
282
  : undefined,
296
283
  prAssignees: changeRequest?.assignees,
297
- prHeadSha: changeRequest?.headSha
284
+ prHeadSha: changeRequest?.headSha,
285
+ inLabelMapping,
286
+ repositoryLabels
298
287
  };
299
288
  }
300
289
  function mapTaskTypeToLabel(taskType) {
@@ -309,7 +298,7 @@ function shouldFetchComments(config) {
309
298
  || config.verify_comment_content
310
299
  || config.verify_task_comment_content);
311
300
  }
312
- function checkStatusLabel(context, remoteData) {
301
+ function checkStatusLabel(context, remoteData, shared) {
313
302
  if (!context.expectedStatusLabel || !context.hasTriage) {
314
303
  return null;
315
304
  }
@@ -320,9 +309,9 @@ function checkStatusLabel(context, remoteData) {
320
309
  if (labels.includes(context.expectedStatusLabel)) {
321
310
  return null;
322
311
  }
323
- return failResult(CHECK_TYPE, `Expected label '${context.expectedStatusLabel}' not found on Issue #${context.issueNumber}`, "check_failed");
312
+ return shared.failResult(CHECK_TYPE, `Expected label '${context.expectedStatusLabel}' not found on Issue #${context.issueNumber}`, "check_failed");
324
313
  }
325
- function checkClosedIssueStatusLabels(context, remoteData) {
314
+ function checkClosedIssueStatusLabels(context, remoteData, shared) {
326
315
  if (!context.config.verify_closed_issue_has_no_status_labels) {
327
316
  return null;
328
317
  }
@@ -334,9 +323,9 @@ function checkClosedIssueStatusLabels(context, remoteData) {
334
323
  if (statusLabels.length === 0) {
335
324
  return null;
336
325
  }
337
- return failResult(CHECK_TYPE, `Closed Issue #${context.issueNumber} retains status labels: ${statusLabels.join(", ")}`, "check_failed");
326
+ return shared.failResult(CHECK_TYPE, `Closed Issue #${context.issueNumber} retains status labels: ${statusLabels.join(", ")}`, "check_failed");
338
327
  }
339
- function checkCommentMarker(context, remoteData) {
328
+ function checkCommentMarker(context, remoteData, shared) {
340
329
  if (!context.marker) {
341
330
  return null;
342
331
  }
@@ -344,9 +333,9 @@ function checkCommentMarker(context, remoteData) {
344
333
  if (comment) {
345
334
  return null;
346
335
  }
347
- return failResult(CHECK_TYPE, `Expected comment marker '${context.marker}' not found on Issue #${context.issueNumber}`, "check_failed");
336
+ return shared.failResult(CHECK_TYPE, `Expected comment marker '${context.marker}' not found on Issue #${context.issueNumber}`, "check_failed");
348
337
  }
349
- function checkPrCommentMarker(context, remoteData) {
338
+ function checkPrCommentMarker(context, remoteData, shared) {
350
339
  if (!context.prMarker) {
351
340
  return null;
352
341
  }
@@ -354,88 +343,107 @@ function checkPrCommentMarker(context, remoteData) {
354
343
  if (comment) {
355
344
  return null;
356
345
  }
357
- return failResult(CHECK_TYPE, `Expected PR comment marker '${context.prMarker}' not found on PR #${context.prNumber}`, "check_failed");
346
+ return shared.failResult(CHECK_TYPE, `Expected PR comment marker '${context.prMarker}' not found on PR #${context.prNumber}`, "check_failed");
358
347
  }
359
- function checkPrCommentLastCommit(context, remoteData) {
348
+ function checkPrCommentLastCommit(context, remoteData, shared) {
360
349
  if (!context.config.verify_pr_comment_last_commit_matches_head) {
361
350
  return null;
362
351
  }
363
352
  if (!context.prMarker) {
364
- return failResult(CHECK_TYPE, "verify_pr_comment_last_commit_matches_head requires expected_pr_comment_marker", "check_failed");
353
+ return shared.failResult(CHECK_TYPE, "verify_pr_comment_last_commit_matches_head requires expected_pr_comment_marker", "check_failed");
365
354
  }
366
355
  const comment = findCommentByMarker(remoteData.prComments, context.prMarker);
367
356
  if (!comment) {
368
- return failResult(CHECK_TYPE, `Expected PR comment marker '${context.prMarker}' not found on PR #${context.prNumber}`, "check_failed");
357
+ return shared.failResult(CHECK_TYPE, `Expected PR comment marker '${context.prMarker}' not found on PR #${context.prNumber}`, "check_failed");
369
358
  }
370
359
  const match = String(comment.body || "").match(/<!--\s*last-commit:\s*([0-9a-f]{7,40})\s*-->/i);
371
360
  if (!match) {
372
- return failResult(CHECK_TYPE, `PR #${context.prNumber} summary comment is missing '<!-- last-commit: <sha> -->' metadata`, "check_failed");
361
+ return shared.failResult(CHECK_TYPE, `PR #${context.prNumber} summary comment is missing '<!-- last-commit: <sha> -->' metadata`, "check_failed");
373
362
  }
374
363
  const expectedHead = String(remoteData.prHeadSha || "").trim();
375
364
  if (!expectedHead)
376
- return blockedResult(CHECK_TYPE, "Unable to resolve the PR head SHA", "network_error");
365
+ return shared.blockedResult(CHECK_TYPE, "Unable to resolve the PR head SHA", "network_error");
377
366
  const actualHead = match[1].trim();
378
367
  if (expectedHead === actualHead) {
379
368
  return null;
380
369
  }
381
- return failResult(CHECK_TYPE, `PR #${context.prNumber} summary comment last-commit metadata mismatch: expected ${expectedHead}, got ${actualHead}`, "check_failed");
370
+ return shared.failResult(CHECK_TYPE, `PR #${context.prNumber} summary comment last-commit metadata mismatch: expected ${expectedHead}, got ${actualHead}`, "check_failed");
382
371
  }
383
- function checkPrCommentRequiredPatterns(context, remoteData) {
372
+ function checkPrCommentRequiredPatterns(context, remoteData, shared) {
384
373
  const patterns = context.config.expected_pr_comment_required_patterns || [];
385
374
  if (!Array.isArray(patterns) || patterns.length === 0) {
386
375
  return null;
387
376
  }
388
377
  if (!context.prMarker) {
389
- return failResult(CHECK_TYPE, "expected_pr_comment_required_patterns requires expected_pr_comment_marker", "check_failed");
378
+ return shared.failResult(CHECK_TYPE, "expected_pr_comment_required_patterns requires expected_pr_comment_marker", "check_failed");
390
379
  }
391
380
  const comment = findCommentByMarker(remoteData.prComments, context.prMarker);
392
381
  if (!comment) {
393
- return failResult(CHECK_TYPE, `Expected PR comment marker '${context.prMarker}' not found on PR #${context.prNumber}`, "check_failed");
382
+ return shared.failResult(CHECK_TYPE, `Expected PR comment marker '${context.prMarker}' not found on PR #${context.prNumber}`, "check_failed");
394
383
  }
395
384
  const body = String(comment.body || "");
396
385
  for (const pattern of patterns) {
397
386
  const regex = new RegExp(pattern, "m");
398
387
  if (!regex.test(body)) {
399
- return failResult(CHECK_TYPE, `PR #${context.prNumber} summary comment is missing required pattern: ${pattern}`, "check_failed");
388
+ return shared.failResult(CHECK_TYPE, `PR #${context.prNumber} summary comment is missing required pattern: ${pattern}`, "check_failed");
400
389
  }
401
390
  }
402
391
  return null;
403
392
  }
404
- function checkCommentContent(context, remoteData) {
393
+ function checkCommentContent(context, remoteData, shared) {
405
394
  if (!context.config.verify_comment_content) {
406
395
  return null;
407
396
  }
408
397
  if (!context.marker) {
409
- return failResult(CHECK_TYPE, "verify_comment_content requires expected_comment_marker", "check_failed");
398
+ return shared.failResult(CHECK_TYPE, "verify_comment_content requires expected_comment_marker", "check_failed");
410
399
  }
411
- if (!context.artifactPath || !safeStat(context.artifactPath)) {
412
- return failResult(CHECK_TYPE, `Artifact not found for comment verification: ${context.artifactFile || "(missing artifactFile)"}`, "check_failed");
400
+ if (!context.artifactPath || !shared.safeStat(context.artifactPath)) {
401
+ return shared.failResult(CHECK_TYPE, `Artifact not found for comment verification: ${context.artifactFile || "(missing artifactFile)"}`, "check_failed");
413
402
  }
414
403
  const comment = findCommentByMarker(remoteData.comments, context.marker);
415
- const localContent = normalizeContent(fs.readFileSync(context.artifactPath, "utf8"));
416
- const commentContent = normalizeContent(extractCommentBody(comment?.body || ""));
404
+ let localContent;
405
+ let commentContent;
406
+ try {
407
+ const localCanonical = canonicalizeCommentBody(fs.readFileSync(context.artifactPath, "utf8"));
408
+ const commentCanonical = canonicalizeCommentBody(extractCommentBody(comment?.body || ""));
409
+ if (!localCanonical.ok) {
410
+ return shared.failResult(CHECK_TYPE, `Comment content cannot be canonicalized for '${path.basename(context.artifactPath, path.extname(context.artifactPath))}': ${localCanonical.error.message}`, "check_failed");
411
+ }
412
+ if (!commentCanonical.ok) {
413
+ return shared.failResult(CHECK_TYPE, `Comment content cannot be canonicalized for '${path.basename(context.artifactPath, path.extname(context.artifactPath))}': ${commentCanonical.error.message}`, "check_failed");
414
+ }
415
+ localContent = shared.normalizeContent(localCanonical.value);
416
+ commentContent = shared.normalizeContent(commentCanonical.value);
417
+ }
418
+ catch (error) {
419
+ return shared.failResult(CHECK_TYPE, `Comment content cannot be read for '${path.basename(context.artifactPath, path.extname(context.artifactPath))}': ${error instanceof Error ? error.message : String(error)}`, "check_failed");
420
+ }
417
421
  if (localContent === commentContent) {
418
422
  return null;
419
423
  }
420
- return failResult(CHECK_TYPE, buildCommentContentMismatchMessage(path.basename(context.artifactPath, path.extname(context.artifactPath)), context.issueNumber, localContent, commentContent), "check_failed");
424
+ return shared.failResult(CHECK_TYPE, buildCommentContentMismatchMessage(path.basename(context.artifactPath, path.extname(context.artifactPath)), context.issueNumber, localContent, commentContent), "check_failed");
421
425
  }
422
- function checkTaskCommentContent(context, remoteData) {
426
+ function checkTaskCommentContent(context, remoteData, shared) {
423
427
  if (!context.config.verify_task_comment_content) {
424
428
  return null;
425
429
  }
426
430
  const taskMarker = `<!-- sync-issue:${context.task.metadata.id}:task -->`;
427
431
  const comment = findCommentByMarker(remoteData.comments, taskMarker);
428
432
  if (!comment) {
429
- return failResult(CHECK_TYPE, `Expected comment marker '${taskMarker}' not found on Issue #${context.issueNumber}`, "check_failed");
433
+ return shared.failResult(CHECK_TYPE, `Expected comment marker '${taskMarker}' not found on Issue #${context.issueNumber}`, "check_failed");
434
+ }
435
+ const expectedTaskBody = buildExpectedTaskBody(context.task.content, shared);
436
+ if (expectedTaskBody === null) {
437
+ return shared.failResult(CHECK_TYPE, "Task content cannot be rendered safely for comment verification", "check_failed");
430
438
  }
431
- const expectedBody = normalizeContent(buildExpectedTaskBody(context.task.content));
432
- const commentBody = normalizeContent(extractCommentBody(comment.body || ""));
439
+ const expectedBody = shared.normalizeContent(expectedTaskBody);
440
+ const commentBody = shared.normalizeContent(extractCommentBody(comment.body || ""));
433
441
  if (expectedBody === commentBody) {
434
442
  return null;
435
443
  }
436
- return failResult(CHECK_TYPE, buildCommentContentMismatchMessage("task", context.issueNumber, expectedBody, commentBody), "check_failed");
444
+ return shared.failResult(CHECK_TYPE, buildCommentContentMismatchMessage("task", context.issueNumber, expectedBody, commentBody), "check_failed");
437
445
  }
438
- function checkPrTypeLabel(context, remoteData) {
446
+ function checkPrTypeLabel(context, remoteData, shared) {
439
447
  if (!context.config.verify_pr_type_label || !context.hasTriage || !context.prNumber || !remoteData.prLabels) {
440
448
  return null;
441
449
  }
@@ -446,9 +454,9 @@ function checkPrTypeLabel(context, remoteData) {
446
454
  if (remoteData.prLabels.includes(expectedLabel)) {
447
455
  return null;
448
456
  }
449
- return failResult(CHECK_TYPE, `Expected type label '${expectedLabel}' not found on PR #${context.prNumber}`, "check_failed");
457
+ return shared.failResult(CHECK_TYPE, `Expected type label '${expectedLabel}' not found on PR #${context.prNumber}`, "check_failed");
450
458
  }
451
- function checkInLabelsMatchPr(context, remoteData) {
459
+ function checkInLabelsMatchPr(context, remoteData, shared) {
452
460
  if (!context.config.verify_in_labels_match_pr || !context.hasTriage || !context.prNumber || !remoteData.prLabels) {
453
461
  return null;
454
462
  }
@@ -461,17 +469,17 @@ function checkInLabelsMatchPr(context, remoteData) {
461
469
  if (arraysEqual(issueInLabels, prInLabels)) {
462
470
  return null;
463
471
  }
464
- return failResult(CHECK_TYPE, `in: labels mismatch — PR #${context.prNumber} has [${formatLabelList(prInLabels)}], Issue #${context.issueNumber} has [${formatLabelList(issueInLabels)}]`, "check_failed");
472
+ return shared.failResult(CHECK_TYPE, `in: labels mismatch — PR #${context.prNumber} has [${formatLabelList(prInLabels)}], Issue #${context.issueNumber} has [${formatLabelList(issueInLabels)}]`, "check_failed");
465
473
  }
466
- function checkInLabelsComputed(context, remoteData) {
474
+ function checkInLabelsComputed(context, remoteData, shared) {
467
475
  if (!context.config.verify_in_labels_computed || !context.hasTriage) {
468
476
  return null;
469
477
  }
470
- const expectedInLabels = computeExpectedInLabels(context.taskDir, context.upstreamRepo);
478
+ const expectedInLabels = computeExpectedInLabels(context.taskDir, remoteData.repositoryLabels, remoteData.inLabelMapping, shared);
471
479
  if (!expectedInLabels.ok) {
472
480
  return expectedInLabels.type === "check_failed"
473
- ? failResult(CHECK_TYPE, expectedInLabels.message, expectedInLabels.type)
474
- : blockedResult(CHECK_TYPE, expectedInLabels.message, expectedInLabels.type);
481
+ ? shared.failResult(CHECK_TYPE, expectedInLabels.message, expectedInLabels.type)
482
+ : shared.blockedResult(CHECK_TYPE, expectedInLabels.message, expectedInLabels.type);
475
483
  }
476
484
  if (expectedInLabels.mode === "skipped") {
477
485
  return null;
@@ -482,32 +490,32 @@ function checkInLabelsComputed(context, remoteData) {
482
490
  if (arraysEqual(expectedInLabels.labels, actualInLabels)) {
483
491
  return null;
484
492
  }
485
- return failResult(CHECK_TYPE, `Issue #${context.issueNumber} in: labels do not match committed changes: expected [${formatLabelList(expectedInLabels.labels)}], got [${formatLabelList(actualInLabels)}]`, "check_failed");
493
+ return shared.failResult(CHECK_TYPE, `Issue #${context.issueNumber} in: labels do not match committed changes: expected [${formatLabelList(expectedInLabels.labels)}], got [${formatLabelList(actualInLabels)}]`, "check_failed");
486
494
  }
487
- function checkSyncedRequirements(context, remoteData) {
495
+ function checkSyncedRequirements(context, remoteData, shared) {
488
496
  if (!context.config.sync_checked_requirements || !context.hasTriage) {
489
497
  return null;
490
498
  }
491
- const checkedRequirements = getCheckedRequirements(context.task.content);
499
+ const checkedRequirements = shared.getCheckedRequirements(context.task.content);
492
500
  if (checkedRequirements.length === 0) {
493
501
  return null;
494
502
  }
495
503
  const issueBody = remoteData.issue.body || "";
496
- const resolution = resolveRequirementSection(issueBody, requirementSectionAnchors(repoRoot, context.task.metadata.type || "task"));
504
+ const resolution = resolveRequirementSection(issueBody, requirementSectionAnchors(shared.repoRoot, context.task.metadata.type || "task"));
497
505
  if (resolution.status === "missing") {
498
506
  return null;
499
507
  }
500
508
  if (resolution.status === "ambiguous") {
501
- return failResult(CHECK_TYPE, `Issue #${context.issueNumber} requirements section is ambiguous`, "check_failed");
509
+ return shared.failResult(CHECK_TYPE, `Issue #${context.issueNumber} requirements section is ambiguous`, "check_failed");
502
510
  }
503
511
  const requirementBody = issueBody.slice(resolution.bodyStart, resolution.bodyEnd);
504
512
  const missingRequirements = checkedRequirements.filter((item) => !hasCheckedRequirement(requirementBody, item));
505
513
  if (missingRequirements.length === 0) {
506
514
  return null;
507
515
  }
508
- return failResult(CHECK_TYPE, `Issue body is missing checked requirements: ${missingRequirements.join(", ")}`, "check_failed");
516
+ return shared.failResult(CHECK_TYPE, `Issue body is missing checked requirements: ${missingRequirements.join(", ")}`, "check_failed");
509
517
  }
510
- function checkIssueType(context, remoteData) {
518
+ function checkIssueType(context, remoteData, shared) {
511
519
  if (!context.config.verify_issue_type || !context.hasPush) {
512
520
  return null;
513
521
  }
@@ -518,15 +526,15 @@ function checkIssueType(context, remoteData) {
518
526
  if (context.repoOwnerType === "User") {
519
527
  return null;
520
528
  }
521
- return failResult(CHECK_TYPE, `Issue #${context.issueNumber} has no Issue Type set`, "check_failed");
529
+ return shared.failResult(CHECK_TYPE, `Issue #${context.issueNumber} has no Issue Type set`, "check_failed");
522
530
  }
523
531
  const expectedType = mapTaskTypeToIssueType(context.task.metadata.type);
524
532
  if (expectedType && remoteData.issueType !== expectedType) {
525
- return failResult(CHECK_TYPE, `Issue #${context.issueNumber} has type '${remoteData.issueType}', expected '${expectedType}' (from task type '${context.task.metadata.type}')`, "check_failed");
533
+ return shared.failResult(CHECK_TYPE, `Issue #${context.issueNumber} has type '${remoteData.issueType}', expected '${expectedType}' (from task type '${context.task.metadata.type}')`, "check_failed");
526
534
  }
527
535
  return null;
528
536
  }
529
- function checkIssueFields(context, remoteData) {
537
+ function checkIssueFields(context, remoteData, shared) {
530
538
  if (!context.config.verify_issue_fields || !context.hasPush) {
531
539
  return null;
532
540
  }
@@ -535,7 +543,7 @@ function checkIssueFields(context, remoteData) {
535
543
  }
536
544
  for (const [metadataKey, fieldName] of Object.entries(FRONTMATTER_FIELD_MAP)) {
537
545
  const expectedRaw = context.task.metadata[metadataKey];
538
- if (isBlank(expectedRaw) || !remoteData.issueFields.pinnedNames.has(fieldName)) {
546
+ if (shared.isBlank(expectedRaw) || !remoteData.issueFields.pinnedNames.has(fieldName)) {
539
547
  continue;
540
548
  }
541
549
  const actual = remoteData.issueFields.values.get(fieldName);
@@ -544,40 +552,40 @@ function checkIssueFields(context, remoteData) {
544
552
  continue;
545
553
  }
546
554
  if (!actual) {
547
- return failResult(CHECK_TYPE, `Issue #${context.issueNumber} field '${fieldName}' is missing, expected '${expected.value}'`, "check_failed");
555
+ return shared.failResult(CHECK_TYPE, `Issue #${context.issueNumber} field '${fieldName}' is missing, expected '${expected.value}'`, "check_failed");
548
556
  }
549
557
  if (actual.kind !== expected.kind || actual.value !== expected.value) {
550
- return failResult(CHECK_TYPE, `Issue #${context.issueNumber} field '${fieldName}' is '${actual.value}', expected '${expected.value}'`, "check_failed");
558
+ return shared.failResult(CHECK_TYPE, `Issue #${context.issueNumber} field '${fieldName}' is '${actual.value}', expected '${expected.value}'`, "check_failed");
551
559
  }
552
560
  }
553
561
  return null;
554
562
  }
555
- function checkPrAssignee(context, remoteData) {
563
+ function checkPrAssignee(context, remoteData, shared) {
556
564
  if (!context.config.verify_pr_assignee || !context.hasPush || !context.prNumber) {
557
565
  return null;
558
566
  }
559
567
  if (!remoteData.prAssignees || remoteData.prAssignees.length === 0) {
560
- return failResult(CHECK_TYPE, `PR #${context.prNumber} has no assignee`, "check_failed");
568
+ return shared.failResult(CHECK_TYPE, `PR #${context.prNumber} has no assignee`, "check_failed");
561
569
  }
562
570
  return null;
563
571
  }
564
- function checkMilestone(context, remoteData) {
572
+ function checkMilestone(context, remoteData, shared) {
565
573
  if (!context.config.verify_milestone || !context.hasTriage) {
566
574
  return null;
567
575
  }
568
576
  if (!remoteData.issue?.milestone?.title) {
569
- return failResult(CHECK_TYPE, `Issue #${context.issueNumber} has no milestone set`, "check_failed");
577
+ return shared.failResult(CHECK_TYPE, `Issue #${context.issueNumber} has no milestone set`, "check_failed");
570
578
  }
571
579
  if (context.prNumber && remoteData.prMilestone !== undefined && !remoteData.prMilestone?.title) {
572
- return failResult(CHECK_TYPE, `PR #${context.prNumber} has no milestone set`, "check_failed");
580
+ return shared.failResult(CHECK_TYPE, `PR #${context.prNumber} has no milestone set`, "check_failed");
573
581
  }
574
582
  if (context.config.verify_milestone_specific) {
575
583
  const issueTitle = remoteData.issue.milestone.title;
576
584
  if (VERSION_LINE_REGEX.test(issueTitle)) {
577
- return failResult(CHECK_TYPE, `Issue #${context.issueNumber} milestone '${issueTitle}' is a release line; narrow to a specific version (e.g. ${issueTitle.replace(/\.x$/, ".N")}) before continuing`, "check_failed");
585
+ return shared.failResult(CHECK_TYPE, `Issue #${context.issueNumber} milestone '${issueTitle}' is a release line; narrow to a specific version (e.g. ${issueTitle.replace(/\.x$/, ".N")}) before continuing`, "check_failed");
578
586
  }
579
587
  if (context.prNumber && remoteData.prMilestone?.title && VERSION_LINE_REGEX.test(remoteData.prMilestone.title)) {
580
- return failResult(CHECK_TYPE, `PR #${context.prNumber} milestone '${remoteData.prMilestone.title}' is a release line; narrow to a specific version before continuing`, "check_failed");
588
+ return shared.failResult(CHECK_TYPE, `PR #${context.prNumber} milestone '${remoteData.prMilestone.title}' is a release line; narrow to a specific version before continuing`, "check_failed");
581
589
  }
582
590
  }
583
591
  return null;
@@ -622,37 +630,38 @@ function extractCommentBody(commentBody) {
622
630
  }
623
631
  return lines.slice(start, end).join("\n");
624
632
  }
625
- function buildExpectedTaskBody(taskContent) {
633
+ function buildExpectedTaskBody(taskContent, shared) {
626
634
  const frontmatterMatch = taskContent.match(/^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/);
627
635
  if (!frontmatterMatch) {
628
- return taskContent.trim();
636
+ return sanitizeCommentContent(taskContent.trim());
637
+ }
638
+ const body = sanitizeCommentContent(taskContent.slice(frontmatterMatch[0].length).trim());
639
+ if (body === null) {
640
+ return null;
629
641
  }
630
- const body = taskContent.slice(frontmatterMatch[0].length).trim();
631
642
  return [
632
- buildTaskFrontmatterSummary(),
643
+ buildTaskFrontmatterSummary(shared),
633
644
  "",
634
- "```yaml",
635
- frontmatterMatch[0].trim(),
636
- "```",
645
+ renderSafeCodeFence(frontmatterMatch[0].trim(), "yaml"),
637
646
  "",
638
647
  "</details>",
639
648
  "",
640
649
  body
641
650
  ].join("\n").trim();
642
651
  }
643
- function buildTaskFrontmatterSummary() {
644
- const language = loadProjectLanguage();
652
+ function buildTaskFrontmatterSummary(shared) {
653
+ const language = loadProjectLanguage(shared);
645
654
  if (language === "en" || language === "en-US") {
646
655
  return "<details><summary>Metadata (frontmatter)</summary>";
647
656
  }
648
657
  return "<details><summary>元数据 (frontmatter)</summary>";
649
658
  }
650
- function loadProjectLanguage() {
659
+ function loadProjectLanguage(shared) {
651
660
  const override = process.env.VALIDATE_ARTIFACT_LANGUAGE;
652
- if (!isBlank(override)) {
661
+ if (!shared.isBlank(override)) {
653
662
  return String(override).trim();
654
663
  }
655
- const configPath = path.join(repoRoot, ".agents", ".airc.json");
664
+ const configPath = path.join(shared.repoRoot, ".agents", ".airc.json");
656
665
  if (!fs.existsSync(configPath)) {
657
666
  return "";
658
667
  }
@@ -767,8 +776,8 @@ function arraysEqual(left, right) {
767
776
  function formatLabelList(labels) {
768
777
  return labels.length > 0 ? labels.join(", ") : "none";
769
778
  }
770
- function computeExpectedInLabels(taskDir, repository) {
771
- const task = loadTask(taskDir);
779
+ function computeExpectedInLabels(taskDir, repositoryLabels, mappingOverride, shared) {
780
+ const task = shared.loadTask(taskDir);
772
781
  if (!task.ok) {
773
782
  return task;
774
783
  }
@@ -784,37 +793,23 @@ function computeExpectedInLabels(taskDir, repository) {
784
793
  .split(/\r?\n/)
785
794
  .map((value) => value.trim())
786
795
  .filter(Boolean);
787
- const mapping = loadInLabelMapping();
788
- if (!mapping.ok) {
789
- return mapping;
790
- }
791
- if (Object.keys(mapping.value ?? {}).length === 0) {
796
+ const mapping = mappingOverride ?? {};
797
+ if (Object.keys(mapping).length === 0) {
792
798
  return { ok: true, labels: [], mode: "mapped" };
793
799
  }
794
- const client = createGitHubClient();
795
- const repoLabelsResult = withRetry(() => client.json([
796
- "api", "--paginate", "--slurp", `repos/${repository}/labels?per_page=100`
797
- ], { cwd: taskDir }));
798
- if (!repoLabelsResult.ok) {
799
- return repoLabelsResult;
800
- }
801
- const repositoryLabels = validateRepositoryLabelPayload(repoLabelsResult.value);
802
- if (!repositoryLabels.ok) {
803
- return { ok: false, type: "check_failed", message: repositoryLabels.error.message };
804
- }
805
800
  const planned = planInLabelUpdate({
806
801
  changedFiles,
807
802
  currentLabels: [],
808
- mapping: mapping.value ?? {},
809
- repositoryLabels: new Set(repositoryLabels.value)
803
+ mapping,
804
+ repositoryLabels: new Set(repositoryLabels)
810
805
  });
811
806
  if (planned.error) {
812
807
  return { ok: false, type: "check_failed", message: planned.error.message };
813
808
  }
814
809
  return { ok: true, labels: planned.target, mode: "mapped" };
815
810
  }
816
- function loadInLabelMapping() {
817
- const configPath = path.join(repoRoot, ".agents", ".airc.json");
811
+ function loadInLabelMapping(shared) {
812
+ const configPath = path.join(shared.repoRoot, ".agents", ".airc.json");
818
813
  if (!fs.existsSync(configPath)) {
819
814
  return { ok: true, value: {} };
820
815
  }
@@ -829,7 +824,7 @@ function loadInLabelMapping() {
829
824
  return { ok: false, type: "check_failed", message: `Unable to parse .agents/.airc.json: ${error.message}` };
830
825
  }
831
826
  }
832
- // === GitHub API ===
827
+ // === Git working tree ===
833
828
  function gitText(args, cwd) {
834
829
  try {
835
830
  const value = execFileSync("git", args, { cwd, encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] });
@@ -845,12 +840,12 @@ function gitText(args, cwd) {
845
840
  }
846
841
  }
847
842
  function resolvePrHeadSha(context) {
848
- const fallback = () => withRetry(() => gitText(["rev-parse", "HEAD"], context.taskDir));
843
+ const fallback = () => gitText(["rev-parse", "HEAD"], context.taskDir);
849
844
  const branch = String(context.task?.metadata?.branch || "").trim();
850
845
  if (!branch) {
851
846
  return fallback();
852
847
  }
853
- const worktreeList = withRetry(() => gitText(["worktree", "list", "--porcelain"], context.taskDir));
848
+ const worktreeList = gitText(["worktree", "list", "--porcelain"], context.taskDir);
854
849
  if (!worktreeList.ok) {
855
850
  return fallback();
856
851
  }
@@ -858,7 +853,7 @@ function resolvePrHeadSha(context) {
858
853
  if (!matchedWorktree) {
859
854
  return fallback();
860
855
  }
861
- const headInWorktree = withRetry(() => gitText(["rev-parse", "HEAD"], matchedWorktree));
856
+ const headInWorktree = gitText(["rev-parse", "HEAD"], matchedWorktree);
862
857
  if (!headInWorktree.ok) {
863
858
  return fallback();
864
859
  }
@@ -881,9 +876,6 @@ function findWorktreeForBranch(porcelainOutput, branch) {
881
876
  }
882
877
  return null;
883
878
  }
884
- function withRetry(operation) {
885
- return operation();
886
- }
887
879
  function interpolate(template, taskDir, artifactFile) {
888
880
  const artifactStem = artifactFile ? path.basename(artifactFile, path.extname(artifactFile)) : "";
889
881
  return template