@fitlab-ai/agent-infra 0.9.12 → 0.9.14

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 (402) hide show
  1. package/README.md +11 -11
  2. package/README.zh-CN.md +11 -11
  3. package/bin/cli.ts +26 -9
  4. package/dist/bin/cli.js +173 -157
  5. package/dist/bin/internal-cli.js +66 -40
  6. package/dist/lib/agent-client.js +5 -0
  7. package/dist/lib/agent-clients/next-steps.js +6 -1
  8. package/dist/lib/decide.js +3 -3
  9. package/dist/lib/internal/agent-client.js +5 -2
  10. package/dist/lib/internal/cli-route-inventory.js +152 -0
  11. package/dist/lib/internal/codex-lifecycle.js +9 -6
  12. package/dist/lib/internal/codex-sandbox-controller.js +8 -2
  13. package/dist/lib/internal/git-workflow.js +10 -6
  14. package/dist/lib/internal/platform-checks.js +19 -7
  15. package/dist/lib/internal/platform-comment.js +25 -13
  16. package/dist/lib/internal/platform-context.js +6 -3
  17. package/dist/lib/internal/platform-issue.js +20 -13
  18. package/dist/lib/internal/platform-metadata.js +88 -0
  19. package/dist/lib/internal/platform-pr-review.js +26 -13
  20. package/dist/lib/internal/platform-pr.js +84 -31
  21. package/dist/lib/internal/platform-release-notes.js +9 -6
  22. package/dist/lib/internal/platform-security.js +93 -0
  23. package/dist/lib/internal/pr-review-grade.js +25 -12
  24. package/dist/lib/internal/release-workflow.js +23 -9
  25. package/dist/lib/internal/sandbox-control.js +50 -7
  26. package/dist/lib/internal/task-activity.js +16 -7
  27. package/dist/lib/internal/task-artifact.js +235 -24
  28. package/dist/lib/internal/task-context.js +4 -1
  29. package/dist/lib/internal/task-create.js +50 -17
  30. package/dist/lib/internal/task-delivery.js +4 -1
  31. package/dist/lib/internal/task-event.js +10 -4
  32. package/dist/lib/internal/task-finalization.js +6 -3
  33. package/dist/lib/internal/task-invalidation.js +63 -0
  34. package/dist/lib/internal/task-ledger.js +20 -6
  35. package/dist/lib/internal/task-lifecycle.js +6 -3
  36. package/dist/lib/internal/task-operation-registry.js +421 -0
  37. package/dist/lib/internal/task-orchestration.js +12 -0
  38. package/dist/lib/internal/task-override.js +14 -7
  39. package/dist/lib/internal/task-qualification.js +90 -0
  40. package/dist/lib/internal/task-review.js +7 -4
  41. package/dist/lib/internal/task-short-id.js +11 -1
  42. package/dist/lib/internal/task-snapshot.js +4 -1
  43. package/dist/lib/internal/task-validate.js +7 -2
  44. package/dist/lib/internal/task-verify.js +11 -7
  45. package/dist/lib/internal/task-warning.js +11 -4
  46. package/dist/lib/platform/adapters.js +1 -3
  47. package/dist/lib/platform/change-request-git-evidence.js +27 -7
  48. package/dist/lib/platform/comment-safety.js +226 -0
  49. package/dist/lib/platform/completion-backfill.js +6 -5
  50. package/dist/lib/platform/context.js +70 -130
  51. package/dist/lib/platform/github-client.js +1 -1
  52. package/dist/lib/platform/github-provider.js +1130 -0
  53. package/dist/lib/platform/in-label-sync.js +143 -0
  54. package/dist/lib/platform/issue-comments.js +237 -72
  55. package/dist/lib/platform/issues.js +335 -189
  56. package/dist/lib/platform/metadata-labels.js +6 -2
  57. package/dist/lib/platform/none-provider.js +28 -0
  58. package/dist/lib/platform/pr-change-report.js +545 -0
  59. package/dist/lib/platform/pr-checks.js +119 -104
  60. package/dist/lib/platform/pr-review.js +88 -72
  61. package/dist/lib/platform/pr-summary.js +541 -50
  62. package/dist/lib/platform/provider-bridge.js +45 -0
  63. package/dist/lib/platform/provider-contract.d.ts +553 -0
  64. package/dist/lib/platform/provider-contract.js +99 -0
  65. package/dist/lib/platform/provider-loader.js +251 -0
  66. package/dist/lib/platform/provider-validation.js +577 -0
  67. package/dist/lib/platform/pull-request-metadata.js +8 -6
  68. package/dist/lib/platform/pull-requests.js +734 -543
  69. package/dist/lib/platform/release-notes.js +78 -55
  70. package/dist/lib/platform/releases.js +79 -50
  71. package/dist/lib/platform/repository-metadata.js +377 -0
  72. package/dist/lib/platform/resource-identity.d.ts +26 -0
  73. package/dist/lib/platform/resource-identity.js +92 -0
  74. package/dist/lib/platform/security-alerts.js +155 -0
  75. package/dist/lib/platform/task-identities.js +31 -0
  76. package/dist/lib/platform/verification-required.js +11 -12
  77. package/dist/lib/platform/verification-sync.js +129 -248
  78. package/dist/lib/pr-review/evidence-grading.js +19 -6
  79. package/dist/lib/process-data/index.js +7 -0
  80. package/dist/lib/run/index.js +68 -23
  81. package/dist/lib/sandbox/commands/control-status.js +22 -0
  82. package/dist/lib/sandbox/commands/create.js +32 -6
  83. package/dist/lib/sandbox/commands/ls.js +5 -0
  84. package/dist/lib/sandbox/commands/rm.js +827 -120
  85. package/dist/lib/sandbox/commands/show.js +16 -0
  86. package/dist/lib/sandbox/control/client.js +174 -19
  87. package/dist/lib/sandbox/control/completed-reentry.js +85 -0
  88. package/dist/lib/sandbox/control/container-identity.js +28 -31
  89. package/dist/lib/sandbox/control/executor.js +151 -16
  90. package/dist/lib/sandbox/control/lifecycle.js +621 -127
  91. package/dist/lib/sandbox/control/native-file-lock.js +177 -0
  92. package/dist/lib/sandbox/control/protocol.js +77 -0
  93. package/dist/lib/sandbox/control/server.js +677 -34
  94. package/dist/lib/sandbox/control/state.js +209 -4
  95. package/dist/lib/sandbox/control/task-view.js +183 -0
  96. package/dist/lib/sandbox/engines/authority.js +289 -0
  97. package/dist/lib/sandbox/engines/docker-exact-discovery.js +97 -0
  98. package/dist/lib/sandbox/index.js +5 -0
  99. package/dist/lib/sandbox/recovery.js +21 -1
  100. package/dist/lib/sandbox/workspace-view.js +2 -1
  101. package/dist/lib/sandbox/worktree-safety.js +4 -1
  102. package/dist/lib/server/index.js +8 -0
  103. package/dist/lib/server/protocol.js +38 -1
  104. package/dist/lib/task/activity-log.js +8 -1
  105. package/dist/lib/task/artifact-lifecycle.js +75 -18
  106. package/dist/lib/task/artifact-operations.js +520 -0
  107. package/dist/lib/task/artifact-repair-intent.js +59 -0
  108. package/dist/lib/task/artifact-schema.js +135 -0
  109. package/dist/lib/task/capabilities.js +310 -0
  110. package/dist/lib/task/commands/status.js +32 -0
  111. package/dist/lib/task/commit-operation.js +12 -1
  112. package/dist/lib/task/control-authority.js +12 -5
  113. package/dist/lib/task/create-service.js +111 -12
  114. package/dist/lib/task/create.js +25 -0
  115. package/dist/lib/task/current-contract.js +5 -1
  116. package/dist/lib/task/decision-details.js +1 -1
  117. package/dist/lib/task/events.js +416 -17
  118. package/dist/lib/task/finalization.js +37 -13
  119. package/dist/lib/task/human-override.js +58 -36
  120. package/dist/lib/task/index.js +11 -3
  121. package/dist/lib/task/invalidation-command.js +86 -0
  122. package/dist/lib/task/invalidation.js +260 -0
  123. package/dist/lib/task/ledger-intents.js +37 -0
  124. package/dist/lib/task/local-artifact-finalization.js +299 -0
  125. package/dist/lib/task/orchestration.js +240 -144
  126. package/dist/lib/task/pr-delivery-fact.js +102 -101
  127. package/dist/lib/task/qualification-audit.js +490 -0
  128. package/dist/lib/task/qualification-confirmation.js +109 -0
  129. package/dist/lib/task/qualification-intents.js +148 -0
  130. package/dist/lib/task/review-artifacts.js +22 -23
  131. package/dist/lib/task/review-finalization.js +121 -4
  132. package/dist/lib/task/review-fingerprint.js +24 -2
  133. package/dist/lib/task/rework-intent.js +138 -0
  134. package/dist/lib/task/sections.js +6 -4
  135. package/dist/lib/task/skill-scope.js +23 -0
  136. package/dist/lib/task/task-execution-lock.js +27 -17
  137. package/dist/lib/task/verification-config.js +11 -0
  138. package/dist/lib/task/verification-engine.js +93 -16
  139. package/dist/lib/task/verification.js +3 -3
  140. package/dist/lib/task/workflow-warning-intents.js +1 -1
  141. package/dist/lib/task/write.js +11 -0
  142. package/dist/lib/version.js +11 -1
  143. package/dist/lifecycle-build-manifest.json +3 -3
  144. package/lib/agent-client.ts +9 -0
  145. package/lib/agent-clients/next-steps.ts +6 -1
  146. package/lib/decide.ts +3 -3
  147. package/lib/init.ts +1 -1
  148. package/lib/internal/agent-client.ts +4 -2
  149. package/lib/internal/cli-route-inventory.ts +140 -0
  150. package/lib/internal/codex-lifecycle.ts +8 -6
  151. package/lib/internal/codex-sandbox-controller.ts +7 -2
  152. package/lib/internal/git-workflow.ts +9 -6
  153. package/lib/internal/platform-checks.ts +14 -6
  154. package/lib/internal/platform-comment.ts +20 -12
  155. package/lib/internal/platform-context.ts +5 -3
  156. package/lib/internal/platform-issue.ts +17 -11
  157. package/lib/internal/platform-metadata.ts +70 -0
  158. package/lib/internal/platform-pr-review.ts +23 -12
  159. package/lib/internal/platform-pr.ts +65 -27
  160. package/lib/internal/platform-release-notes.ts +8 -6
  161. package/lib/internal/platform-security.ts +83 -0
  162. package/lib/internal/pr-review-grade.ts +20 -11
  163. package/lib/internal/release-workflow.ts +21 -9
  164. package/lib/internal/sandbox-control.ts +44 -6
  165. package/lib/internal/task-activity.ts +15 -7
  166. package/lib/internal/task-artifact.ts +204 -22
  167. package/lib/internal/task-context.ts +3 -1
  168. package/lib/internal/task-create.ts +78 -17
  169. package/lib/internal/task-delivery.ts +3 -1
  170. package/lib/internal/task-event.ts +9 -4
  171. package/lib/internal/task-finalization.ts +6 -4
  172. package/lib/internal/task-invalidation.ts +41 -0
  173. package/lib/internal/task-ledger.ts +21 -8
  174. package/lib/internal/task-lifecycle.ts +5 -3
  175. package/lib/internal/task-operation-registry.ts +482 -0
  176. package/lib/internal/task-orchestration.ts +12 -0
  177. package/lib/internal/task-override.ts +15 -9
  178. package/lib/internal/task-qualification.ts +61 -0
  179. package/lib/internal/task-review.ts +6 -4
  180. package/lib/internal/task-short-id.ts +10 -1
  181. package/lib/internal/task-snapshot.ts +3 -1
  182. package/lib/internal/task-validate.ts +6 -2
  183. package/lib/internal/task-verify.ts +10 -7
  184. package/lib/internal/task-warning.ts +10 -4
  185. package/lib/platform/adapters.ts +3 -2
  186. package/lib/platform/change-request-git-evidence.ts +36 -9
  187. package/lib/platform/comment-safety.ts +250 -0
  188. package/lib/platform/completion-backfill.ts +9 -8
  189. package/lib/platform/context.ts +97 -134
  190. package/lib/platform/github-client.ts +2 -2
  191. package/lib/platform/github-provider.ts +1132 -0
  192. package/lib/platform/in-label-sync.ts +208 -0
  193. package/lib/platform/issue-comments.ts +255 -80
  194. package/lib/platform/issues.ts +344 -174
  195. package/lib/platform/metadata-labels.ts +5 -2
  196. package/lib/platform/none-provider.ts +35 -0
  197. package/lib/platform/pr-change-report.ts +639 -0
  198. package/lib/platform/pr-checks.ts +126 -106
  199. package/lib/platform/pr-review.ts +88 -85
  200. package/lib/platform/pr-summary.ts +581 -53
  201. package/lib/platform/provider-bridge.ts +82 -0
  202. package/lib/platform/provider-contract.ts +611 -0
  203. package/lib/platform/provider-loader.ts +307 -0
  204. package/lib/platform/provider-validation.ts +611 -0
  205. package/lib/platform/pull-request-metadata.ts +10 -7
  206. package/lib/platform/pull-requests.ts +781 -550
  207. package/lib/platform/release-notes.ts +76 -59
  208. package/lib/platform/releases.ts +77 -43
  209. package/lib/platform/repository-metadata.ts +453 -0
  210. package/lib/platform/resource-identity.ts +121 -0
  211. package/lib/platform/security-alerts.ts +191 -0
  212. package/lib/platform/task-identities.ts +40 -0
  213. package/lib/platform/types.ts +4 -2
  214. package/lib/platform/verification-required.ts +11 -13
  215. package/lib/platform/verification-sync.ts +128 -304
  216. package/lib/pr-review/evidence-grading.ts +25 -8
  217. package/lib/process-data/index.ts +7 -0
  218. package/lib/run/index.ts +62 -22
  219. package/lib/sandbox/commands/control-status.ts +26 -0
  220. package/lib/sandbox/commands/create.ts +46 -6
  221. package/lib/sandbox/commands/ls.ts +7 -0
  222. package/lib/sandbox/commands/rm.ts +1000 -87
  223. package/lib/sandbox/commands/show.ts +16 -0
  224. package/lib/sandbox/control/client.ts +203 -19
  225. package/lib/sandbox/control/completed-reentry.ts +98 -0
  226. package/lib/sandbox/control/container-identity.ts +27 -43
  227. package/lib/sandbox/control/executor.ts +180 -15
  228. package/lib/sandbox/control/lifecycle.ts +629 -17
  229. package/lib/sandbox/control/native-file-lock.ts +209 -0
  230. package/lib/sandbox/control/protocol.ts +118 -2
  231. package/lib/sandbox/control/server.ts +724 -34
  232. package/lib/sandbox/control/state.ts +257 -4
  233. package/lib/sandbox/control/task-view.ts +240 -0
  234. package/lib/sandbox/engines/authority.ts +365 -0
  235. package/lib/sandbox/engines/docker-exact-discovery.ts +132 -0
  236. package/lib/sandbox/index.ts +7 -0
  237. package/lib/sandbox/recovery.ts +21 -1
  238. package/lib/sandbox/workspace-view.ts +9 -3
  239. package/lib/sandbox/worktree-safety.ts +4 -1
  240. package/lib/server/index.ts +10 -0
  241. package/lib/server/protocol.ts +36 -1
  242. package/lib/task/activity-log.ts +8 -0
  243. package/lib/task/artifact-lifecycle.ts +70 -22
  244. package/lib/task/artifact-operations.ts +626 -0
  245. package/lib/task/artifact-repair-intent.ts +71 -0
  246. package/lib/task/artifact-schema.ts +192 -0
  247. package/lib/task/capabilities.ts +331 -0
  248. package/lib/task/commands/status.ts +38 -0
  249. package/lib/task/commit-operation.ts +12 -1
  250. package/lib/task/control-authority.ts +22 -9
  251. package/lib/task/create-service.ts +139 -14
  252. package/lib/task/create.ts +27 -0
  253. package/lib/task/current-contract.ts +6 -1
  254. package/lib/task/decision-details.ts +6 -2
  255. package/lib/task/events.ts +392 -16
  256. package/lib/task/finalization.ts +50 -14
  257. package/lib/task/human-override.ts +70 -46
  258. package/lib/task/index.ts +16 -3
  259. package/lib/task/invalidation-command.ts +86 -0
  260. package/lib/task/invalidation.ts +332 -0
  261. package/lib/task/ledger-intents.ts +35 -1
  262. package/lib/task/local-artifact-finalization.ts +432 -0
  263. package/lib/task/orchestration.ts +223 -133
  264. package/lib/task/pr-delivery-fact.ts +131 -148
  265. package/lib/task/qualification-audit.ts +599 -0
  266. package/lib/task/qualification-confirmation.ts +117 -0
  267. package/lib/task/qualification-intents.ts +150 -0
  268. package/lib/task/review-artifacts.ts +21 -22
  269. package/lib/task/review-finalization.ts +133 -4
  270. package/lib/task/review-fingerprint.ts +23 -2
  271. package/lib/task/rework-intent.ts +165 -0
  272. package/lib/task/sections.ts +6 -4
  273. package/lib/task/skill-scope.ts +24 -0
  274. package/lib/task/task-execution-lock.ts +23 -14
  275. package/lib/task/verification-config.ts +12 -0
  276. package/lib/task/verification-engine.ts +103 -17
  277. package/lib/task/verification.ts +4 -4
  278. package/lib/task/workflow-warning-intents.ts +1 -1
  279. package/lib/task/write.ts +19 -0
  280. package/lib/update.ts +1 -1
  281. package/lib/version.ts +15 -1
  282. package/package.json +12 -1
  283. package/templates/.agents/rules/README.en.md +3 -1
  284. package/templates/.agents/rules/README.zh-CN.md +3 -1
  285. package/templates/.agents/rules/decision-qualification.en.md +22 -0
  286. package/templates/.agents/rules/decision-qualification.zh-CN.md +22 -0
  287. package/templates/.agents/rules/evidence-reporting.en.md +28 -0
  288. package/templates/.agents/rules/evidence-reporting.zh-CN.md +28 -0
  289. package/templates/.agents/rules/issue-pr-commands.en.md +1 -1
  290. package/templates/.agents/rules/issue-pr-commands.zh-CN.md +1 -1
  291. package/templates/.agents/rules/issue-sync.en.md +2 -0
  292. package/templates/.agents/rules/issue-sync.zh-CN.md +2 -0
  293. package/templates/.agents/rules/label-milestone-setup.en.md +7 -7
  294. package/templates/.agents/rules/label-milestone-setup.zh-CN.md +7 -7
  295. package/templates/.agents/rules/local-artifact-repair.en.md +13 -2
  296. package/templates/.agents/rules/local-artifact-repair.zh-CN.md +13 -2
  297. package/templates/.agents/rules/pr-sync.en.md +10 -4
  298. package/templates/.agents/rules/pr-sync.zh-CN.md +17 -4
  299. package/templates/.agents/rules/security-alerts.en.md +8 -8
  300. package/templates/.agents/rules/security-alerts.zh-CN.md +8 -8
  301. package/templates/.agents/rules/task-short-id.en.md +1 -4
  302. package/templates/.agents/rules/task-short-id.zh-CN.md +1 -3
  303. package/templates/.agents/skills/analyze-task/SKILL.en.md +28 -5
  304. package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +29 -5
  305. package/templates/.agents/skills/analyze-task/config/verify.en.json +1 -12
  306. package/templates/.agents/skills/analyze-task/config/verify.zh-CN.json +1 -12
  307. package/templates/.agents/skills/check-task/SKILL.en.md +16 -37
  308. package/templates/.agents/skills/check-task/SKILL.zh-CN.md +16 -37
  309. package/templates/.agents/skills/close-codescan/SKILL.en.md +2 -2
  310. package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +2 -2
  311. package/templates/.agents/skills/close-dependabot/SKILL.en.md +2 -2
  312. package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +2 -2
  313. package/templates/.agents/skills/code-task/SKILL.en.md +41 -9
  314. package/templates/.agents/skills/code-task/SKILL.zh-CN.md +43 -11
  315. package/templates/.agents/skills/code-task/config/verify.en.json +6 -14
  316. package/templates/.agents/skills/code-task/config/verify.zh-CN.json +6 -14
  317. package/templates/.agents/skills/code-task/reference/code-rules.en.md +5 -4
  318. package/templates/.agents/skills/code-task/reference/code-rules.zh-CN.md +5 -4
  319. package/templates/.agents/skills/code-task/reference/dual-mode.en.md +1 -1
  320. package/templates/.agents/skills/code-task/reference/dual-mode.zh-CN.md +1 -1
  321. package/templates/.agents/skills/code-task/reference/fix-mode.en.md +3 -1
  322. package/templates/.agents/skills/code-task/reference/fix-mode.zh-CN.md +3 -1
  323. package/templates/.agents/skills/code-task/reference/report-template.en.md +42 -15
  324. package/templates/.agents/skills/code-task/reference/report-template.zh-CN.md +39 -12
  325. package/templates/.agents/skills/commit/reference/issue-metadata-sync.en.md +2 -0
  326. package/templates/.agents/skills/commit/reference/issue-metadata-sync.zh-CN.md +2 -0
  327. package/templates/.agents/skills/commit/reference/pr-summary-sync.en.md +3 -3
  328. package/templates/.agents/skills/commit/reference/pr-summary-sync.zh-CN.md +3 -3
  329. package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +10 -4
  330. package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +10 -4
  331. package/templates/.agents/skills/complete-manual-validation/reference/report-template.en.md +2 -0
  332. package/templates/.agents/skills/complete-manual-validation/reference/report-template.zh-CN.md +2 -0
  333. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.en.md +4 -3
  334. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.zh-CN.md +4 -3
  335. package/templates/.agents/skills/complete-task/SKILL.en.md +26 -21
  336. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +25 -21
  337. package/templates/.agents/skills/create-pr/SKILL.en.md +7 -6
  338. package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +7 -7
  339. package/templates/.agents/skills/create-pr/config/verify.json +1 -0
  340. package/templates/.agents/skills/create-pr/reference/change-report.en.md +16 -4
  341. package/templates/.agents/skills/create-pr/reference/change-report.zh-CN.md +16 -4
  342. package/templates/.agents/skills/create-pr/reference/comment-publish.en.md +5 -3
  343. package/templates/.agents/skills/create-pr/reference/comment-publish.zh-CN.md +5 -3
  344. package/templates/.agents/skills/create-pr/reference/pr-body-template.en.md +1 -1
  345. package/templates/.agents/skills/create-pr/reference/pr-body-template.zh-CN.md +1 -1
  346. package/templates/.agents/skills/create-pr/scripts/change-report.mjs +3 -0
  347. package/templates/.agents/skills/create-task/SKILL.en.md +8 -0
  348. package/templates/.agents/skills/create-task/SKILL.zh-CN.md +7 -0
  349. package/templates/.agents/skills/import-codescan/SKILL.en.md +1 -1
  350. package/templates/.agents/skills/import-codescan/SKILL.zh-CN.md +1 -1
  351. package/templates/.agents/skills/import-dependabot/SKILL.en.md +1 -1
  352. package/templates/.agents/skills/import-dependabot/SKILL.zh-CN.md +1 -1
  353. package/templates/.agents/skills/init-labels/SKILL.en.md +8 -8
  354. package/templates/.agents/skills/init-labels/SKILL.zh-CN.md +8 -8
  355. package/templates/.agents/skills/init-milestones/SKILL.en.md +9 -11
  356. package/templates/.agents/skills/init-milestones/SKILL.zh-CN.md +9 -11
  357. package/templates/.agents/skills/plan-task/SKILL.en.md +27 -4
  358. package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +26 -4
  359. package/templates/.agents/skills/plan-task/config/verify.en.json +4 -14
  360. package/templates/.agents/skills/plan-task/config/verify.zh-CN.json +4 -14
  361. package/templates/.agents/skills/review-analysis/SKILL.en.md +17 -4
  362. package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +17 -4
  363. package/templates/.agents/skills/review-analysis/config/verify.en.json +1 -18
  364. package/templates/.agents/skills/review-analysis/config/verify.zh-CN.json +1 -18
  365. package/templates/.agents/skills/review-analysis/reference/report-template.en.md +50 -23
  366. package/templates/.agents/skills/review-analysis/reference/report-template.zh-CN.md +45 -18
  367. package/templates/.agents/skills/review-code/SKILL.en.md +18 -5
  368. package/templates/.agents/skills/review-code/SKILL.zh-CN.md +18 -5
  369. package/templates/.agents/skills/review-code/config/verify.en.json +1 -23
  370. package/templates/.agents/skills/review-code/config/verify.zh-CN.json +1 -23
  371. package/templates/.agents/skills/review-code/reference/report-template.en.md +49 -22
  372. package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +45 -18
  373. package/templates/.agents/skills/review-plan/SKILL.en.md +17 -4
  374. package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +17 -4
  375. package/templates/.agents/skills/review-plan/config/verify.en.json +1 -18
  376. package/templates/.agents/skills/review-plan/config/verify.zh-CN.json +1 -18
  377. package/templates/.agents/skills/review-plan/reference/report-template.en.md +49 -22
  378. package/templates/.agents/skills/review-plan/reference/report-template.zh-CN.md +45 -18
  379. package/templates/.agents/skills/review-pr/SKILL.en.md +5 -1
  380. package/templates/.agents/skills/review-pr/SKILL.zh-CN.md +5 -1
  381. package/templates/.agents/skills/review-pr/reference/report-template.en.md +4 -3
  382. package/templates/.agents/skills/review-pr/reference/report-template.zh-CN.md +4 -3
  383. package/templates/.agents/skills/run-manual-validation/SKILL.en.md +9 -3
  384. package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +9 -3
  385. package/templates/.agents/skills/run-manual-validation/reference/report-template.en.md +3 -1
  386. package/templates/.agents/skills/run-manual-validation/reference/report-template.zh-CN.md +3 -1
  387. package/templates/.agents/skills/test/SKILL.en.md +13 -2
  388. package/templates/.agents/skills/test/SKILL.zh-CN.md +13 -2
  389. package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +14 -0
  390. package/templates/.agents/skills/watch-pr/SKILL.en.md +2 -2
  391. package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +2 -2
  392. package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.en.md +6 -0
  393. package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.zh-CN.md +6 -0
  394. package/templates/.agents/templates/task.en.md +24 -1
  395. package/templates/.agents/templates/task.zh-CN.md +24 -1
  396. package/templates/.github/workflows/pr-label.yml +7 -26
  397. package/templates/.agents/scripts/security-alerts.github.sh +0 -151
  398. package/templates/.agents/scripts/security-alerts.sh +0 -31
  399. package/templates/.agents/skills/init-labels/scripts/init-labels.github.sh +0 -146
  400. package/templates/.agents/skills/init-labels/scripts/init-labels.sh +0 -32
  401. package/templates/.agents/skills/init-milestones/scripts/init-milestones.github.sh +0 -294
  402. package/templates/.agents/skills/init-milestones/scripts/init-milestones.sh +0 -6
