@fitlab-ai/agent-infra 0.9.14 → 0.10.0

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 (354) hide show
  1. package/README.md +10 -0
  2. package/README.zh-CN.md +10 -0
  3. package/bin/internal-cli.sh +45 -0
  4. package/dist/bin/internal-cli.js +93 -30
  5. package/dist/lib/agent-clients/adapters/codex-lifecycle/capability-store.js +165 -27
  6. package/dist/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.js +9 -4
  7. package/dist/lib/agent-clients/adapters/codex-lifecycle/store.js +43 -3
  8. package/dist/lib/defaults.json +103 -103
  9. package/dist/lib/fs/durable-write.js +39 -0
  10. package/dist/lib/fs/file-lock.js +111 -0
  11. package/dist/lib/host-control/audit.js +19 -0
  12. package/dist/lib/host-control/client.js +142 -0
  13. package/dist/lib/host-control/command.js +49 -0
  14. package/dist/lib/host-control/path.js +160 -0
  15. package/dist/lib/host-control/secure-fs.js +140 -0
  16. package/dist/lib/host-control/server.js +159 -0
  17. package/dist/lib/host-control/service.js +112 -0
  18. package/dist/lib/init.js +11 -2
  19. package/dist/lib/internal/cli-route-inventory.js +4 -2
  20. package/dist/lib/internal/codex-lifecycle.js +6 -6
  21. package/dist/lib/internal/demo-promotion.js +322 -0
  22. package/dist/lib/internal/demo-transcript.js +454 -0
  23. package/dist/lib/internal/host-control.js +28 -0
  24. package/dist/lib/internal/manual-validation.js +331 -0
  25. package/dist/lib/internal/platform-pr.js +18 -4
  26. package/dist/lib/internal/release-workflow.js +63 -84
  27. package/dist/lib/internal/sandbox-control.js +149 -2
  28. package/dist/lib/internal/task-artifact.js +16 -263
  29. package/dist/lib/internal/task-create.js +6 -5
  30. package/dist/lib/internal/task-event.js +37 -37
  31. package/dist/lib/internal/task-lifecycle.js +2 -2
  32. package/dist/lib/internal/task-operation-registry.js +101 -4
  33. package/dist/lib/internal/task-review.js +11 -56
  34. package/dist/lib/internal/task-validate.js +30 -10
  35. package/dist/lib/owned-path.js +33 -0
  36. package/dist/lib/platform/check-status.js +11 -0
  37. package/dist/lib/platform/comment-safety.js +4 -1
  38. package/dist/lib/platform/completion-backfill.js +1 -1
  39. package/dist/lib/platform/github-data.js +241 -0
  40. package/dist/lib/platform/github-provider.js +15 -20
  41. package/dist/lib/platform/issue-comments.js +18 -12
  42. package/dist/lib/platform/issues.js +2 -180
  43. package/dist/lib/platform/pr-change-report.js +5 -4
  44. package/dist/lib/platform/pr-checks.js +12 -65
  45. package/dist/lib/platform/pr-summary.js +117 -33
  46. package/dist/lib/platform/pull-requests.js +5 -108
  47. package/dist/lib/platform/snapshots.js +2 -0
  48. package/dist/lib/platform/task-identities.js +2 -9
  49. package/dist/lib/platform/verification-sync.js +132 -143
  50. package/dist/lib/pr-review/evidence-grading.js +4 -8
  51. package/dist/lib/release/workflow.js +1 -1
  52. package/dist/lib/sandbox/commands/create.js +7 -2
  53. package/dist/lib/sandbox/commands/rm.js +19 -1511
  54. package/dist/lib/sandbox/control/audit.js +203 -0
  55. package/dist/lib/sandbox/control/client.js +93 -54
  56. package/dist/lib/sandbox/control/executor.js +129 -65
  57. package/dist/lib/sandbox/control/finalization-response.js +27 -0
  58. package/dist/lib/sandbox/control/identity-sentinel.js +107 -0
  59. package/dist/lib/sandbox/control/lifecycle.js +36 -2
  60. package/dist/lib/sandbox/control/native-file-lock.js +6 -98
  61. package/dist/lib/sandbox/control/protocol.js +55 -4
  62. package/dist/lib/sandbox/control/server.js +518 -127
  63. package/dist/lib/sandbox/control/state.js +64 -0
  64. package/dist/lib/sandbox/control/task-workflow.js +74 -0
  65. package/dist/lib/sandbox/control/workflow-executor.js +89 -0
  66. package/dist/lib/sandbox/index.js +1 -2
  67. package/dist/lib/sandbox/recovery.js +28 -4
  68. package/dist/lib/sandbox/removal.js +822 -0
  69. package/dist/lib/sandbox/task-cleanup.js +217 -0
  70. package/dist/lib/sandbox/workspace-identity.js +17 -1
  71. package/dist/lib/sandbox/workspace-view.js +13 -14
  72. package/dist/lib/task/activity-intent.js +2 -1
  73. package/dist/lib/task/activity-log.js +30 -24
  74. package/dist/lib/task/artifact-command.js +79 -0
  75. package/dist/lib/task/artifact-lifecycle.js +30 -28
  76. package/dist/lib/task/artifact-name.js +36 -0
  77. package/dist/lib/task/artifact-operations.js +29 -289
  78. package/dist/lib/task/artifact-receipts.js +3 -15
  79. package/dist/lib/task/artifact-recovery.js +430 -0
  80. package/dist/lib/task/artifact-repair-intent.js +134 -29
  81. package/dist/lib/task/artifact-schema.js +6 -14
  82. package/dist/lib/task/capabilities.js +12 -19
  83. package/dist/lib/task/codex-orchestration.js +71 -25
  84. package/dist/lib/task/commit-identity.js +28 -0
  85. package/dist/lib/task/commit-operation.js +33 -20
  86. package/dist/lib/task/control-authority.js +403 -6
  87. package/dist/lib/task/control-recovery.js +140 -0
  88. package/dist/lib/task/create-service.js +1 -8
  89. package/dist/lib/task/create.js +62 -9
  90. package/dist/lib/task/decision-details.js +2 -46
  91. package/dist/lib/task/delegation-receipts.js +64 -3
  92. package/dist/lib/task/events.js +275 -22
  93. package/dist/lib/task/finalization-state.js +9 -0
  94. package/dist/lib/task/human-override.js +8 -14
  95. package/dist/lib/task/implementation-inputs.js +3 -3
  96. package/dist/lib/task/intermediate-cleanup.js +441 -0
  97. package/dist/lib/task/invalidation.js +18 -65
  98. package/dist/lib/task/ledger-intents.js +7 -22
  99. package/dist/lib/task/ledger.js +1 -3
  100. package/dist/lib/task/lifecycle-recovery.js +549 -0
  101. package/dist/lib/task/lifecycle.js +31 -4
  102. package/dist/lib/task/local-artifact-finalization.js +197 -197
  103. package/dist/lib/task/manual-validation-completion.js +26 -0
  104. package/dist/lib/task/manual-validation-evidence.js +130 -0
  105. package/dist/lib/task/manual-validation-receipt.js +105 -0
  106. package/dist/lib/task/manual-validation-shared.js +40 -0
  107. package/dist/lib/task/manual-validation-transaction.js +243 -0
  108. package/dist/lib/task/markdown.js +47 -0
  109. package/dist/lib/task/orchestration.js +66 -19
  110. package/dist/lib/task/pr-delivery-fact.js +12 -56
  111. package/dist/lib/task/qualification-audit.js +20 -34
  112. package/dist/lib/task/recovery-warning.js +19 -0
  113. package/dist/lib/task/review-artifacts.js +2 -18
  114. package/dist/lib/task/review-command.js +26 -0
  115. package/dist/lib/task/review-finalization.js +175 -216
  116. package/dist/lib/task/review-fingerprint.js +2 -2
  117. package/dist/lib/task/sections.js +34 -56
  118. package/dist/lib/task/short-id.js +15 -7
  119. package/dist/lib/task/task-execution-lock.js +1 -1
  120. package/dist/lib/task/verification-engine.js +119 -337
  121. package/dist/lib/task/verification.js +1 -1
  122. package/dist/lib/task/workflow-command.js +16 -0
  123. package/dist/lib/task/workflow-warning-intents.js +0 -16
  124. package/dist/lib/task/workflow-warnings.js +28 -2
  125. package/dist/lib/update.js +2 -0
  126. package/dist/lib/version.js +1 -11
  127. package/dist/lifecycle-build-manifest.json +3 -3
  128. package/lib/agent-clients/adapters/codex-lifecycle/capability-store.ts +223 -33
  129. package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.ts +10 -4
  130. package/lib/agent-clients/adapters/codex-lifecycle/store.ts +55 -3
  131. package/lib/fs/durable-write.ts +30 -0
  132. package/lib/fs/file-lock.ts +113 -0
  133. package/lib/host-control/audit.ts +22 -0
  134. package/lib/host-control/client.ts +172 -0
  135. package/lib/host-control/command.ts +60 -0
  136. package/lib/host-control/path.ts +167 -0
  137. package/lib/host-control/secure-fs.ts +135 -0
  138. package/lib/host-control/server.ts +180 -0
  139. package/lib/host-control/service.ts +133 -0
  140. package/lib/init.ts +15 -2
  141. package/lib/internal/cli-route-inventory.ts +4 -2
  142. package/lib/internal/codex-lifecycle.ts +6 -6
  143. package/lib/internal/demo-promotion.ts +354 -0
  144. package/lib/internal/demo-transcript.ts +419 -0
  145. package/lib/internal/host-control.ts +28 -0
  146. package/lib/internal/manual-validation.ts +315 -0
  147. package/lib/internal/platform-pr.ts +15 -4
  148. package/lib/internal/release-workflow.ts +74 -82
  149. package/lib/internal/sandbox-control.ts +162 -1
  150. package/lib/internal/task-artifact.ts +14 -217
  151. package/lib/internal/task-create.ts +5 -5
  152. package/lib/internal/task-event.ts +20 -11
  153. package/lib/internal/task-lifecycle.ts +4 -3
  154. package/lib/internal/task-operation-registry.ts +111 -4
  155. package/lib/internal/task-review.ts +9 -54
  156. package/lib/internal/task-validate.ts +26 -10
  157. package/lib/owned-path.ts +26 -0
  158. package/lib/platform/check-status.ts +9 -0
  159. package/lib/platform/comment-safety.ts +5 -0
  160. package/lib/platform/completion-backfill.ts +1 -1
  161. package/lib/platform/github-data.ts +322 -0
  162. package/lib/platform/github-provider.ts +15 -20
  163. package/lib/platform/issue-comments.ts +21 -14
  164. package/lib/platform/issues.ts +5 -246
  165. package/lib/platform/pr-change-report.ts +7 -5
  166. package/lib/platform/pr-checks.ts +13 -77
  167. package/lib/platform/pr-summary.ts +123 -35
  168. package/lib/platform/pull-requests.ts +6 -173
  169. package/lib/platform/snapshots.ts +53 -0
  170. package/lib/platform/task-identities.ts +3 -14
  171. package/lib/platform/verification-sync.ts +150 -156
  172. package/lib/pr-review/evidence-grading.ts +4 -9
  173. package/lib/release/workflow.ts +3 -3
  174. package/lib/sandbox/commands/create.ts +7 -6
  175. package/lib/sandbox/commands/rm.ts +21 -1895
  176. package/lib/sandbox/control/audit.ts +253 -0
  177. package/lib/sandbox/control/client.ts +109 -66
  178. package/lib/sandbox/control/executor.ts +134 -74
  179. package/lib/sandbox/control/finalization-response.ts +29 -0
  180. package/lib/sandbox/control/identity-sentinel.ts +137 -0
  181. package/lib/sandbox/control/lifecycle.ts +45 -3
  182. package/lib/sandbox/control/native-file-lock.ts +6 -107
  183. package/lib/sandbox/control/protocol.ts +80 -6
  184. package/lib/sandbox/control/server.ts +567 -132
  185. package/lib/sandbox/control/state.ts +86 -0
  186. package/lib/sandbox/control/task-workflow.ts +95 -0
  187. package/lib/sandbox/control/workflow-executor.ts +92 -0
  188. package/lib/sandbox/index.ts +1 -2
  189. package/lib/sandbox/recovery.ts +28 -5
  190. package/lib/sandbox/removal.ts +1142 -0
  191. package/lib/sandbox/task-cleanup.ts +209 -0
  192. package/lib/sandbox/workspace-identity.ts +19 -3
  193. package/lib/sandbox/workspace-view.ts +17 -15
  194. package/lib/task/activity-intent.ts +2 -2
  195. package/lib/task/activity-log.ts +27 -19
  196. package/lib/task/artifact-command.ts +85 -0
  197. package/lib/task/artifact-lifecycle.ts +31 -26
  198. package/lib/task/artifact-name.ts +55 -0
  199. package/lib/task/artifact-operations.ts +34 -310
  200. package/lib/task/artifact-receipts.ts +3 -14
  201. package/lib/task/artifact-recovery.ts +519 -0
  202. package/lib/task/artifact-repair-intent.ts +162 -33
  203. package/lib/task/artifact-schema.ts +5 -34
  204. package/lib/task/capabilities.ts +13 -22
  205. package/lib/task/codex-orchestration.ts +85 -24
  206. package/lib/task/commit-identity.ts +31 -0
  207. package/lib/task/commit-operation.ts +37 -20
  208. package/lib/task/control-authority.ts +540 -10
  209. package/lib/task/control-recovery.ts +186 -0
  210. package/lib/task/create-service.ts +1 -10
  211. package/lib/task/create.ts +63 -8
  212. package/lib/task/decision-details.ts +3 -71
  213. package/lib/task/delegation-receipts.ts +73 -2
  214. package/lib/task/events.ts +309 -24
  215. package/lib/task/finalization-state.ts +11 -0
  216. package/lib/task/human-override.ts +8 -16
  217. package/lib/task/implementation-inputs.ts +3 -3
  218. package/lib/task/intermediate-cleanup.ts +493 -0
  219. package/lib/task/invalidation.ts +18 -62
  220. package/lib/task/ledger-intents.ts +7 -24
  221. package/lib/task/ledger.ts +1 -3
  222. package/lib/task/lifecycle-recovery.ts +691 -0
  223. package/lib/task/lifecycle.ts +30 -5
  224. package/lib/task/local-artifact-finalization.ts +244 -234
  225. package/lib/task/manual-validation-completion.ts +40 -0
  226. package/lib/task/manual-validation-evidence.ts +209 -0
  227. package/lib/task/manual-validation-receipt.ts +156 -0
  228. package/lib/task/manual-validation-shared.ts +50 -0
  229. package/lib/task/manual-validation-transaction.ts +304 -0
  230. package/lib/task/markdown.ts +70 -0
  231. package/lib/task/orchestration.ts +84 -19
  232. package/lib/task/pr-delivery-fact.ts +15 -62
  233. package/lib/task/qualification-audit.ts +22 -33
  234. package/lib/task/recovery-warning.ts +21 -0
  235. package/lib/task/review-artifacts.ts +1 -20
  236. package/lib/task/review-command.ts +24 -0
  237. package/lib/task/review-finalization.ts +183 -292
  238. package/lib/task/review-fingerprint.ts +2 -2
  239. package/lib/task/sections.ts +33 -54
  240. package/lib/task/short-id.ts +27 -5
  241. package/lib/task/task-execution-lock.ts +1 -1
  242. package/lib/task/verification-engine.ts +113 -383
  243. package/lib/task/verification-types.ts +15 -0
  244. package/lib/task/verification.ts +1 -1
  245. package/lib/task/workflow-command.ts +17 -0
  246. package/lib/task/workflow-warning-intents.ts +0 -13
  247. package/lib/task/workflow-warnings.ts +21 -2
  248. package/lib/update.ts +6 -0
  249. package/lib/version.ts +1 -15
  250. package/package.json +6 -6
  251. package/templates/.agents/hooks/lifecycle-delegation.js +6 -6
  252. package/templates/.agents/rules/create-issue.en.md +1 -1
  253. package/templates/.agents/rules/create-issue.zh-CN.md +1 -1
  254. package/templates/.agents/rules/issue-sync.en.md +4 -2
  255. package/templates/.agents/rules/issue-sync.zh-CN.md +4 -3
  256. package/templates/.agents/rules/local-artifact-repair.en.md +46 -39
  257. package/templates/.agents/rules/local-artifact-repair.zh-CN.md +43 -36
  258. package/templates/.agents/rules/testing-discipline.en.md +2 -0
  259. package/templates/.agents/rules/testing-discipline.zh-CN.md +2 -0
  260. package/templates/.agents/scripts/package.json +3 -0
  261. package/templates/.agents/skills/analyze-task/SKILL.en.md +6 -6
  262. package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +6 -6
  263. package/templates/.agents/skills/analyze-task/config/verify.en.json +1 -1
  264. package/templates/.agents/skills/analyze-task/config/verify.zh-CN.json +1 -1
  265. package/templates/.agents/skills/block-task/SKILL.en.md +2 -2
  266. package/templates/.agents/skills/block-task/SKILL.zh-CN.md +2 -2
  267. package/templates/.agents/skills/block-task/config/verify.json +1 -1
  268. package/templates/.agents/skills/cancel-task/SKILL.en.md +3 -3
  269. package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +3 -3
  270. package/templates/.agents/skills/cancel-task/config/verify.json +1 -1
  271. package/templates/.agents/skills/code-task/SKILL.en.md +7 -7
  272. package/templates/.agents/skills/code-task/SKILL.zh-CN.md +7 -7
  273. package/templates/.agents/skills/code-task/config/verify.en.json +1 -1
  274. package/templates/.agents/skills/code-task/config/verify.zh-CN.json +1 -1
  275. package/templates/.agents/skills/commit/reference/issue-metadata-sync.en.md +1 -1
  276. package/templates/.agents/skills/commit/reference/issue-metadata-sync.zh-CN.md +1 -1
  277. package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +33 -21
  278. package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +33 -21
  279. package/templates/.agents/skills/complete-manual-validation/config/verify.en.json +1 -1
  280. package/templates/.agents/skills/complete-manual-validation/config/verify.zh-CN.json +1 -1
  281. package/templates/.agents/skills/complete-manual-validation/reference/report-template.en.md +2 -0
  282. package/templates/.agents/skills/complete-manual-validation/reference/report-template.zh-CN.md +2 -0
  283. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.en.md +16 -7
  284. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.zh-CN.md +16 -7
  285. package/templates/.agents/skills/complete-task/SKILL.en.md +3 -3
  286. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +3 -3
  287. package/templates/.agents/skills/complete-task/config/verify.en.json +2 -2
  288. package/templates/.agents/skills/complete-task/config/verify.zh-CN.json +2 -2
  289. package/templates/.agents/skills/complete-task/reference/external-delivery.en.md +1 -1
  290. package/templates/.agents/skills/complete-task/reference/external-delivery.zh-CN.md +1 -1
  291. package/templates/.agents/skills/create-pr/SKILL.en.md +2 -2
  292. package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +2 -2
  293. package/templates/.agents/skills/create-pr/config/verify.json +1 -1
  294. package/templates/.agents/skills/create-pr/reference/pr-body-template.en.md +2 -2
  295. package/templates/.agents/skills/create-pr/reference/pr-body-template.zh-CN.md +2 -2
  296. package/templates/.agents/skills/create-task/SKILL.en.md +5 -5
  297. package/templates/.agents/skills/create-task/SKILL.zh-CN.md +5 -5
  298. package/templates/.agents/skills/create-task/config/verify.json +1 -1
  299. package/templates/.agents/skills/import-issue/SKILL.en.md +8 -8
  300. package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +8 -8
  301. package/templates/.agents/skills/import-issue/config/verify.json +2 -2
  302. package/templates/.agents/skills/plan-task/SKILL.en.md +4 -4
  303. package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +4 -4
  304. package/templates/.agents/skills/plan-task/config/verify.en.json +1 -1
  305. package/templates/.agents/skills/plan-task/config/verify.zh-CN.json +1 -1
  306. package/templates/.agents/skills/release/SKILL.en.md +7 -3
  307. package/templates/.agents/skills/release/SKILL.zh-CN.md +7 -3
  308. package/templates/.agents/skills/restore-task/SKILL.en.md +2 -2
  309. package/templates/.agents/skills/restore-task/SKILL.zh-CN.md +2 -2
  310. package/templates/.agents/skills/restore-task/config/verify.json +1 -1
  311. package/templates/.agents/skills/review-analysis/SKILL.en.md +2 -2
  312. package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +2 -2
  313. package/templates/.agents/skills/review-analysis/config/verify.en.json +1 -1
  314. package/templates/.agents/skills/review-analysis/config/verify.zh-CN.json +1 -1
  315. package/templates/.agents/skills/review-analysis/reference/report-template.en.md +7 -7
  316. package/templates/.agents/skills/review-analysis/reference/report-template.zh-CN.md +6 -6
  317. package/templates/.agents/skills/review-code/SKILL.en.md +2 -2
  318. package/templates/.agents/skills/review-code/SKILL.zh-CN.md +2 -2
  319. package/templates/.agents/skills/review-code/config/verify.en.json +1 -1
  320. package/templates/.agents/skills/review-code/config/verify.zh-CN.json +1 -1
  321. package/templates/.agents/skills/review-code/reference/report-template.en.md +12 -12
  322. package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +12 -12
  323. package/templates/.agents/skills/review-plan/SKILL.en.md +2 -2
  324. package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +2 -2
  325. package/templates/.agents/skills/review-plan/config/verify.en.json +1 -1
  326. package/templates/.agents/skills/review-plan/config/verify.zh-CN.json +1 -1
  327. package/templates/.agents/skills/review-plan/reference/report-template.en.md +7 -7
  328. package/templates/.agents/skills/review-plan/reference/report-template.zh-CN.md +7 -7
  329. package/templates/.agents/skills/review-pr/config/verify.en.json +1 -1
  330. package/templates/.agents/skills/review-pr/config/verify.zh-CN.json +1 -1
  331. package/templates/.agents/skills/review-pr/reference/evidence-grading.en.md +3 -3
  332. package/templates/.agents/skills/review-pr/reference/evidence-grading.zh-CN.md +3 -3
  333. package/templates/.agents/skills/run-manual-validation/SKILL.en.md +20 -3
  334. package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +20 -3
  335. package/templates/.agents/skills/run-manual-validation/config/verify.en.json +1 -1
  336. package/templates/.agents/skills/run-manual-validation/config/verify.zh-CN.json +1 -1
  337. package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.en.md +4 -1
  338. package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.zh-CN.md +4 -1
  339. package/templates/.agents/skills/run-manual-validation/reference/report-template.en.md +2 -0
  340. package/templates/.agents/skills/run-manual-validation/reference/report-template.zh-CN.md +2 -0
  341. package/templates/.agents/skills/run-task/SKILL.en.md +1 -1
  342. package/templates/.agents/skills/run-task/SKILL.zh-CN.md +1 -1
  343. package/templates/.agents/skills/test/SKILL.en.md +17 -6
  344. package/templates/.agents/skills/test/SKILL.zh-CN.md +17 -6
  345. package/templates/.agents/templates/task.en.md +1 -1
  346. package/templates/.agents/templates/task.zh-CN.md +1 -1
  347. package/templates/.claude/commands/import-issue.en.md +1 -1
  348. package/templates/.claude/commands/import-issue.zh-CN.md +1 -1
  349. package/templates/.claude/commands/restore-task.en.md +1 -1
  350. package/templates/.claude/commands/restore-task.zh-CN.md +1 -1
  351. package/templates/.claude/commands/run-manual-validation.en.md +1 -1
  352. package/templates/.claude/commands/run-manual-validation.zh-CN.md +1 -1
  353. package/dist/lib/platform/adapters.js +0 -56
  354. package/lib/platform/adapters.ts +0 -197
@@ -3,8 +3,9 @@ import path from 'node:path';
3
3
  import { createHash, randomUUID } from 'node:crypto';
4
4
  import { execFileSync } from 'node:child_process';
5
5
  import { escapeHtmlText } from './comment-safety.js';
6
+ import { isResourceIdentity } from './resource-identity.js';
6
7
  const REPORT_FILE_NAME = 'pr-change-report.json';
7
- const REPORT_VERSION = 1;
8
+ const REPORT_VERSION = 2;
8
9
  const CANONICAL_REPORT_PLACEHOLDER = '<!-- canonical-pr-change-report -->';
9
10
  const CANONICAL_REPORT_HEADING = '### PR 代码增减';
10
11
  const PRECHECK_IDS = [
@@ -211,8 +212,8 @@ function validatePrChangeReport(value) {
211
212
  }
212
213
  const identity = value.identity;
213
214
  const identityRepository = identity.repository;
214
- const identityNumber = identity.number;
215
- if (!isNonEmptyString(identityRepository) || typeof identityNumber !== 'number' || !Number.isSafeInteger(identityNumber) || identityNumber <= 0)
215
+ const identityResource = identity.resource;
216
+ if (!isNonEmptyString(identityRepository) || !isResourceIdentity(identityResource))
216
217
  return invalid('change report identity is invalid');
217
218
  const parseIdentityPart = (part, name) => {
218
219
  if (!isRecord(part) || !isNonEmptyString(part.repository) || !isNonEmptyString(part.ref) || !isSha(part.sha))
@@ -259,7 +260,7 @@ function validatePrChangeReport(value) {
259
260
  version: REPORT_VERSION,
260
261
  identity: {
261
262
  repository: identityRepository,
262
- number: identityNumber,
263
+ resource: identityResource,
263
264
  base: base.value,
264
265
  head: head.value
265
266
  },
@@ -1,6 +1,7 @@
1
1
  import fs from 'node:fs';
2
2
  import { parseTypedTaskFrontmatter } from '../task/frontmatter.js';
3
3
  import { resolveTaskRef } from '../task/resolve-ref.js';
4
+ import { checkStatusBucket } from './check-status.js';
4
5
  import { resolvePlatformProviderContext } from './context.js';
5
6
  import { inspectPlatformPullRequest } from './pull-requests.js';
6
7
  import { platformResult } from './types.js';
@@ -26,20 +27,6 @@ function classifyPullRequestReadiness(input) {
26
27
  return { state: 'pending', headSha: input.headSha };
27
28
  return { state: 'ready', headSha: input.headSha };
28
29
  }
29
- function parseRunJobIdentity(detailsUrl) {
30
- try {
31
- const url = new URL(detailsUrl);
32
- if (url.hostname !== 'github.com')
33
- return null;
34
- const match = url.pathname.match(/\/actions\/runs\/(\d+)(?:\/job\/(\d+))?/);
35
- if (!match)
36
- return null;
37
- return { runId: Number(match[1]), jobId: match[2] ? Number(match[2]) : null };
38
- }
39
- catch {
40
- return null;
41
- }
42
- }
43
30
  function resolveRunCandidate(candidates, headSha, checkName) {
44
31
  const matches = candidates.filter((candidate) => candidate.headSha === headSha && candidate.name === checkName);
45
32
  if (matches.length === 1)
@@ -54,42 +41,6 @@ function checksResult(status, overrides = {}) {
54
41
  ...overrides
55
42
  };
56
43
  }
57
- function normalizeBucket(value) {
58
- const raw = String(value.bucket || value.status || value.conclusion || value.state || '').toLowerCase();
59
- if (['pass', 'success', 'successful', 'neutral'].includes(raw))
60
- return 'pass';
61
- if (['fail', 'failure', 'failed', 'error', 'timed_out', 'action_required'].includes(raw))
62
- return 'fail';
63
- if (['cancel', 'cancelled', 'canceled', 'skipped', 'stale'].includes(raw))
64
- return 'cancel';
65
- return 'pending';
66
- }
67
- function normalizeChecks(value) {
68
- if (!Array.isArray(value))
69
- return [];
70
- return value.flatMap((raw) => {
71
- const item = raw;
72
- const name = String(item.name || item.context || '');
73
- return name ? [{
74
- name,
75
- bucket: normalizeBucket(item),
76
- workflow: item.workflow ? String(item.workflow) : null,
77
- conclusion: item.conclusion ? String(item.conclusion) : item.state ? String(item.state) : null,
78
- detailsUrl: item.link ? String(item.link) : item.detailsUrl ? String(item.detailsUrl) : null,
79
- startedAt: item.startedAt ? String(item.startedAt) : null,
80
- completedAt: item.completedAt ? String(item.completedAt) : null
81
- }] : [];
82
- });
83
- }
84
- function inspectGitHubRequiredChecks(client, repository, number, cwd) {
85
- const inspected = client.json([
86
- 'pr', 'checks', String(number), '--repo', repository,
87
- '--json', 'name,state,bucket,link,workflow,startedAt,completedAt'
88
- ], { cwd });
89
- return inspected.ok
90
- ? { ok: true, value: normalizeChecks(inspected.value) }
91
- : { ok: false, error: inspected.error };
92
- }
93
44
  async function resolvedTask(taskRef, options) {
94
45
  const resolved = resolveTaskRef(taskRef, options.cwd ? { repoRoot: options.cwd } : {});
95
46
  if (!resolved.ok)
@@ -117,10 +68,9 @@ async function inspectRequiredChecks(taskRef, options = {}) {
117
68
  const base = await resolvedTask(taskRef, options);
118
69
  if (!base.ok)
119
70
  return base.output;
120
- return inspectChecksForResolvedTask(base, options);
71
+ return inspectChecksForResolvedTask(base);
121
72
  }
122
- async function inspectChecksForResolvedTask(base, options) {
123
- const repository = base.context.platform.repository;
73
+ async function inspectChecksForResolvedTask(base) {
124
74
  {
125
75
  const inspected = base.provider.checks?.inspectRequired
126
76
  ? await base.provider.checks.inspectRequired({
@@ -137,7 +87,13 @@ async function inspectChecksForResolvedTask(base, options) {
137
87
  pullRequest: base.pullRequest,
138
88
  error: providerError(inspected.error, 'PLATFORM_PROVIDER_OPERATION_FAILED')
139
89
  });
140
- const required = normalizeChecks(inspected.value);
90
+ const required = inspected.value.map((check) => ({
91
+ name: check.name,
92
+ bucket: checkStatusBucket(check.status),
93
+ conclusion: check.conclusion || null,
94
+ detailsUrl: check.detailsUrl || null,
95
+ workflow: null, startedAt: null, completedAt: null
96
+ }));
141
97
  const classified = classifyRequiredChecks(required);
142
98
  const status = classified.state === 'passed' || classified.state === 'no-required'
143
99
  ? 'no-op'
@@ -157,7 +113,7 @@ async function inspectPullRequestReadiness(taskRef, options = {}) {
157
113
  const base = await resolvedTask(taskRef, options);
158
114
  if (!base.ok)
159
115
  return base.output;
160
- const checked = await inspectChecksForResolvedTask(base, options);
116
+ const checked = await inspectChecksForResolvedTask(base);
161
117
  const classifiedCodes = new Set(['REQUIRED_CHECKS_PENDING', 'REQUIRED_CHECKS_FAILED', 'REQUIRED_CHECKS_CANCELLED']);
162
118
  if (checked.error && !classifiedCodes.has(checked.error.code))
163
119
  return checked;
@@ -242,15 +198,6 @@ async function resolvePlatformCheckRun(taskRef, options) {
242
198
  });
243
199
  }
244
200
  }
245
- function fetchCheckLogText(client, args, cwd) {
246
- if (!client.text)
247
- return { ok: false, error: { code: 'PLATFORM_CLIENT_TEXT_UNAVAILABLE', message: 'Platform client does not support text responses', retryable: false } };
248
- const fetched = client.text(args, { cwd });
249
- if (fetched.ok || args[0] !== 'api' || !/response contains terminal escape sequences/i.test(fetched.error.message)) {
250
- return fetched;
251
- }
252
- return client.text([...args, '--allow-escape-sequences'], { cwd });
253
- }
254
201
  async function fetchPlatformCheckLogs(taskRef, options) {
255
202
  const base = await resolvedTask(taskRef, options);
256
203
  if (!base.ok)
@@ -281,5 +228,5 @@ async function fetchPlatformCheckLogs(taskRef, options) {
281
228
  });
282
229
  }
283
230
  }
284
- export { classifyPullRequestReadiness, classifyRequiredChecks, fetchCheckLogText, fetchPlatformCheckLogs, inspectGitHubRequiredChecks, inspectRequiredChecks, inspectPullRequestReadiness, parseRunJobIdentity, resolvePlatformCheckRun, resolveRunCandidate, watchPullRequestReadiness };
231
+ export { classifyPullRequestReadiness, classifyRequiredChecks, fetchPlatformCheckLogs, inspectRequiredChecks, inspectPullRequestReadiness, resolvePlatformCheckRun, resolveRunCandidate, watchPullRequestReadiness };
285
232
  //# sourceMappingURL=pr-checks.js.map
@@ -11,7 +11,10 @@ import { normalizeCommentContent } from './issue-comments.js';
11
11
  import { CONTROL_MARKER_PATTERN, escapeHtmlText, sanitizeMarkdownDocument, splitDocumentPlaceholder } from './comment-safety.js';
12
12
  import { platformResult } from './types.js';
13
13
  import { providerError, providerOperationContext, providerStatus, providerResourceToken, resourceIdentityNumber, unsupportedProviderOperation } from './provider-bridge.js';
14
+ import { resourceIdentityEquals } from './resource-identity.js';
14
15
  import { buildPrChangeReport, CANONICAL_REPORT_PLACEHOLDER, readPrChangeReport, replaceCanonicalReportPlaceholder, runMechanicalChangeReport, taskIntentDigest, validateMechanicalReport, validatePrecheckCandidate, writePrChangeReportAtomic } from './pr-change-report.js';
16
+ import { manualValidationFinalSummaryProjectionMatches } from '../task/manual-validation-receipt.js';
17
+ import { readManualValidationCompletion } from '../task/manual-validation-completion.js';
15
18
  function summaryMarker(taskId) {
16
19
  return `<!-- sync-pr:${taskId}:summary -->`;
17
20
  }
@@ -46,7 +49,8 @@ function readBoundTaskSnapshot(taskMdPath) {
46
49
  const frontmatter = parseTypedTaskFrontmatter(taskContent);
47
50
  const fact = readPrDeliveryFact(frontmatter);
48
51
  const boundFact = fact.status === 'valid' && fact.fact.state === 'bound' ? fact.fact : null;
49
- return { taskContent, boundFact };
52
+ const factError = fact.status === 'invalid' ? { code: fact.error.code, message: fact.error.message } : null;
53
+ return { taskContent, boundFact, factError };
50
54
  }
51
55
  function boundPrNumberForWarning(taskMdPath) {
52
56
  try {
@@ -115,7 +119,7 @@ function isHardSummaryFailure(error) {
115
119
  function inspectionError(error) {
116
120
  return error.retryable ? 'blocked' : 'failed';
117
121
  }
118
- async function inspectBoundPullRequest(context, repoRoot, prNumber, loadedContext) {
122
+ async function inspectBoundPullRequest(context, repoRoot, prIdentity, loadedContext) {
119
123
  if (!context.platform.repository) {
120
124
  return { ok: false, status: context.status, error: context.error };
121
125
  }
@@ -124,16 +128,15 @@ async function inspectBoundPullRequest(context, repoRoot, prNumber, loadedContex
124
128
  : await resolvePlatformProviderContext({ cwd: repoRoot });
125
129
  if (!loaded.ok)
126
130
  return { ok: false, status: loaded.context.status, error: loaded.context.error };
127
- const target = providerResourceToken(loaded.value.provider, 'pull-request', String(prNumber));
128
131
  const inspected = loaded.value.provider.changeRequests?.inspect
129
- ? await loaded.value.provider.changeRequests.inspect({ context: providerOperationContext(loaded.value), target })
132
+ ? await loaded.value.provider.changeRequests.inspect({ context: providerOperationContext(loaded.value), target: prIdentity })
130
133
  : unsupportedProviderOperation(loaded.value.provider, 'changeRequests.inspect');
131
134
  if (!inspected.ok) {
132
135
  const error = providerError(inspected.error, 'PLATFORM_PROVIDER_OPERATION_FAILED');
133
136
  return { ok: false, status: inspectionError(error), error };
134
137
  }
135
138
  const remote = inspected.value;
136
- const number = remote.number ?? resourceIdentityNumber(remote.identity) ?? prNumber;
139
+ const number = remote.number ?? resourceIdentityNumber(remote.identity) ?? resourceIdentityNumber(prIdentity) ?? 0;
137
140
  const head = remote.head || { repository: context.platform.repository, ref: '', sha: remote.headSha || '' };
138
141
  const base = remote.base || { repository: context.platform.repository, ref: '', sha: remote.baseSha || '' };
139
142
  return {
@@ -141,6 +144,7 @@ async function inspectBoundPullRequest(context, repoRoot, prNumber, loadedContex
141
144
  value: {
142
145
  repository: context.platform.repository,
143
146
  number,
147
+ identity: remote.identity ?? prIdentity,
144
148
  nodeId: remote.id,
145
149
  url: remote.displayUrl || '',
146
150
  state: remote.state === 'closed' ? 'closed' : 'open',
@@ -161,20 +165,22 @@ async function inspectBoundPullRequest(context, repoRoot, prNumber, loadedContex
161
165
  function sameIdentity(left, right) {
162
166
  return JSON.stringify({
163
167
  repository: left.repository,
164
- number: left.number,
168
+ identity: left.identity ?? null,
165
169
  base: left.base,
166
170
  head: left.head
167
171
  }) === JSON.stringify({
168
172
  repository: right.repository,
169
- number: right.number,
173
+ identity: right.identity ?? null,
170
174
  base: right.base,
171
175
  head: right.head
172
176
  });
173
177
  }
174
178
  function identityFromSnapshot(snapshot) {
179
+ if (!snapshot.identity)
180
+ throw new Error('pull-request snapshot has no canonical identity');
175
181
  return {
176
182
  repository: snapshot.repository,
177
- number: snapshot.number,
183
+ resource: snapshot.identity,
178
184
  base: { ...snapshot.base },
179
185
  head: { ...snapshot.head }
180
186
  };
@@ -203,7 +209,7 @@ function currentReportCheck(report, snapshot, taskContent, repoRoot) {
203
209
  if (!digest.ok)
204
210
  return { ok: false, error: platformError(digest.error) };
205
211
  const reportIdentity = report.identity;
206
- if (reportIdentity.repository !== snapshot.repository || reportIdentity.number !== snapshot.number ||
212
+ if (reportIdentity.repository !== snapshot.repository || !resourceIdentityEquals(reportIdentity.resource, snapshot.identity) ||
207
213
  JSON.stringify(reportIdentity.base) !== JSON.stringify(snapshot.base) || JSON.stringify(reportIdentity.head) !== JSON.stringify(snapshot.head))
208
214
  return {
209
215
  ok: false,
@@ -261,14 +267,23 @@ async function summaryContext(taskRef, options = {}) {
261
267
  };
262
268
  const taskContent = fs.readFileSync(resolved.taskMdPath, 'utf8');
263
269
  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
270
  const reportPath = taskReportPath(resolved.taskDir);
271
+ const fact = readPrDeliveryFact(frontmatter, options.runtimeVersion);
272
+ if (fact.status === 'invalid')
273
+ return {
274
+ ...platformResult('failed', { error: { code: fact.error.code, message: fact.error.message, retryable: false } }),
275
+ task: { id: resolved.taskId, prNumber: null },
276
+ pullRequest: null,
277
+ changeReport: { status: 'invalid', path: reportPath, taskIntentSha256: null, reason: fact.error.message },
278
+ artifacts: []
279
+ };
280
+ const prIdentity = fact.status === 'valid' && fact.fact.state === 'bound' ? fact.fact.identity.resource : null;
281
+ const prNumber = resourceIdentityNumber(prIdentity);
267
282
  const loaded = await resolvePlatformProviderContext({ cwd: resolved.repoRoot, client: options.client });
268
283
  const context = loaded.ok ? loaded.value.context : loaded.context;
269
284
  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);
285
+ if (loaded.ok && prIdentity && context.platform.repository && ['no-op', 'degraded'].includes(context.status)) {
286
+ const inspected = await inspectBoundPullRequest(context, resolved.repoRoot, prIdentity, loaded.value);
272
287
  if (inspected.ok)
273
288
  pullRequest = inspected.value;
274
289
  }
@@ -286,10 +301,45 @@ async function summaryContext(taskRef, options = {}) {
286
301
  artifacts: canonicalArtifacts(resolved.taskDir)
287
302
  };
288
303
  }
304
+ async function summaryCommentState(taskRef, options = {}) {
305
+ const context = await summaryContext(taskRef, options);
306
+ const prIdentity = context.pullRequest?.identity;
307
+ if (!prIdentity)
308
+ return { ...context, comment: null };
309
+ const loaded = await resolvePlatformProviderContext({ cwd: options.cwd, client: options.client });
310
+ if (!loaded.ok || !loaded.value.provider.comments?.list)
311
+ return { ...context, comment: null };
312
+ const listed = await loaded.value.provider.comments.list({
313
+ context: providerOperationContext(loaded.value),
314
+ parent: prIdentity
315
+ });
316
+ if (!listed.ok)
317
+ return { ...context, comment: null };
318
+ const matches = listed.value.filter((comment) => comment.body.includes(summaryMarker(context.task.id)));
319
+ return {
320
+ ...context,
321
+ comment: matches.length === 1 ? { id: /^\d+$/u.test(matches[0].id) ? Number(matches[0].id) : matches[0].id, body: matches[0].body } : null
322
+ };
323
+ }
289
324
  async function reportWrite(taskRef, options) {
290
325
  const resolved = resolveTaskRef(taskRef, options.cwd ? { repoRoot: options.cwd } : {});
291
326
  if (!resolved.ok)
292
327
  return { ...platformResult('failed', { error: { code: resolved.code, message: resolved.message, retryable: false } }), report: null };
328
+ let preflight;
329
+ try {
330
+ preflight = readBoundTaskSnapshot(resolved.taskMdPath);
331
+ }
332
+ catch (error) {
333
+ return {
334
+ ...platformResult('failed', { error: { code: 'PR_CHANGE_REPORT_FAILED', message: error instanceof Error ? error.message : String(error), retryable: false } }),
335
+ report: null
336
+ };
337
+ }
338
+ if (preflight.factError)
339
+ return {
340
+ ...platformResult('failed', { error: { code: preflight.factError.code, message: preflight.factError.message, retryable: false } }),
341
+ report: null
342
+ };
293
343
  const loaded = await resolvePlatformProviderContext({ cwd: resolved.repoRoot, client: options.client });
294
344
  const context = loaded.ok ? loaded.value.context : loaded.context;
295
345
  if (!loaded.ok || !context.platform.repository || !['no-op', 'degraded'].includes(context.status))
@@ -297,20 +347,21 @@ async function reportWrite(taskRef, options) {
297
347
  let knownPrNumber = null;
298
348
  try {
299
349
  return await withTaskExecutionLock(resolved.repoRoot, resolved.taskId, options.agent, async () => {
300
- const { taskContent, boundFact } = readBoundTaskSnapshot(resolved.taskMdPath);
301
- if (!boundFact)
350
+ const { taskContent, boundFact, factError } = readBoundTaskSnapshot(resolved.taskMdPath);
351
+ if (factError)
302
352
  return {
303
- ...basePlatformResult('failed', context, resolved.taskId, null, { code: 'PR_NOT_LINKED', message: 'Task has no verified bound pull request', retryable: false }),
353
+ ...basePlatformResult('failed', context, resolved.taskId, null, { code: factError.code, message: factError.message, retryable: false }),
304
354
  report: null
305
355
  };
306
- const boundPrNumber = resourceIdentityNumber(boundFact.identity.resource);
307
- if (!boundPrNumber)
356
+ if (!boundFact)
308
357
  return {
309
- ...basePlatformResult('failed', context, resolved.taskId, null, { code: 'PR_NUMBER_INVALID', message: 'Bound pull request has no numeric identifier', retryable: false }),
358
+ ...basePlatformResult('failed', context, resolved.taskId, null, { code: 'PR_NOT_LINKED', message: 'Task has no verified bound pull request', retryable: false }),
310
359
  report: null
311
360
  };
361
+ const boundPrIdentity = boundFact.identity.resource;
362
+ const boundPrNumber = resourceIdentityNumber(boundPrIdentity);
312
363
  knownPrNumber = boundPrNumber;
313
- const initial = await inspectBoundPullRequest(context, resolved.repoRoot, boundPrNumber, loaded.value);
364
+ const initial = await inspectBoundPullRequest(context, resolved.repoRoot, boundPrIdentity, loaded.value);
314
365
  if (!initial.ok)
315
366
  return { ...basePlatformResult(initial.status, context, resolved.taskId, boundPrNumber, initial.error), report: null };
316
367
  const digest = taskIntentDigest(taskContent);
@@ -343,7 +394,7 @@ async function reportWrite(taskRef, options) {
343
394
  const built = buildPrChangeReport(identityFromSnapshot(initial.value), digest.value.sha256, mechanical.value, candidate.value);
344
395
  if (!built.ok)
345
396
  return { ...basePlatformResult('failed', context, resolved.taskId, boundPrNumber, platformError(built.error)), report: null };
346
- const final = await inspectBoundPullRequest(context, resolved.repoRoot, boundPrNumber, loaded.value);
397
+ const final = await inspectBoundPullRequest(context, resolved.repoRoot, boundPrIdentity, loaded.value);
347
398
  if (!final.ok)
348
399
  return { ...basePlatformResult(final.status, context, resolved.taskId, boundPrNumber, final.error), report: null };
349
400
  if (!sameIdentity(initial.value, final.value))
@@ -444,8 +495,13 @@ async function syncPullRequestSummary(taskRef, options) {
444
495
  return preserveFailure(context);
445
496
  }
446
497
  try {
447
- return await withTaskExecutionLock(resolved.repoRoot, resolved.taskId, options.agent, async () => {
448
- const { taskContent, boundFact } = readBoundTaskSnapshot(resolved.taskMdPath);
498
+ const execute = async () => {
499
+ const { taskContent, boundFact, factError } = readBoundTaskSnapshot(resolved.taskMdPath);
500
+ if (factError)
501
+ return {
502
+ ...platformResult('failed', { resource: { kind: 'pull-request', number: null }, error: { code: factError.code, message: factError.message, retryable: false } }),
503
+ result: null, warnings: []
504
+ };
449
505
  if (!boundFact)
450
506
  return {
451
507
  ...platformResult('failed', { resource: { kind: 'pull-request', number: null }, error: { code: 'PR_NOT_LINKED', message: 'Task has no verified bound pull request', retryable: false } }),
@@ -453,16 +509,41 @@ async function syncPullRequestSummary(taskRef, options) {
453
509
  };
454
510
  const prIdentity = boundFact.identity.resource;
455
511
  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
512
  knownPrNumber = boundPrNumber;
462
513
  const prNumber = boundPrNumber;
463
- const initial = await inspectBoundPullRequest(context, resolved.repoRoot, boundPrNumber, loaded.value);
514
+ const initial = await inspectBoundPullRequest(context, resolved.repoRoot, prIdentity, loaded.value);
464
515
  if (!initial.ok)
465
516
  return fail(initial.status, context, initial.error);
517
+ const manual = options.manualValidation;
518
+ const hasFinalManualValidation = /###\s+✅\s+(?:Manual Validation Passed|人工验证已通过)/u.test(options.body);
519
+ if (hasFinalManualValidation && (!manual || manual.phase !== 'final'))
520
+ return fail('failed', context, { code: 'MANUAL_VALIDATION_TRANSACTION_REQUIRED', message: 'final manual-validation summary requires the transaction coordinator', retryable: false }, prNumber);
521
+ if (manual && (manual.phase === 'pending' ? hasFinalManualValidation : !hasFinalManualValidation))
522
+ return fail('failed', context, { code: 'MANUAL_VALIDATION_SUMMARY_PHASE_INVALID', message: 'manual-validation summary phase does not match the requested writer phase', retryable: false }, prNumber);
523
+ if (manual) {
524
+ if (prNumber === null)
525
+ return fail('failed', context, { code: 'MANUAL_VALIDATION_PR_NUMBER_REQUIRED', message: 'manual-validation summary requires a numeric pull-request identity', retryable: false }, prNumber);
526
+ if (manual.phase === 'final' && (!manual.transactionId || !manual.receiptDigest || !manual.prHeadSha || manual.prHeadSha !== initial.value.head.sha))
527
+ return fail('failed', context, { code: 'MANUAL_VALIDATION_TRANSACTION_REQUIRED', message: 'final manual-validation summary requires transaction, receipt, and current head identity', retryable: false }, prNumber);
528
+ if (manual.phase === 'final') {
529
+ if (manual.authority !== 'coordinator')
530
+ return fail('failed', context, { code: 'MANUAL_VALIDATION_TRANSACTION_REQUIRED', message: 'final manual-validation summary requires coordinator authority', retryable: false }, prNumber);
531
+ const completion = readManualValidationCompletion(resolved.taskDir, {
532
+ transactionId: manual.transactionId,
533
+ taskId: resolved.taskId,
534
+ prNumber,
535
+ prHeadSha: initial.value.head.sha,
536
+ receiptDigest: manual.receiptDigest
537
+ });
538
+ if (!completion.ok)
539
+ return fail('failed', context, platformError(completion.error), prNumber);
540
+ const { receipt, transaction } = completion.value;
541
+ if (transaction.phase !== 'final-promotion-in-progress' || !transaction.eventAppended)
542
+ return fail('failed', context, { code: 'MANUAL_VALIDATION_TRANSACTION_PHASE_INVALID', message: 'final summary requires a receipt-backed promotion intent', retryable: false }, prNumber);
543
+ if (!manualValidationFinalSummaryProjectionMatches(options.body, receipt))
544
+ return fail('failed', context, { code: 'MANUAL_VALIDATION_SUMMARY_PROJECTION_INVALID', message: 'final summary body does not match the canonical receipt projection', retryable: false }, prNumber);
545
+ }
546
+ }
466
547
  const expectedReportPath = taskReportPath(resolved.taskDir);
467
548
  const suppliedReportPath = path.resolve(resolved.repoRoot, options.changeReportFile);
468
549
  if (suppliedReportPath !== expectedReportPath)
@@ -521,7 +602,7 @@ async function syncPullRequestSummary(taskRef, options) {
521
602
  ? listed.value.find((comment) => comment.id === reconciliation.commentId)?.body ?? null
522
603
  : null;
523
604
  const info = { precheckVerdict: report.value.precheck.verdict, nextAction: report.value.precheck.route };
524
- const beforeWrite = await inspectBoundPullRequest(context, resolved.repoRoot, boundPrNumber, loaded.value);
605
+ const beforeWrite = await inspectBoundPullRequest(context, resolved.repoRoot, prIdentity, loaded.value);
525
606
  if (!beforeWrite.ok)
526
607
  return fail(beforeWrite.status, context, beforeWrite.error, prNumber);
527
608
  if (!sameIdentity(initial.value, beforeWrite.value))
@@ -556,7 +637,7 @@ async function syncPullRequestSummary(taskRef, options) {
556
637
  : unsupportedProviderOperation(loaded.value.provider, 'comments.write');
557
638
  if (!written.ok)
558
639
  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);
640
+ const after = await inspectBoundPullRequest(context, resolved.repoRoot, prIdentity, loaded.value);
560
641
  const id = /^\d+$/.test(written.value.remoteId) ? Number(written.value.remoteId) : written.value.remoteId;
561
642
  const compensateWrittenSummary = async () => reconciliation.action === 'create'
562
643
  ? written.value.remoteId && loaded.value.provider.comments?.delete
@@ -610,7 +691,10 @@ async function syncPullRequestSummary(taskRef, options) {
610
691
  operations: [{ name: `summary:${reconciliation.action}`, status: 'applied', reasonCode: null }],
611
692
  result: null, warnings: [], ...info
612
693
  };
613
- });
694
+ };
695
+ return await (options.lockAlreadyHeld
696
+ ? execute()
697
+ : withTaskExecutionLock(resolved.repoRoot, resolved.taskId, options.agent, execute));
614
698
  }
615
699
  catch (error) {
616
700
  const lockError = error instanceof TaskExecutionLockError
@@ -619,5 +703,5 @@ async function syncPullRequestSummary(taskRef, options) {
619
703
  return fail(lockError.retryable ? 'blocked' : 'failed', context, lockError);
620
704
  }
621
705
  }
622
- export { buildPullRequestSummary, reconcileSummaryComment, reportWrite, summaryContext, summaryMarker, syncPullRequestSummary, warningResultForPrimary };
706
+ export { buildPullRequestSummary, reconcileSummaryComment, reportWrite, summaryCommentState, summaryContext, summaryMarker, syncPullRequestSummary, warningResultForPrimary };
623
707
  //# sourceMappingURL=pr-summary.js.map
@@ -1,3 +1,4 @@
1
+ import { normalizePullRequest } from './github-data.js';
1
2
  import fs from 'node:fs';
2
3
  import path from 'node:path';
3
4
  import { execFileSync } from 'node:child_process';
@@ -54,56 +55,6 @@ function withCreation(output, creation) {
54
55
  const PRECONDITION_NOT_CREATED = {
55
56
  kind: 'not-created', reason: 'precondition-failed', createdByCurrentOperation: false
56
57
  };
57
- function normalizePullRequest(remote, repository) {
58
- const number = Number(remote.number);
59
- const headRepository = remote.head?.repo?.full_name;
60
- const baseRepository = remote.base?.repo?.full_name;
61
- if (!Number.isInteger(number) || number <= 0 || !remote.node_id || !remote.html_url ||
62
- !remote.head?.ref || !remote.head.sha || !headRepository || !remote.base?.ref || !baseRepository)
63
- return null;
64
- const mergeabilityDetail = remote.mergeable_state?.trim().toLowerCase() || null;
65
- const mergeability = remote.mergeable === false
66
- ? { state: 'conflicting', detail: mergeabilityDetail }
67
- : remote.mergeable === true && mergeabilityDetail !== 'dirty'
68
- ? { state: 'mergeable', detail: mergeabilityDetail }
69
- : { state: 'unknown', detail: mergeabilityDetail };
70
- return {
71
- repository,
72
- number,
73
- nodeId: remote.node_id,
74
- url: remote.html_url,
75
- state: remote.state === 'closed' ? 'closed' : 'open',
76
- title: remote.title || '',
77
- body: remote.body || '',
78
- draft: Boolean(remote.draft),
79
- head: { repository: headRepository, ref: remote.head.ref, sha: remote.head.sha },
80
- base: { repository: baseRepository, ref: remote.base.ref, sha: remote.base.sha || '' },
81
- mergedAt: remote.merged_at || null,
82
- mergeCommitSha: remote.merge_commit_sha || null,
83
- labels: (remote.labels || []).map((label) => typeof label === 'string' ? label : label.name || '').filter(Boolean).sort(),
84
- assignees: (remote.assignees || []).map((assignee) => assignee.login || '').filter(Boolean).sort(),
85
- milestone: remote.milestone?.title || null,
86
- mergeability
87
- };
88
- }
89
- function normalizeClosingPullRequest(node, repository) {
90
- return normalizePullRequest({
91
- number: node.number,
92
- node_id: node.id,
93
- html_url: node.url,
94
- state: node.state === 'OPEN' ? 'open' : 'closed',
95
- title: node.title,
96
- body: node.body,
97
- draft: node.isDraft,
98
- head: { ref: node.headRefName, sha: node.headRefOid, repo: node.headRepository ? { full_name: node.headRepository.nameWithOwner } : null },
99
- base: { ref: node.baseRefName, sha: node.baseRefOid, repo: node.baseRepository ? { full_name: node.baseRepository.nameWithOwner } : null },
100
- merged_at: node.mergedAt,
101
- merge_commit_sha: node.mergeCommit?.oid,
102
- labels: node.labels?.nodes,
103
- assignees: node.assignees?.nodes,
104
- milestone: node.milestone
105
- }, repository);
106
- }
107
58
  function repositoryKey(repository) {
108
59
  return repository.trim().toLowerCase();
109
60
  }
@@ -183,51 +134,6 @@ function selectExternalPullRequest(candidates, repository, existingPrIdentity, e
183
134
  };
184
135
  return { status: 'selected', source: 'unique', selected: eligible[0], candidates, eligible };
185
136
  }
186
- const CLOSING_PULL_REQUESTS_QUERY = `query($owner:String!,$name:String!,$issue:Int!,$cursor:String){repository(owner:$owner,name:$name){issue(number:$issue){closedByPullRequestsReferences(first:100,after:$cursor){nodes{number id url state title body isDraft headRefName headRefOid headRepository{nameWithOwner} baseRefName baseRefOid baseRepository{nameWithOwner} mergedAt mergeCommit{oid} labels(first:100){nodes{name}} assignees(first:100){nodes{login}} milestone{title}} pageInfo{hasNextPage endCursor}}}}}`;
187
- function inspectGitHubIssueClosingChangeRequests(client, repository, issueNumber, cwd) {
188
- const [owner, name] = repository.split('/');
189
- if (!owner || !name || !Number.isInteger(issueNumber) || issueNumber <= 0)
190
- return {
191
- ok: false,
192
- error: { code: 'PR_IDENTITY_INVALID', message: 'Repository or Issue identity is invalid', retryable: false }
193
- };
194
- const candidates = [];
195
- let cursor = null;
196
- for (;;) {
197
- const args = [
198
- 'api', 'graphql', '-f', `query=${CLOSING_PULL_REQUESTS_QUERY}`,
199
- '-F', `owner=${owner}`, '-F', `name=${name}`, '-F', `issue=${issueNumber}`
200
- ];
201
- if (cursor)
202
- args.push('-F', `cursor=${cursor}`);
203
- const response = client.json(args, { cwd });
204
- if (!response.ok)
205
- return response;
206
- const connection = response.value?.data?.repository?.issue?.closedByPullRequestsReferences;
207
- if (!connection || !Array.isArray(connection.nodes) || !connection.pageInfo)
208
- return {
209
- ok: false,
210
- error: { code: 'PR_IDENTITY_INVALID', message: 'Closing pull request response is incomplete', retryable: false }
211
- };
212
- for (const node of connection.nodes) {
213
- const normalized = normalizeClosingPullRequest(node, repository);
214
- if (!normalized)
215
- return {
216
- ok: false,
217
- error: { code: 'PR_IDENTITY_INVALID', message: 'Closing pull request identity is incomplete', retryable: false }
218
- };
219
- candidates.push(normalized);
220
- }
221
- if (!connection.pageInfo.hasNextPage)
222
- return { ok: true, value: candidates };
223
- if (!connection.pageInfo.endCursor || connection.pageInfo.endCursor === cursor)
224
- return {
225
- ok: false,
226
- error: { code: 'PR_IDENTITY_INVALID', message: 'Closing pull request pagination cursor is invalid', retryable: false }
227
- };
228
- cursor = connection.pageInfo.endCursor;
229
- }
230
- }
231
137
  function expectedHead(repository, head) {
232
138
  const colon = head.indexOf(':');
233
139
  if (colon === -1)
@@ -346,7 +252,7 @@ async function resolvedContext(taskRef, options) {
346
252
  const fact = factRead.status === 'valid' ? factRead.fact : null;
347
253
  let issueIdentity;
348
254
  try {
349
- issueIdentity = taskIssueIdentity(frontmatter, undefined, options.runtimeVersion);
255
+ issueIdentity = taskIssueIdentity(frontmatter);
350
256
  }
351
257
  catch (error) {
352
258
  return { ok: false, output: result('failed', resolved.taskId, null, null, { error: { ...taskIssueIdentityError(error), retryable: false } }) };
@@ -420,15 +326,6 @@ async function inspectExternalPullRequest(base, prIdentity) {
420
326
  error: null
421
327
  });
422
328
  }
423
- function inspectGitHubPullRequest(client, repository, number, cwd) {
424
- const fetched = client.json(['api', `repos/${repository}/pulls/${number}`], { cwd });
425
- if (!fetched.ok)
426
- return fetched;
427
- const pullRequest = normalizePullRequest(fetched.value, repository);
428
- return pullRequest
429
- ? { ok: true, value: pullRequest }
430
- : { ok: false, error: { code: 'PR_IDENTITY_INVALID', message: 'Remote resource is not a valid pull request', retryable: false } };
431
- }
432
329
  async function inspectPlatformPullRequest(taskRef, options = {}) {
433
330
  const base = await resolvedContext(taskRef, options);
434
331
  if (!base.ok)
@@ -879,7 +776,7 @@ async function resolveExternalPullRequest(taskRef, options) {
879
776
  return externalResult(result('failed', base.resolved.taskId, null, base.prNumber, {
880
777
  platform: base.context.platform,
881
778
  capabilities: base.context.capabilities,
882
- error: { code: 'EXTERNAL_DELIVERY_ISSUE_REQUIRED', message: 'External delivery requires a valid issue_number', retryable: false }
779
+ error: { code: 'EXTERNAL_DELIVERY_ISSUE_REQUIRED', message: 'External delivery requires a valid platform issue identity', retryable: false }
883
780
  }));
884
781
  const inspected = base.provider.changeRequests?.listClosing
885
782
  ? await base.provider.changeRequests.listClosing({
@@ -1388,7 +1285,7 @@ function skipPlatformPullRequestFactUnlocked(resolved, options) {
1388
1285
  }
1389
1286
  let issueIdentity;
1390
1287
  try {
1391
- issueIdentity = taskIssueIdentity(frontmatter, undefined, options.runtimeVersion);
1288
+ issueIdentity = taskIssueIdentity(frontmatter);
1392
1289
  }
1393
1290
  catch (error) {
1394
1291
  return result('failed', resolved.taskId, null, null, { error: { ...taskIssueIdentityError(error), retryable: false } });
@@ -1463,6 +1360,6 @@ async function skipPlatformPullRequestFact(taskRef, options) {
1463
1360
  });
1464
1361
  }
1465
1362
  }
1466
- export { bindPlatformPullRequest, createPlatformPullRequest, inspectGitHubPullRequest, inspectGitHubIssueClosingChangeRequests, inspectPlatformPullRequest, inspectPlatformPullRequestByNumber, normalizePullRequest, resolveExternalPullRequest, skipPlatformPullRequestFact, selectExternalPullRequest, selectPullRequest, syncPlatformPullRequest, syncPlatformPullRequestInLabels };
1363
+ export { bindPlatformPullRequest, createPlatformPullRequest, inspectPlatformPullRequest, inspectPlatformPullRequestByNumber, resolveExternalPullRequest, skipPlatformPullRequestFact, selectExternalPullRequest, selectPullRequest, syncPlatformPullRequest, syncPlatformPullRequestInLabels };
1467
1364
  export { warningResultForPrimary };
1468
1365
  //# sourceMappingURL=pull-requests.js.map
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=snapshots.js.map
@@ -1,7 +1,6 @@
1
1
  import { parseResourceIdentity } from './resource-identity.js';
2
- import { isLegacyCompatibilityEnabled, legacyCompatibilityError, VERSION } from '../version.js';
3
2
  import { parse as parseYaml } from 'yaml';
4
- function taskIssueIdentity(frontmatter, _declaration, runtimeVersion = VERSION) {
3
+ function taskIssueIdentity(frontmatter) {
5
4
  const serialized = frontmatter.platform_issue_identity;
6
5
  if (typeof serialized === 'string' && serialized.trim()) {
7
6
  try {
@@ -12,13 +11,7 @@ function taskIssueIdentity(frontmatter, _declaration, runtimeVersion = VERSION)
12
11
  return null;
13
12
  }
14
13
  }
15
- const legacy = frontmatter.issue_number;
16
- const number = typeof legacy === 'number' ? legacy : typeof legacy === 'string' && /^[1-9]\d*$/u.test(legacy) ? Number(legacy) : NaN;
17
- // TODO(compat): Remove the numeric issue_number fallback before the first stable v1.0.0 release.
18
- if (Number.isSafeInteger(number) && number > 0 && !isLegacyCompatibilityEnabled(runtimeVersion)) {
19
- throw legacyCompatibilityError('issue_number', runtimeVersion);
20
- }
21
- return Number.isSafeInteger(number) && number > 0 ? { kind: 'number', value: number } : null;
14
+ return null;
22
15
  }
23
16
  function taskIssueIdentityError(error) {
24
17
  const value = error && typeof error === 'object' ? error : {};