@@ -1,14 +1,62 @@
1
1
  import fs from 'node:fs';
2
- import { execFileSync } from 'node:child_process';
2
+ import path from 'node:path';
3
3
  import { enumerateArtifacts } from '../task/artifacts.js';
4
4
  import { parseTypedTaskFrontmatter } from '../task/frontmatter.js';
5
5
  import { renderHumanOverrideAudit } from '../task/human-override.js';
6
6
  import { resolveTaskRef } from '../task/resolve-ref.js';
7
- import { resolvePlatformContext } from './context.js';
8
- import { createGitHubClient } from './github-client.js';
9
- import { listRemoteComments, normalizeCommentContent, writeComment } from './issue-comments.js';
10
- import { platformResult } from './types.js';
7
+ import { TaskExecutionLockError, withTaskExecutionLock } from '../task/task-execution-lock.js';
11
8
  import { readPrDeliveryFact } from '../task/pr-delivery-fact.js';
9
+ import { resolvePlatformProviderContext } from './context.js';
10
+ import { normalizeCommentContent } from './issue-comments.js';
11
+ import { CONTROL_MARKER_PATTERN, escapeHtmlText, sanitizeMarkdownDocument, splitDocumentPlaceholder } from './comment-safety.js';
12
+ import { platformResult } from './types.js';
13
+ import { providerError, providerOperationContext, providerStatus, providerResourceToken, resourceIdentityNumber, unsupportedProviderOperation } from './provider-bridge.js';
14
+ import { buildPrChangeReport, CANONICAL_REPORT_PLACEHOLDER, readPrChangeReport, replaceCanonicalReportPlaceholder, runMechanicalChangeReport, taskIntentDigest, validateMechanicalReport, validatePrecheckCandidate, writePrChangeReportAtomic } from './pr-change-report.js';
15
+ function summaryMarker(taskId) {
16
+ return `<!-- sync-pr:${taskId}:summary -->`;
17
+ }
18
+ function escapeSummaryAuditText(value) {
19
+ return escapeHtmlText(value).replace(/\r\n?/g, '\n');
20
+ }
21
+ function renderSummaryAuditLine(value) {
22
+ const safe = escapeSummaryAuditText(value);
23
+ const longestRun = Math.max(0, ...(safe.match(/`+/g) || []).map((run) => run.length));
24
+ const delimiter = '`'.repeat(longestRun + 1);
25
+ return `${delimiter} ${safe} ${delimiter}`;
26
+ }
27
+ function renderSafeHumanOverrideAudit(value) {
28
+ const normalized = value.trim().replace(/\r\n?/g, '\n');
29
+ if (!normalized)
30
+ return '';
31
+ const [heading, ...rows] = normalized.split('\n');
32
+ const safeHeading = heading === '## Human Override Audit' ? heading : renderSummaryAuditLine(heading || '');
33
+ return [safeHeading, ...rows.map((row) => row ? renderSummaryAuditLine(row) : '')].join('\n');
34
+ }
35
+ function isSafeSummaryEnvelope(value, taskId) {
36
+ return value.includes(summaryMarker(taskId))
37
+ && (value.match(/<!--\s*sync-pr:/gi) || []).length === 1
38
+ && (value.match(/<!--\s*last-commit:/gi) || []).length === 1
39
+ && !/<!--\s*canonical-pr-change-report\b/i.test(value);
40
+ }
41
+ function taskReportPath(taskDir) {
42
+ return path.join(taskDir, 'pr-change-report.json');
43
+ }
44
+ function readBoundTaskSnapshot(taskMdPath) {
45
+ const taskContent = fs.readFileSync(taskMdPath, 'utf8');
46
+ const frontmatter = parseTypedTaskFrontmatter(taskContent);
47
+ const fact = readPrDeliveryFact(frontmatter);
48
+ const boundFact = fact.status === 'valid' && fact.fact.state === 'bound' ? fact.fact : null;
49
+ return { taskContent, boundFact };
50
+ }
51
+ function boundPrNumberForWarning(taskMdPath) {
52
+ try {
53
+ const { boundFact } = readBoundTaskSnapshot(taskMdPath);
54
+ return boundFact ? resourceIdentityNumber(boundFact.identity.resource) : null;
55
+ }
56
+ catch {
57
+ return null;
58
+ }
59
+ }
12
60
  function warningResultForPrimary(primaryResult) {
13
61
  if (primaryResult === 'pr_created')
14
62
  return 'pr_created_with_warnings';
@@ -16,11 +64,11 @@ function warningResultForPrimary(primaryResult) {
16
64
  return 'pr_reused_with_warnings';
17
65
  return 'no_op_with_warnings';
18
66
  }
19
- function summaryMarker(taskId) {
20
- return `<!-- sync-pr:${taskId}:summary -->`;
67
+ function platformError(error) {
68
+ return { code: error.code, message: error.message, retryable: error.retryable ?? false };
21
69
  }
22
70
  function buildPullRequestSummary(taskId, body, headSha, humanOverrideAudit = '') {
23
- const sections = [body.replace(/\s+$/, ''), humanOverrideAudit.trim()].filter(Boolean).join('\n\n');
71
+ const sections = [body.replace(/\s+$/, ''), renderSafeHumanOverrideAudit(humanOverrideAudit)].filter(Boolean).join('\n\n');
24
72
  return normalizeCommentContent(`${summaryMarker(taskId)}\n<!-- last-commit: ${headSha} -->\n\n${sections}\n`);
25
73
  }
26
74
  function reconcileSummaryComment(comments, taskId, desired) {
@@ -47,26 +95,308 @@ function canonicalArtifacts(taskDir) {
47
95
  if (!current || round > current.round)
48
96
  byFamily.set(family, { family, name: artifact.name, path: artifact.path, round });
49
97
  }
50
- return [...byFamily.values()].map(({ family, name, path }) => ({ family, name, path }));
98
+ return [...byFamily.values()].map(({ family, name, path: artifactPath }) => ({ family, name, path: artifactPath }));
99
+ }
100
+ function basePlatformResult(status, context, taskId, prNumber, error = null) {
101
+ return platformResult(status, {
102
+ platform: context.platform,
103
+ capabilities: context.capabilities,
104
+ operations: context.operations,
105
+ resource: { kind: 'pull-request', number: prNumber },
106
+ error,
107
+ ...(taskId ? { task: { id: taskId, prNumber } } : {})
108
+ });
109
+ }
110
+ function isHardSummaryFailure(error) {
111
+ return error?.code === 'PR_SUMMARY_HEAD_RACE'
112
+ || error?.code === 'PR_SUMMARY_POSTWRITE_VERIFY_FAILED'
113
+ || error?.code === 'PR_SUMMARY_RACE_COMPENSATION_FAILED';
114
+ }
115
+ function inspectionError(error) {
116
+ return error.retryable ? 'blocked' : 'failed';
117
+ }
118
+ async function inspectBoundPullRequest(context, repoRoot, prNumber, loadedContext) {
119
+ if (!context.platform.repository) {
120
+ return { ok: false, status: context.status, error: context.error };
121
+ }
122
+ const loaded = loadedContext
123
+ ? { ok: true, value: loadedContext }
124
+ : await resolvePlatformProviderContext({ cwd: repoRoot });
125
+ if (!loaded.ok)
126
+ return { ok: false, status: loaded.context.status, error: loaded.context.error };
127
+ const target = providerResourceToken(loaded.value.provider, 'pull-request', String(prNumber));
128
+ const inspected = loaded.value.provider.changeRequests?.inspect
129
+ ? await loaded.value.provider.changeRequests.inspect({ context: providerOperationContext(loaded.value), target })
130
+ : unsupportedProviderOperation(loaded.value.provider, 'changeRequests.inspect');
131
+ if (!inspected.ok) {
132
+ const error = providerError(inspected.error, 'PLATFORM_PROVIDER_OPERATION_FAILED');
133
+ return { ok: false, status: inspectionError(error), error };
134
+ }
135
+ const remote = inspected.value;
136
+ const number = remote.number ?? resourceIdentityNumber(remote.identity) ?? prNumber;
137
+ const head = remote.head || { repository: context.platform.repository, ref: '', sha: remote.headSha || '' };
138
+ const base = remote.base || { repository: context.platform.repository, ref: '', sha: remote.baseSha || '' };
139
+ return {
140
+ ok: true,
141
+ value: {
142
+ repository: context.platform.repository,
143
+ number,
144
+ nodeId: remote.id,
145
+ url: remote.displayUrl || '',
146
+ state: remote.state === 'closed' ? 'closed' : 'open',
147
+ title: remote.title,
148
+ body: remote.body,
149
+ draft: remote.draft ?? false,
150
+ head: { ...head, sha: remote.headSha || head.sha },
151
+ base: { ...base, sha: remote.baseSha || base.sha },
152
+ mergedAt: remote.mergedAt ?? null,
153
+ mergeCommitSha: remote.mergeCommitSha ?? null,
154
+ labels: [...(remote.labels || [])].sort(),
155
+ assignees: [...(remote.assignees || [])].sort(),
156
+ milestone: remote.milestone ?? null,
157
+ mergeability: remote.mergeability || { state: 'unknown', detail: null }
158
+ }
159
+ };
160
+ }
161
+ function sameIdentity(left, right) {
162
+ return JSON.stringify({
163
+ repository: left.repository,
164
+ number: left.number,
165
+ base: left.base,
166
+ head: left.head
167
+ }) === JSON.stringify({
168
+ repository: right.repository,
169
+ number: right.number,
170
+ base: right.base,
171
+ head: right.head
172
+ });
173
+ }
174
+ function identityFromSnapshot(snapshot) {
175
+ return {
176
+ repository: snapshot.repository,
177
+ number: snapshot.number,
178
+ base: { ...snapshot.base },
179
+ head: { ...snapshot.head }
180
+ };
181
+ }
182
+ function readJsonInput(file) {
183
+ try {
184
+ return { ok: true, value: JSON.parse(fs.readFileSync(file, 'utf8')) };
185
+ }
186
+ catch (error) {
187
+ const errno = error.code;
188
+ return {
189
+ ok: false,
190
+ error: {
191
+ code: errno === 'ENOENT' ? 'PR_CHANGE_REPORT_INPUT_MISSING' : 'PR_CHANGE_REPORT_INPUT_INVALID',
192
+ message: error instanceof Error ? error.message : String(error),
193
+ retryable: false
194
+ }
195
+ };
196
+ }
197
+ }
198
+ function sameMechanical(left, right) {
199
+ return JSON.stringify(left) === JSON.stringify(right);
200
+ }
201
+ function currentReportCheck(report, snapshot, taskContent, repoRoot) {
202
+ const digest = taskIntentDigest(taskContent);
203
+ if (!digest.ok)
204
+ return { ok: false, error: platformError(digest.error) };
205
+ const reportIdentity = report.identity;
206
+ if (reportIdentity.repository !== snapshot.repository || reportIdentity.number !== snapshot.number ||
207
+ JSON.stringify(reportIdentity.base) !== JSON.stringify(snapshot.base) || JSON.stringify(reportIdentity.head) !== JSON.stringify(snapshot.head))
208
+ return {
209
+ ok: false,
210
+ error: { code: 'PR_CHANGE_REPORT_STALE', message: 'Change report identity does not match the authoritative pull request snapshot', retryable: false }
211
+ };
212
+ if (report.inputs.taskIntentSha256 !== digest.value.sha256)
213
+ return {
214
+ ok: false,
215
+ error: { code: 'PR_CHANGE_REPORT_STALE', message: 'Change report task intent digest is stale', retryable: false }
216
+ };
217
+ let mechanical;
218
+ try {
219
+ mechanical = runMechanicalChangeReport(repoRoot, snapshot.base.sha, snapshot.head.sha);
220
+ }
221
+ catch (error) {
222
+ return { ok: false, error: { code: 'PR_CHANGE_REPORT_GIT_FAILED', message: error instanceof Error ? error.message : String(error), retryable: false } };
223
+ }
224
+ const reportMechanical = {
225
+ version: 1,
226
+ base: snapshot.base.sha,
227
+ head: snapshot.head.sha,
228
+ mergeBase: report.diff.mergeBase,
229
+ patchSha256: report.diff.patchSha256,
230
+ files: report.diff.files,
231
+ totals: report.diff.totals
232
+ };
233
+ if (!sameMechanical(reportMechanical, mechanical))
234
+ return {
235
+ ok: false,
236
+ error: { code: 'PR_CHANGE_REPORT_STALE', message: 'Change report patch or mechanical statistics are stale', retryable: false }
237
+ };
238
+ return { ok: true, digest: digest.value.sha256, mechanical };
239
+ }
240
+ function summaryReportStatus(reportPath, snapshot, taskContent, repoRoot) {
241
+ const digest = taskIntentDigest(taskContent);
242
+ if (!digest.ok)
243
+ return { status: 'invalid', taskIntentSha256: null, reason: digest.error.message };
244
+ const report = readPrChangeReport(reportPath);
245
+ if (!report.ok)
246
+ return { status: report.error.code === 'PR_CHANGE_REPORT_MISSING' ? 'missing' : 'invalid', taskIntentSha256: digest.value.sha256, reason: report.error.message };
247
+ if (!snapshot)
248
+ return { status: 'stale', taskIntentSha256: digest.value.sha256, reason: 'No bound pull request snapshot is available' };
249
+ const checked = currentReportCheck(report.value, snapshot, taskContent, repoRoot);
250
+ return checked.ok
251
+ ? { status: 'ready', taskIntentSha256: checked.digest }
252
+ : { status: checked.error.code === 'PR_CHANGE_REPORT_STALE' ? 'stale' : 'invalid', taskIntentSha256: digest.value.sha256, reason: checked.error.message };
51
253
  }
52
- function summaryContext(taskRef, options = {}) {
254
+ async function summaryContext(taskRef, options = {}) {
53
255
  const resolved = resolveTaskRef(taskRef, options.cwd ? { repoRoot: options.cwd } : {});
54
256
  if (!resolved.ok)
55
- return { ...platformResult('failed', { error: { code: resolved.code, message: resolved.message, retryable: false } }), task: { id: resolved.taskId, prNumber: null }, artifacts: [] };
56
- const frontmatter = parseTypedTaskFrontmatter(fs.readFileSync(resolved.taskMdPath, 'utf8'));
57
- const fact = readPrDeliveryFact(frontmatter);
58
- const prNumber = fact.status === 'valid' && fact.fact.state === 'bound' ? fact.fact.identity.number : null;
59
- return { ...platformResult('no-op'), task: { id: resolved.taskId, prNumber }, artifacts: canonicalArtifacts(resolved.taskDir) };
257
+ return {
258
+ ...platformResult('failed', { error: { code: resolved.code, message: resolved.message, retryable: false } }),
259
+ task: { id: resolved.taskId, prNumber: null }, pullRequest: null,
260
+ changeReport: { status: 'invalid', path: '', taskIntentSha256: null, reason: resolved.message }, artifacts: []
261
+ };
262
+ const taskContent = fs.readFileSync(resolved.taskMdPath, 'utf8');
263
+ const frontmatter = parseTypedTaskFrontmatter(taskContent);
264
+ const fact = readPrDeliveryFact(frontmatter, options.runtimeVersion);
265
+ const prNumber = fact.status === 'valid' && fact.fact.state === 'bound' ? resourceIdentityNumber(fact.fact.identity.resource) : null;
266
+ const reportPath = taskReportPath(resolved.taskDir);
267
+ const loaded = await resolvePlatformProviderContext({ cwd: resolved.repoRoot, client: options.client });
268
+ const context = loaded.ok ? loaded.value.context : loaded.context;
269
+ let pullRequest = null;
270
+ if (loaded.ok && prNumber && context.platform.repository && ['no-op', 'degraded'].includes(context.status)) {
271
+ const inspected = await inspectBoundPullRequest(context, resolved.repoRoot, prNumber, loaded.value);
272
+ if (inspected.ok)
273
+ pullRequest = inspected.value;
274
+ }
275
+ const report = summaryReportStatus(reportPath, pullRequest, taskContent, resolved.repoRoot);
276
+ return {
277
+ ...platformResult(context.status, {
278
+ platform: context.platform,
279
+ capabilities: context.capabilities,
280
+ operations: context.operations,
281
+ error: context.error
282
+ }),
283
+ task: { id: resolved.taskId, prNumber },
284
+ pullRequest,
285
+ changeReport: { status: report.status, path: reportPath, taskIntentSha256: report.taskIntentSha256, ...(report.reason ? { reason: report.reason } : {}) },
286
+ artifacts: canonicalArtifacts(resolved.taskDir)
287
+ };
60
288
  }
61
- function syncPullRequestSummary(taskRef, options) {
289
+ async function reportWrite(taskRef, options) {
290
+ const resolved = resolveTaskRef(taskRef, options.cwd ? { repoRoot: options.cwd } : {});
291
+ if (!resolved.ok)
292
+ return { ...platformResult('failed', { error: { code: resolved.code, message: resolved.message, retryable: false } }), report: null };
293
+ const loaded = await resolvePlatformProviderContext({ cwd: resolved.repoRoot, client: options.client });
294
+ const context = loaded.ok ? loaded.value.context : loaded.context;
295
+ if (!loaded.ok || !context.platform.repository || !['no-op', 'degraded'].includes(context.status))
296
+ return { ...context, report: null };
297
+ let knownPrNumber = null;
298
+ try {
299
+ return await withTaskExecutionLock(resolved.repoRoot, resolved.taskId, options.agent, async () => {
300
+ const { taskContent, boundFact } = readBoundTaskSnapshot(resolved.taskMdPath);
301
+ if (!boundFact)
302
+ return {
303
+ ...basePlatformResult('failed', context, resolved.taskId, null, { code: 'PR_NOT_LINKED', message: 'Task has no verified bound pull request', retryable: false }),
304
+ report: null
305
+ };
306
+ const boundPrNumber = resourceIdentityNumber(boundFact.identity.resource);
307
+ if (!boundPrNumber)
308
+ return {
309
+ ...basePlatformResult('failed', context, resolved.taskId, null, { code: 'PR_NUMBER_INVALID', message: 'Bound pull request has no numeric identifier', retryable: false }),
310
+ report: null
311
+ };
312
+ knownPrNumber = boundPrNumber;
313
+ const initial = await inspectBoundPullRequest(context, resolved.repoRoot, boundPrNumber, loaded.value);
314
+ if (!initial.ok)
315
+ return { ...basePlatformResult(initial.status, context, resolved.taskId, boundPrNumber, initial.error), report: null };
316
+ const digest = taskIntentDigest(taskContent);
317
+ if (!digest.ok)
318
+ return { ...basePlatformResult('failed', context, resolved.taskId, boundPrNumber, platformError(digest.error)), report: null };
319
+ const mechanicalInput = readJsonInput(path.resolve(resolved.repoRoot, options.mechanicalFile));
320
+ if (!mechanicalInput.ok)
321
+ return { ...basePlatformResult('failed', context, resolved.taskId, boundPrNumber, mechanicalInput.error), report: null };
322
+ const mechanical = validateMechanicalReport(mechanicalInput.value);
323
+ if (!mechanical.ok)
324
+ return { ...basePlatformResult('failed', context, resolved.taskId, boundPrNumber, platformError(mechanical.error)), report: null };
325
+ const candidateInput = readJsonInput(path.resolve(resolved.repoRoot, options.precheckFile));
326
+ if (!candidateInput.ok)
327
+ return { ...basePlatformResult('failed', context, resolved.taskId, boundPrNumber, candidateInput.error), report: null };
328
+ const candidate = validatePrecheckCandidate(candidateInput.value);
329
+ if (!candidate.ok)
330
+ return { ...basePlatformResult('failed', context, resolved.taskId, boundPrNumber, platformError(candidate.error)), report: null };
331
+ let recomputed;
332
+ try {
333
+ recomputed = runMechanicalChangeReport(resolved.repoRoot, initial.value.base.sha, initial.value.head.sha);
334
+ }
335
+ catch (error) {
336
+ return { ...basePlatformResult('failed', context, resolved.taskId, boundPrNumber, { code: 'PR_CHANGE_REPORT_GIT_FAILED', message: error instanceof Error ? error.message : String(error), retryable: false }), report: null };
337
+ }
338
+ if (!sameMechanical(mechanical.value, recomputed))
339
+ return {
340
+ ...basePlatformResult('failed', context, resolved.taskId, boundPrNumber, { code: 'PR_CHANGE_REPORT_MECHANICAL_MISMATCH', message: 'Mechanical report input does not match the current complete diff', retryable: false }),
341
+ report: null
342
+ };
343
+ const built = buildPrChangeReport(identityFromSnapshot(initial.value), digest.value.sha256, mechanical.value, candidate.value);
344
+ if (!built.ok)
345
+ return { ...basePlatformResult('failed', context, resolved.taskId, boundPrNumber, platformError(built.error)), report: null };
346
+ const final = await inspectBoundPullRequest(context, resolved.repoRoot, boundPrNumber, loaded.value);
347
+ if (!final.ok)
348
+ return { ...basePlatformResult(final.status, context, resolved.taskId, boundPrNumber, final.error), report: null };
349
+ if (!sameIdentity(initial.value, final.value))
350
+ return {
351
+ ...basePlatformResult('blocked', context, resolved.taskId, boundPrNumber, { code: 'PR_CHANGE_REPORT_HEAD_RACE', message: 'Pull request identity changed while generating the change report', retryable: true }),
352
+ report: null
353
+ };
354
+ const target = taskReportPath(resolved.taskDir);
355
+ const existing = readPrChangeReport(target);
356
+ const reportInfo = {
357
+ path: target,
358
+ precheckVerdict: built.value.precheck.verdict,
359
+ nextAction: built.value.precheck.route
360
+ };
361
+ if (existing.ok && JSON.stringify(existing.value) === JSON.stringify(built.value))
362
+ return {
363
+ ...basePlatformResult('no-op', context, resolved.taskId, boundPrNumber),
364
+ report: { ...reportInfo, status: 'no-op' }
365
+ };
366
+ if (options.dryRun)
367
+ return {
368
+ ...basePlatformResult('planned', context, resolved.taskId, boundPrNumber),
369
+ operations: [{ name: 'change-report:write', status: 'planned', reasonCode: null }],
370
+ report: { ...reportInfo, status: 'planned' }
371
+ };
372
+ try {
373
+ writePrChangeReportAtomic(target, built.value);
374
+ }
375
+ catch (error) {
376
+ return { ...basePlatformResult('failed', context, resolved.taskId, boundPrNumber, { code: 'PR_CHANGE_REPORT_WRITE_FAILED', message: error instanceof Error ? error.message : String(error), retryable: false }), report: null };
377
+ }
378
+ return {
379
+ ...basePlatformResult('applied', context, resolved.taskId, boundPrNumber),
380
+ operations: [{ name: 'change-report:write', status: 'applied', reasonCode: null }],
381
+ report: { ...reportInfo, status: 'written' }
382
+ };
383
+ });
384
+ }
385
+ catch (error) {
386
+ const lockError = error instanceof TaskExecutionLockError
387
+ ? { code: error.code, message: error.message, retryable: error.code === 'ORCHESTRATION_LOCK_BUSY' }
388
+ : { code: 'PR_CHANGE_REPORT_FAILED', message: error instanceof Error ? error.message : String(error), retryable: false };
389
+ return { ...basePlatformResult(lockError.retryable ? 'blocked' : 'failed', context, resolved.taskId, knownPrNumber, lockError), report: null };
390
+ }
391
+ }
392
+ async function syncPullRequestSummary(taskRef, options) {
62
393
  const warningResult = warningResultForPrimary(options.primaryResult);
63
394
  let knownPrNumber = null;
64
395
  const softenFailure = (output) => {
65
396
  const prNumber = output.resource.kind === 'pull-request' && output.resource.number
66
397
  ? output.resource.number
67
398
  : knownPrNumber;
68
- const warning = output.error && prNumber
69
- && output.error.code !== 'PR_NOT_LINKED'
399
+ const warning = output.error && prNumber && output.error.code !== 'PR_NOT_LINKED'
70
400
  ? {
71
401
  code: output.error.code,
72
402
  message: output.error.message,
@@ -88,45 +418,206 @@ function syncPullRequestSummary(taskRef, options) {
88
418
  }
89
419
  : { ...output, result: null, warnings: [] };
90
420
  };
421
+ const preserveFailure = (output) => options.strict
422
+ ? { ...output, result: null, warnings: [] }
423
+ : softenFailure(output);
91
424
  const resolved = resolveTaskRef(taskRef, options.cwd ? { repoRoot: options.cwd } : {});
92
425
  if (!resolved.ok)
93
426
  return softenFailure(platformResult('failed', { error: { code: resolved.code, message: resolved.message, retryable: false } }));
427
+ const fail = (status, context, error, prNumber = knownPrNumber) => {
428
+ const output = basePlatformResult(status, context, resolved.taskId, prNumber, error);
429
+ return options.strict || isHardSummaryFailure(error)
430
+ ? { ...output, result: null, warnings: [] }
431
+ : { ...softenFailure(output) };
432
+ };
94
433
  const frontmatter = parseTypedTaskFrontmatter(fs.readFileSync(resolved.taskMdPath, 'utf8'));
95
- const fact = readPrDeliveryFact(frontmatter);
434
+ const fact = readPrDeliveryFact(frontmatter, options.runtimeVersion);
96
435
  if (fact.status === 'invalid')
97
- return softenFailure(platformResult('failed', { error: { code: 'PR_DELIVERY_FACT_INVALID', message: fact.error.message, retryable: false } }));
98
- const prNumber = fact.status === 'valid' && fact.fact.state === 'bound' ? fact.fact.identity.number : null;
99
- if (!prNumber)
100
- return softenFailure(platformResult('failed', { error: { code: fact.status === 'missing' ? 'PR_DELIVERY_FACT_MISSING' : 'PR_NOT_LINKED', message: 'Task has no verified bound pull request', retryable: false } }));
101
- knownPrNumber = prNumber;
102
- const client = options.client || createGitHubClient();
103
- const context = resolvePlatformContext({ cwd: resolved.repoRoot, client });
104
- if (!context.platform.repository || !['no-op', 'degraded'].includes(context.status))
105
- return softenFailure(context);
106
- let headSha;
436
+ return preserveFailure(platformResult('failed', { error: { code: fact.error.code, message: fact.error.message, retryable: false } }));
437
+ if (!options.changeReportFile)
438
+ return preserveFailure(platformResult('failed', { error: { code: 'PR_CHANGE_REPORT_MISSING', message: 'summary-sync requires --change-report-file', retryable: false } }));
439
+ const loaded = await resolvePlatformProviderContext({ cwd: resolved.repoRoot, client: options.client });
440
+ const context = loaded.ok ? loaded.value.context : loaded.context;
441
+ if (!loaded.ok || !context.platform.repository || !['no-op', 'degraded'].includes(context.status)) {
442
+ // This diagnostic-only read preserves the known PR target when context resolution fails before the execution lock.
443
+ knownPrNumber = boundPrNumberForWarning(resolved.taskMdPath);
444
+ return preserveFailure(context);
445
+ }
107
446
  try {
108
- headSha = execFileSync('git', ['rev-parse', 'HEAD'], { cwd: resolved.repoRoot, encoding: 'utf8' }).trim();
447
+ return await withTaskExecutionLock(resolved.repoRoot, resolved.taskId, options.agent, async () => {
448
+ const { taskContent, boundFact } = readBoundTaskSnapshot(resolved.taskMdPath);
449
+ if (!boundFact)
450
+ return {
451
+ ...platformResult('failed', { resource: { kind: 'pull-request', number: null }, error: { code: 'PR_NOT_LINKED', message: 'Task has no verified bound pull request', retryable: false } }),
452
+ result: null, warnings: []
453
+ };
454
+ const prIdentity = boundFact.identity.resource;
455
+ const boundPrNumber = resourceIdentityNumber(prIdentity);
456
+ if (!boundPrNumber)
457
+ return {
458
+ ...platformResult('failed', { resource: { kind: 'pull-request', number: null }, error: { code: 'PR_NUMBER_INVALID', message: 'Bound pull request has no numeric identifier', retryable: false } }),
459
+ result: null, warnings: []
460
+ };
461
+ knownPrNumber = boundPrNumber;
462
+ const prNumber = boundPrNumber;
463
+ const initial = await inspectBoundPullRequest(context, resolved.repoRoot, boundPrNumber, loaded.value);
464
+ if (!initial.ok)
465
+ return fail(initial.status, context, initial.error);
466
+ const expectedReportPath = taskReportPath(resolved.taskDir);
467
+ const suppliedReportPath = path.resolve(resolved.repoRoot, options.changeReportFile);
468
+ if (suppliedReportPath !== expectedReportPath)
469
+ return fail('failed', context, { code: 'PR_CHANGE_REPORT_PATH_INVALID', message: 'summary-sync must consume the task-bound pr-change-report.json', retryable: false });
470
+ const report = readPrChangeReport(suppliedReportPath);
471
+ if (!report.ok)
472
+ return fail('failed', context, platformError(report.error));
473
+ const checked = currentReportCheck(report.value, initial.value, taskContent, resolved.repoRoot);
474
+ if (!checked.ok)
475
+ return fail('failed', context, checked.error);
476
+ const placeholder = CANONICAL_REPORT_PLACEHOLDER;
477
+ const split = splitDocumentPlaceholder(options.body, placeholder);
478
+ if (!split.ok)
479
+ return fail('failed', context, {
480
+ code: 'PR_SUMMARY_BODY_CONTRACT_INVALID',
481
+ message: split.error.message,
482
+ retryable: false
483
+ });
484
+ const prefix = sanitizeMarkdownDocument(split.value.prefix, { reservedMarkers: [CONTROL_MARKER_PATTERN] });
485
+ const suffix = sanitizeMarkdownDocument(split.value.suffix, { reservedMarkers: [CONTROL_MARKER_PATTERN] });
486
+ if (!prefix.ok) {
487
+ return fail('failed', context, {
488
+ code: 'PR_SUMMARY_BODY_CONTRACT_INVALID',
489
+ message: `${prefix.error.message} at offset ${prefix.error.offset}`,
490
+ retryable: false
491
+ });
492
+ }
493
+ if (!suffix.ok) {
494
+ return fail('failed', context, {
495
+ code: 'PR_SUMMARY_BODY_CONTRACT_INVALID',
496
+ message: `${suffix.error.message} at offset ${suffix.error.offset}`,
497
+ retryable: false
498
+ });
499
+ }
500
+ const replaced = replaceCanonicalReportPlaceholder(`${prefix.value}${placeholder}${suffix.value}`, report.value);
501
+ if (!replaced.ok)
502
+ return fail('failed', context, platformError(replaced.error));
503
+ const desired = buildPullRequestSummary(resolved.taskId, replaced.value, initial.value.head.sha, renderHumanOverrideAudit(taskContent));
504
+ if (!isSafeSummaryEnvelope(desired, resolved.taskId))
505
+ return fail('failed', context, {
506
+ code: 'PR_SUMMARY_RENDER_INVALID',
507
+ message: 'Summary contains an invalid or duplicated reserved control marker',
508
+ retryable: false
509
+ });
510
+ const listed = loaded.value.provider.comments?.list
511
+ ? await loaded.value.provider.comments.list({ context: providerOperationContext(loaded.value), parent: prIdentity }).then((response) => response.ok
512
+ ? { ok: true, value: response.value.map((comment) => ({ id: /^\d+$/.test(comment.id) ? Number(comment.id) : comment.id, body: comment.body })) }
513
+ : response)
514
+ : unsupportedProviderOperation(loaded.value.provider, 'comments.list');
515
+ if (!listed.ok)
516
+ return fail(providerStatus(listed.error) === 'blocked' ? 'blocked' : 'failed', context, providerError(listed.error, 'PLATFORM_PROVIDER_OPERATION_FAILED'));
517
+ const reconciliation = reconcileSummaryComment(listed.value, resolved.taskId, desired);
518
+ if (reconciliation.action === 'conflict')
519
+ return fail('failed', context, { code: 'PR_SUMMARY_MARKER_AMBIGUOUS', message: 'Multiple PR comments contain the summary marker', retryable: false });
520
+ const previousBody = reconciliation.action === 'update'
521
+ ? listed.value.find((comment) => comment.id === reconciliation.commentId)?.body ?? null
522
+ : null;
523
+ const info = { precheckVerdict: report.value.precheck.verdict, nextAction: report.value.precheck.route };
524
+ const beforeWrite = await inspectBoundPullRequest(context, resolved.repoRoot, boundPrNumber, loaded.value);
525
+ if (!beforeWrite.ok)
526
+ return fail(beforeWrite.status, context, beforeWrite.error, prNumber);
527
+ if (!sameIdentity(initial.value, beforeWrite.value))
528
+ return fail('blocked', context, {
529
+ code: 'PR_SUMMARY_HEAD_RACE',
530
+ message: 'Pull request head changed before publishing the summary',
531
+ retryable: true
532
+ }, prNumber);
533
+ if (reconciliation.action === 'no-op')
534
+ return {
535
+ ...basePlatformResult('no-op', context, resolved.taskId, prNumber),
536
+ comment: { kind: 'summary', marker: summaryMarker(resolved.taskId), ids: [reconciliation.commentId], parts: 1 },
537
+ result: null, warnings: [], ...info
538
+ };
539
+ if (options.dryRun)
540
+ return {
541
+ ...basePlatformResult('planned', context, resolved.taskId, prNumber),
542
+ operations: [{ name: `summary:${reconciliation.action}`, status: 'planned', reasonCode: null }],
543
+ result: null, warnings: [], ...info
544
+ };
545
+ const existingComment = reconciliation.commentId === null
546
+ ? undefined
547
+ : providerResourceToken(loaded.value.provider, 'comment', String(reconciliation.commentId));
548
+ const written = loaded.value.provider.comments?.write
549
+ ? await loaded.value.provider.comments.write({
550
+ context: providerOperationContext(loaded.value),
551
+ parent: prIdentity,
552
+ body: desired,
553
+ ...(existingComment ? { existingComment } : {}),
554
+ mutation: { idempotencyKey: `pr-summary:${resolved.taskId}` }
555
+ })
556
+ : unsupportedProviderOperation(loaded.value.provider, 'comments.write');
557
+ if (!written.ok)
558
+ return fail(providerStatus(written.error) === 'blocked' ? 'blocked' : 'failed', context, providerError(written.error, 'PLATFORM_PROVIDER_OPERATION_FAILED'), prNumber);
559
+ const after = await inspectBoundPullRequest(context, resolved.repoRoot, prNumber, loaded.value);
560
+ const id = /^\d+$/.test(written.value.remoteId) ? Number(written.value.remoteId) : written.value.remoteId;
561
+ const compensateWrittenSummary = async () => reconciliation.action === 'create'
562
+ ? written.value.remoteId && loaded.value.provider.comments?.delete
563
+ ? await loaded.value.provider.comments.delete({
564
+ context: providerOperationContext(loaded.value),
565
+ parent: prIdentity,
566
+ comment: providerResourceToken(loaded.value.provider, 'comment', written.value.remoteId),
567
+ mutation: { idempotencyKey: `pr-summary-delete:${resolved.taskId}:${written.value.remoteId}` }
568
+ })
569
+ : { ok: false, error: { code: 'PR_SUMMARY_RACE_COMPENSATION_FAILED', message: 'Created summary comment has no valid id', retryable: true } }
570
+ : reconciliation.commentId && previousBody !== null && loaded.value.provider.comments?.write
571
+ ? await loaded.value.provider.comments.write({
572
+ context: providerOperationContext(loaded.value),
573
+ parent: prIdentity,
574
+ body: previousBody,
575
+ existingComment: providerResourceToken(loaded.value.provider, 'comment', String(reconciliation.commentId)),
576
+ mutation: { idempotencyKey: `pr-summary-restore:${resolved.taskId}` }
577
+ })
578
+ : { ok: false, error: { code: 'PR_SUMMARY_RACE_COMPENSATION_FAILED', message: 'Previous summary body is unavailable for restoration', retryable: true } };
579
+ if (!after.ok) {
580
+ const compensated = await compensateWrittenSummary();
581
+ if (!compensated.ok)
582
+ return fail('blocked', context, {
583
+ code: 'PR_SUMMARY_RACE_COMPENSATION_FAILED',
584
+ message: `Summary post-write verification failed and compensation failed: ${compensated.error.message}`,
585
+ retryable: true
586
+ }, prNumber);
587
+ return fail('blocked', context, {
588
+ code: 'PR_SUMMARY_POSTWRITE_VERIFY_FAILED',
589
+ message: `Summary was written but pull-request verification failed: ${after.error?.message || 'unknown verification error'}; the previous summary was restored or the new comment was deleted`,
590
+ retryable: true
591
+ }, prNumber);
592
+ }
593
+ if (!sameIdentity(initial.value, after.value)) {
594
+ const compensated = await compensateWrittenSummary();
595
+ if (!compensated.ok)
596
+ return fail('blocked', context, {
597
+ code: 'PR_SUMMARY_RACE_COMPENSATION_FAILED',
598
+ message: `Pull-request head changed and summary compensation failed: ${compensated.error.message}`,
599
+ retryable: true
600
+ }, prNumber);
601
+ return fail('blocked', context, {
602
+ code: 'PR_SUMMARY_HEAD_RACE',
603
+ message: 'Pull-request head changed while publishing; the previous summary was restored or the new comment was deleted; rerun report and summary sync',
604
+ retryable: true
605
+ }, prNumber);
606
+ }
607
+ return {
608
+ ...basePlatformResult('applied', context, resolved.taskId, prNumber),
609
+ comment: { kind: 'summary', marker: summaryMarker(resolved.taskId), ids: Number.isInteger(id) ? [id] : [], parts: 1 },
610
+ operations: [{ name: `summary:${reconciliation.action}`, status: 'applied', reasonCode: null }],
611
+ result: null, warnings: [], ...info
612
+ };
613
+ });
109
614
  }
110
615
  catch (error) {
111
- return softenFailure(platformResult('failed', { platform: context.platform, capabilities: context.capabilities, error: { code: 'GIT_HEAD_UNRESOLVED', message: error instanceof Error ? error.message : String(error), retryable: false } }));
616
+ const lockError = error instanceof TaskExecutionLockError
617
+ ? { code: error.code, message: error.message, retryable: error.code === 'ORCHESTRATION_LOCK_BUSY' }
618
+ : { code: 'PR_SUMMARY_FAILED', message: error instanceof Error ? error.message : String(error), retryable: false };
619
+ return fail(lockError.retryable ? 'blocked' : 'failed', context, lockError);
112
620
  }
113
- const desired = buildPullRequestSummary(resolved.taskId, options.body, headSha, renderHumanOverrideAudit(fs.readFileSync(resolved.taskMdPath, 'utf8')));
114
- const listed = listRemoteComments(client, context.platform.repository, prNumber, resolved.repoRoot);
115
- if (!listed.ok)
116
- return softenFailure(platformResult(listed.error.retryable ? 'blocked' : 'failed', { platform: context.platform, capabilities: context.capabilities, resource: { kind: 'pull-request', number: prNumber }, error: listed.error }));
117
- const reconciliation = reconcileSummaryComment(listed.value, resolved.taskId, desired);
118
- if (reconciliation.action === 'conflict')
119
- return softenFailure(platformResult('failed', { platform: context.platform, capabilities: context.capabilities, resource: { kind: 'pull-request', number: prNumber }, error: { code: 'PR_SUMMARY_MARKER_AMBIGUOUS', message: 'Multiple PR comments contain the summary marker', retryable: false } }));
120
- if (reconciliation.action === 'no-op')
121
- return softenFailure(platformResult('no-op', { platform: context.platform, capabilities: context.capabilities, resource: { kind: 'pull-request', number: prNumber }, comment: { kind: 'summary', marker: summaryMarker(resolved.taskId), ids: [reconciliation.commentId], parts: 1 }, error: null }));
122
- if (options.dryRun)
123
- return softenFailure(platformResult('planned', { platform: context.platform, capabilities: context.capabilities, resource: { kind: 'pull-request', number: prNumber }, operations: [{ name: `summary:${reconciliation.action}`, status: 'planned', reasonCode: null }], error: null }));
124
- const written = writeComment(client, context.platform.repository, prNumber, resolved.repoRoot, desired, reconciliation.commentId || undefined);
125
- if (!written.ok)
126
- return softenFailure(platformResult(written.error.retryable ? 'blocked' : 'failed', { platform: context.platform, capabilities: context.capabilities, resource: { kind: 'pull-request', number: prNumber }, error: written.error }));
127
- const id = Number(written.value.id || reconciliation.commentId);
128
- return softenFailure(platformResult('applied', { platform: context.platform, capabilities: context.capabilities, resource: { kind: 'pull-request', number: prNumber }, comment: { kind: 'summary', marker: summaryMarker(resolved.taskId), ids: Number.isInteger(id) ? [id] : [], parts: 1 }, operations: [{ name: `summary:${reconciliation.action}`, status: 'applied', reasonCode: null }], error: null }));
129
- }
130
- export { buildPullRequestSummary, reconcileSummaryComment, summaryContext, summaryMarker, syncPullRequestSummary };
131
- export { warningResultForPrimary };
621
+ }
622
+ export { buildPullRequestSummary, reconcileSummaryComment, reportWrite, summaryContext, summaryMarker, syncPullRequestSummary, warningResultForPrimary };
132
623
  //# sourceMappingURL=pr-summary.js.map