@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
@@ -16,7 +16,9 @@ import { platformResult } from './types.ts';
16
16
  import type { PlatformResult } from './types.ts';
17
17
  import type { OperationWarning } from '../task/operation-outcome.ts';
18
18
  import { providerError, providerOperationContext, providerStatus, providerResourceToken, resourceIdentityNumber, unsupportedProviderOperation } from './provider-bridge.ts';
19
- import type { PlatformChangeRequestSnapshot } from './adapters.ts';
19
+ import { resourceIdentityEquals } from './resource-identity.ts';
20
+ import type { ResourceIdentity } from './resource-identity.ts';
21
+ import type { PlatformChangeRequestSnapshot } from './snapshots.ts';
20
22
  import type { ChangeRequestSnapshot } from './provider-contract.ts';
21
23
  import {
22
24
  buildPrChangeReport,
@@ -33,6 +35,8 @@ import type {
33
35
  MechanicalChangeReport,
34
36
  PrChangeReport
35
37
  } from './pr-change-report.ts';
38
+ import { manualValidationFinalSummaryProjectionMatches } from '../task/manual-validation-receipt.ts';
39
+ import { readManualValidationCompletion } from '../task/manual-validation-completion.ts';
36
40
 
37
41
  type SummaryComment = { id: number | string; body: string };
38
42
  type ChangeReportState = 'ready' | 'missing' | 'stale' | 'invalid';
@@ -50,9 +54,21 @@ type PullRequestSummaryResult = PlatformResult & {
50
54
  };
51
55
  type PullRequestPrimaryResult = 'pr_created' | 'pr_reused' | 'no_op';
52
56
  type SummaryOptions = { cwd?: string; client?: PlatformClient; runtimeVersion?: string };
57
+ type ManualValidationSummaryOptions = {
58
+ phase: 'pending' | 'final';
59
+ transactionId?: string;
60
+ receiptDigest?: string;
61
+ prHeadSha?: string;
62
+ authority?: 'coordinator';
63
+ };
53
64
  type ReportWriteResult = PlatformResult & {
54
65
  report: { path: string; status: 'written' | 'no-op' | 'planned'; precheckVerdict: 'clear' | 'needs-review'; nextAction: 'watch-pr' | 'review-code' } | null;
55
66
  };
67
+ type SummaryCommentStateResult = Omit<PlatformResult, 'comment'> & {
68
+ task: { id: string | null; prNumber: number | null };
69
+ pullRequest: PlatformChangeRequestSnapshot | null;
70
+ comment: SummaryComment | null;
71
+ };
56
72
 
57
73
  function summaryMarker(taskId: string): string {
58
74
  return `<!-- sync-pr:${taskId}:summary -->`;
@@ -88,12 +104,17 @@ function taskReportPath(taskDir: string): string {
88
104
  return path.join(taskDir, 'pr-change-report.json');
89
105
  }
90
106
 
91
- function readBoundTaskSnapshot(taskMdPath: string): { taskContent: string; boundFact: Extract<PrDeliveryFact, { state: 'bound' }> | null } {
107
+ function readBoundTaskSnapshot(taskMdPath: string): {
108
+ taskContent: string;
109
+ boundFact: Extract<PrDeliveryFact, { state: 'bound' }> | null;
110
+ factError: { code: string; message: string } | null;
111
+ } {
92
112
  const taskContent = fs.readFileSync(taskMdPath, 'utf8');
93
113
  const frontmatter = parseTypedTaskFrontmatter(taskContent);
94
114
  const fact = readPrDeliveryFact(frontmatter);
95
115
  const boundFact = fact.status === 'valid' && fact.fact.state === 'bound' ? fact.fact : null;
96
- return { taskContent, boundFact };
116
+ const factError = fact.status === 'invalid' ? { code: fact.error.code, message: fact.error.message } : null;
117
+ return { taskContent, boundFact, factError };
97
118
  }
98
119
 
99
120
  function boundPrNumberForWarning(taskMdPath: string): number | null {
@@ -175,7 +196,7 @@ function inspectionError(error: { code: string; message: string; retryable: bool
175
196
  async function inspectBoundPullRequest(
176
197
  context: PlatformResult,
177
198
  repoRoot: string,
178
- prNumber: number,
199
+ prIdentity: ResourceIdentity,
179
200
  loadedContext?: LoadedContext
180
201
  ): Promise<{ ok: true; value: PlatformChangeRequestSnapshot } | { ok: false; error: PlatformResult['error']; status: PlatformResult['status'] }> {
181
202
  if (!context.platform.repository) {
@@ -185,16 +206,15 @@ async function inspectBoundPullRequest(
185
206
  ? { ok: true as const, value: loadedContext }
186
207
  : await resolvePlatformProviderContext({ cwd: repoRoot });
187
208
  if (!loaded.ok) return { ok: false, status: loaded.context.status, error: loaded.context.error };
188
- const target = providerResourceToken(loaded.value.provider, 'pull-request', String(prNumber));
189
209
  const inspected = loaded.value.provider.changeRequests?.inspect
190
- ? await loaded.value.provider.changeRequests.inspect({ context: providerOperationContext(loaded.value), target })
210
+ ? await loaded.value.provider.changeRequests.inspect({ context: providerOperationContext(loaded.value), target: prIdentity })
191
211
  : unsupportedProviderOperation(loaded.value.provider, 'changeRequests.inspect');
192
212
  if (!inspected.ok) {
193
213
  const error = providerError(inspected.error, 'PLATFORM_PROVIDER_OPERATION_FAILED');
194
214
  return { ok: false, status: inspectionError(error), error };
195
215
  }
196
216
  const remote: ChangeRequestSnapshot = inspected.value;
197
- const number = remote.number ?? resourceIdentityNumber(remote.identity) ?? prNumber;
217
+ const number = remote.number ?? resourceIdentityNumber(remote.identity) ?? resourceIdentityNumber(prIdentity) ?? 0;
198
218
  const head = remote.head || { repository: context.platform.repository, ref: '', sha: remote.headSha || '' };
199
219
  const base = remote.base || { repository: context.platform.repository, ref: '', sha: remote.baseSha || '' };
200
220
  return {
@@ -202,6 +222,7 @@ async function inspectBoundPullRequest(
202
222
  value: {
203
223
  repository: context.platform.repository,
204
224
  number,
225
+ identity: remote.identity ?? prIdentity,
205
226
  nodeId: remote.id,
206
227
  url: remote.displayUrl || '',
207
228
  state: remote.state === 'closed' ? 'closed' : 'open',
@@ -223,21 +244,22 @@ async function inspectBoundPullRequest(
223
244
  function sameIdentity(left: PlatformChangeRequestSnapshot, right: PlatformChangeRequestSnapshot): boolean {
224
245
  return JSON.stringify({
225
246
  repository: left.repository,
226
- number: left.number,
247
+ identity: left.identity ?? null,
227
248
  base: left.base,
228
249
  head: left.head
229
250
  }) === JSON.stringify({
230
251
  repository: right.repository,
231
- number: right.number,
252
+ identity: right.identity ?? null,
232
253
  base: right.base,
233
254
  head: right.head
234
255
  });
235
256
  }
236
257
 
237
258
  function identityFromSnapshot(snapshot: PlatformChangeRequestSnapshot) {
259
+ if (!snapshot.identity) throw new Error('pull-request snapshot has no canonical identity');
238
260
  return {
239
261
  repository: snapshot.repository,
240
- number: snapshot.number,
262
+ resource: snapshot.identity,
241
263
  base: { ...snapshot.base },
242
264
  head: { ...snapshot.head }
243
265
  };
@@ -272,7 +294,7 @@ function currentReportCheck(
272
294
  const digest = taskIntentDigest(taskContent);
273
295
  if (!digest.ok) return { ok: false, error: platformError(digest.error) };
274
296
  const reportIdentity = report.identity;
275
- if (reportIdentity.repository !== snapshot.repository || reportIdentity.number !== snapshot.number ||
297
+ if (reportIdentity.repository !== snapshot.repository || !resourceIdentityEquals(reportIdentity.resource, snapshot.identity) ||
276
298
  JSON.stringify(reportIdentity.base) !== JSON.stringify(snapshot.base) || JSON.stringify(reportIdentity.head) !== JSON.stringify(snapshot.head)) return {
277
299
  ok: false,
278
300
  error: { code: 'PR_CHANGE_REPORT_STALE', message: 'Change report identity does not match the authoritative pull request snapshot', retryable: false }
@@ -329,14 +351,22 @@ async function summaryContext(taskRef: string, options: SummaryOptions = {}): Pr
329
351
  };
330
352
  const taskContent = fs.readFileSync(resolved.taskMdPath, 'utf8');
331
353
  const frontmatter = parseTypedTaskFrontmatter(taskContent);
332
- const fact = readPrDeliveryFact(frontmatter, options.runtimeVersion);
333
- const prNumber = fact.status === 'valid' && fact.fact.state === 'bound' ? resourceIdentityNumber(fact.fact.identity.resource) : null;
334
354
  const reportPath = taskReportPath(resolved.taskDir);
355
+ const fact = readPrDeliveryFact(frontmatter, options.runtimeVersion);
356
+ if (fact.status === 'invalid') return {
357
+ ...platformResult('failed', { error: { code: fact.error.code, message: fact.error.message, retryable: false } }),
358
+ task: { id: resolved.taskId, prNumber: null },
359
+ pullRequest: null,
360
+ changeReport: { status: 'invalid', path: reportPath, taskIntentSha256: null, reason: fact.error.message },
361
+ artifacts: []
362
+ };
363
+ const prIdentity = fact.status === 'valid' && fact.fact.state === 'bound' ? fact.fact.identity.resource : null;
364
+ const prNumber = resourceIdentityNumber(prIdentity);
335
365
  const loaded = await resolvePlatformProviderContext({ cwd: resolved.repoRoot, client: options.client });
336
366
  const context = loaded.ok ? loaded.value.context : loaded.context;
337
367
  let pullRequest: PlatformChangeRequestSnapshot | null = null;
338
- if (loaded.ok && prNumber && context.platform.repository && ['no-op', 'degraded'].includes(context.status)) {
339
- const inspected = await inspectBoundPullRequest(context, resolved.repoRoot, prNumber, loaded.value);
368
+ if (loaded.ok && prIdentity && context.platform.repository && ['no-op', 'degraded'].includes(context.status)) {
369
+ const inspected = await inspectBoundPullRequest(context, resolved.repoRoot, prIdentity, loaded.value);
340
370
  if (inspected.ok) pullRequest = inspected.value;
341
371
  }
342
372
  const report = summaryReportStatus(reportPath, pullRequest, taskContent, resolved.repoRoot);
@@ -354,6 +384,24 @@ async function summaryContext(taskRef: string, options: SummaryOptions = {}): Pr
354
384
  };
355
385
  }
356
386
 
387
+ async function summaryCommentState(taskRef: string, options: SummaryOptions = {}): Promise<SummaryCommentStateResult> {
388
+ const context = await summaryContext(taskRef, options);
389
+ const prIdentity = context.pullRequest?.identity;
390
+ if (!prIdentity) return { ...context, comment: null };
391
+ const loaded = await resolvePlatformProviderContext({ cwd: options.cwd, client: options.client });
392
+ if (!loaded.ok || !loaded.value.provider.comments?.list) return { ...context, comment: null };
393
+ const listed = await loaded.value.provider.comments.list({
394
+ context: providerOperationContext(loaded.value),
395
+ parent: prIdentity
396
+ });
397
+ if (!listed.ok) return { ...context, comment: null };
398
+ const matches = listed.value.filter((comment) => comment.body.includes(summaryMarker(context.task.id!)));
399
+ return {
400
+ ...context,
401
+ comment: matches.length === 1 ? { id: /^\d+$/u.test(matches[0]!.id) ? Number(matches[0]!.id) : matches[0]!.id, body: matches[0]!.body } : null
402
+ };
403
+ }
404
+
357
405
  type ReportWriteOptions = {
358
406
  agent: string;
359
407
  mechanicalFile: string;
@@ -366,24 +414,38 @@ type ReportWriteOptions = {
366
414
  async function reportWrite(taskRef: string, options: ReportWriteOptions): Promise<ReportWriteResult> {
367
415
  const resolved = resolveTaskRef(taskRef, options.cwd ? { repoRoot: options.cwd } : {});
368
416
  if (!resolved.ok) return { ...platformResult('failed', { error: { code: resolved.code, message: resolved.message, retryable: false } }), report: null };
417
+ let preflight: ReturnType<typeof readBoundTaskSnapshot>;
418
+ try {
419
+ preflight = readBoundTaskSnapshot(resolved.taskMdPath);
420
+ } catch (error) {
421
+ return {
422
+ ...platformResult('failed', { error: { code: 'PR_CHANGE_REPORT_FAILED', message: error instanceof Error ? error.message : String(error), retryable: false } }),
423
+ report: null
424
+ };
425
+ }
426
+ if (preflight.factError) return {
427
+ ...platformResult('failed', { error: { code: preflight.factError.code, message: preflight.factError.message, retryable: false } }),
428
+ report: null
429
+ };
369
430
  const loaded = await resolvePlatformProviderContext({ cwd: resolved.repoRoot, client: options.client });
370
431
  const context = loaded.ok ? loaded.value.context : loaded.context;
371
432
  if (!loaded.ok || !context.platform.repository || !['no-op', 'degraded'].includes(context.status)) return { ...context, report: null };
372
433
  let knownPrNumber: number | null = null;
373
434
  try {
374
435
  return await withTaskExecutionLock(resolved.repoRoot, resolved.taskId, options.agent, async () => {
375
- const { taskContent, boundFact } = readBoundTaskSnapshot(resolved.taskMdPath);
376
- if (!boundFact) return {
377
- ...basePlatformResult('failed', context, resolved.taskId, null, { code: 'PR_NOT_LINKED', message: 'Task has no verified bound pull request', retryable: false }),
436
+ const { taskContent, boundFact, factError } = readBoundTaskSnapshot(resolved.taskMdPath);
437
+ if (factError) return {
438
+ ...basePlatformResult('failed', context, resolved.taskId, null, { code: factError.code, message: factError.message, retryable: false }),
378
439
  report: null
379
440
  };
380
- const boundPrNumber = resourceIdentityNumber(boundFact.identity.resource);
381
- if (!boundPrNumber) return {
382
- ...basePlatformResult('failed', context, resolved.taskId, null, { code: 'PR_NUMBER_INVALID', message: 'Bound pull request has no numeric identifier', retryable: false }),
441
+ if (!boundFact) return {
442
+ ...basePlatformResult('failed', context, resolved.taskId, null, { code: 'PR_NOT_LINKED', message: 'Task has no verified bound pull request', retryable: false }),
383
443
  report: null
384
444
  };
445
+ const boundPrIdentity = boundFact.identity.resource;
446
+ const boundPrNumber = resourceIdentityNumber(boundPrIdentity);
385
447
  knownPrNumber = boundPrNumber;
386
- const initial = await inspectBoundPullRequest(context, resolved.repoRoot, boundPrNumber, loaded.value);
448
+ const initial = await inspectBoundPullRequest(context, resolved.repoRoot, boundPrIdentity, loaded.value);
387
449
  if (!initial.ok) return { ...basePlatformResult(initial.status, context, resolved.taskId, boundPrNumber, initial.error), report: null };
388
450
  const digest = taskIntentDigest(taskContent);
389
451
  if (!digest.ok) return { ...basePlatformResult('failed', context, resolved.taskId, boundPrNumber, platformError(digest.error)), report: null };
@@ -407,7 +469,7 @@ async function reportWrite(taskRef: string, options: ReportWriteOptions): Promis
407
469
  };
408
470
  const built = buildPrChangeReport(identityFromSnapshot(initial.value), digest.value.sha256, mechanical.value, candidate.value);
409
471
  if (!built.ok) return { ...basePlatformResult('failed', context, resolved.taskId, boundPrNumber, platformError(built.error)), report: null };
410
- const final = await inspectBoundPullRequest(context, resolved.repoRoot, boundPrNumber, loaded.value);
472
+ const final = await inspectBoundPullRequest(context, resolved.repoRoot, boundPrIdentity, loaded.value);
411
473
  if (!final.ok) return { ...basePlatformResult(final.status, context, resolved.taskId, boundPrNumber, final.error), report: null };
412
474
  if (!sameIdentity(initial.value, final.value)) return {
413
475
  ...basePlatformResult('blocked', context, resolved.taskId, boundPrNumber, { code: 'PR_CHANGE_REPORT_HEAD_RACE', message: 'Pull request identity changed while generating the change report', retryable: true }),
@@ -450,7 +512,7 @@ async function reportWrite(taskRef: string, options: ReportWriteOptions): Promis
450
512
 
451
513
  async function syncPullRequestSummary(
452
514
  taskRef: string,
453
- options: { agent: string; body: string; changeReportFile?: string; cwd?: string; client?: PlatformClient; dryRun?: boolean; strict?: boolean; primaryResult: PullRequestPrimaryResult; runtimeVersion?: string }
515
+ options: { agent: string; body: string; changeReportFile?: string; cwd?: string; client?: PlatformClient; dryRun?: boolean; strict?: boolean; primaryResult: PullRequestPrimaryResult; runtimeVersion?: string; manualValidation?: ManualValidationSummaryOptions; lockAlreadyHeld?: boolean }
454
516
  ): Promise<PullRequestSummaryResult> {
455
517
  const warningResult = warningResultForPrimary(options.primaryResult);
456
518
  let knownPrNumber: number | null = null;
@@ -503,22 +565,44 @@ async function syncPullRequestSummary(
503
565
  return preserveFailure(context);
504
566
  }
505
567
  try {
506
- return await withTaskExecutionLock(resolved.repoRoot, resolved.taskId, options.agent, async () => {
507
- const { taskContent, boundFact } = readBoundTaskSnapshot(resolved.taskMdPath);
568
+ const execute = async (): Promise<PullRequestSummaryResult> => {
569
+ const { taskContent, boundFact, factError } = readBoundTaskSnapshot(resolved.taskMdPath);
570
+ if (factError) return {
571
+ ...platformResult('failed', { resource: { kind: 'pull-request', number: null }, error: { code: factError.code, message: factError.message, retryable: false } }),
572
+ result: null, warnings: []
573
+ };
508
574
  if (!boundFact) return {
509
575
  ...platformResult('failed', { resource: { kind: 'pull-request', number: null }, error: { code: 'PR_NOT_LINKED', message: 'Task has no verified bound pull request', retryable: false } }),
510
576
  result: null, warnings: []
511
577
  };
512
578
  const prIdentity = boundFact.identity.resource;
513
579
  const boundPrNumber = resourceIdentityNumber(prIdentity);
514
- if (!boundPrNumber) return {
515
- ...platformResult('failed', { resource: { kind: 'pull-request', number: null }, error: { code: 'PR_NUMBER_INVALID', message: 'Bound pull request has no numeric identifier', retryable: false } }),
516
- result: null, warnings: []
517
- };
518
580
  knownPrNumber = boundPrNumber;
519
581
  const prNumber = boundPrNumber;
520
- const initial = await inspectBoundPullRequest(context, resolved.repoRoot, boundPrNumber, loaded.value);
582
+ const initial = await inspectBoundPullRequest(context, resolved.repoRoot, prIdentity, loaded.value);
521
583
  if (!initial.ok) return fail(initial.status, context, initial.error);
584
+ const manual = options.manualValidation;
585
+ const hasFinalManualValidation = /###\s+✅\s+(?:Manual Validation Passed|人工验证已通过)/u.test(options.body);
586
+ if (hasFinalManualValidation && (!manual || manual.phase !== 'final')) return fail('failed', context, { code: 'MANUAL_VALIDATION_TRANSACTION_REQUIRED', message: 'final manual-validation summary requires the transaction coordinator', retryable: false }, prNumber);
587
+ if (manual && (manual.phase === 'pending' ? hasFinalManualValidation : !hasFinalManualValidation)) 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);
588
+ if (manual) {
589
+ if (prNumber === null) return fail('failed', context, { code: 'MANUAL_VALIDATION_PR_NUMBER_REQUIRED', message: 'manual-validation summary requires a numeric pull-request identity', retryable: false }, prNumber);
590
+ if (manual.phase === 'final' && (!manual.transactionId || !manual.receiptDigest || !manual.prHeadSha || manual.prHeadSha !== initial.value.head.sha)) return fail('failed', context, { code: 'MANUAL_VALIDATION_TRANSACTION_REQUIRED', message: 'final manual-validation summary requires transaction, receipt, and current head identity', retryable: false }, prNumber);
591
+ if (manual.phase === 'final') {
592
+ if (manual.authority !== 'coordinator') return fail('failed', context, { code: 'MANUAL_VALIDATION_TRANSACTION_REQUIRED', message: 'final manual-validation summary requires coordinator authority', retryable: false }, prNumber);
593
+ const completion = readManualValidationCompletion(resolved.taskDir, {
594
+ transactionId: manual.transactionId,
595
+ taskId: resolved.taskId,
596
+ prNumber,
597
+ prHeadSha: initial.value.head.sha,
598
+ receiptDigest: manual.receiptDigest
599
+ });
600
+ if (!completion.ok) return fail('failed', context, platformError(completion.error), prNumber);
601
+ const { receipt, transaction } = completion.value;
602
+ if (transaction.phase !== 'final-promotion-in-progress' || !transaction.eventAppended) return fail('failed', context, { code: 'MANUAL_VALIDATION_TRANSACTION_PHASE_INVALID', message: 'final summary requires a receipt-backed promotion intent', retryable: false }, prNumber);
603
+ if (!manualValidationFinalSummaryProjectionMatches(options.body, receipt)) return fail('failed', context, { code: 'MANUAL_VALIDATION_SUMMARY_PROJECTION_INVALID', message: 'final summary body does not match the canonical receipt projection', retryable: false }, prNumber);
604
+ }
605
+ }
522
606
  const expectedReportPath = taskReportPath(resolved.taskDir);
523
607
  const suppliedReportPath = path.resolve(resolved.repoRoot, options.changeReportFile!);
524
608
  if (suppliedReportPath !== expectedReportPath) return fail('failed', context, { code: 'PR_CHANGE_REPORT_PATH_INVALID', message: 'summary-sync must consume the task-bound pr-change-report.json', retryable: false });
@@ -569,7 +653,7 @@ async function syncPullRequestSummary(
569
653
  ? listed.value.find((comment) => comment.id === reconciliation.commentId)?.body ?? null
570
654
  : null;
571
655
  const info = { precheckVerdict: report.value.precheck.verdict, nextAction: report.value.precheck.route } as const;
572
- const beforeWrite = await inspectBoundPullRequest(context, resolved.repoRoot, boundPrNumber, loaded.value);
656
+ const beforeWrite = await inspectBoundPullRequest(context, resolved.repoRoot, prIdentity, loaded.value);
573
657
  if (!beforeWrite.ok) return fail(beforeWrite.status, context, beforeWrite.error, prNumber);
574
658
  if (!sameIdentity(initial.value, beforeWrite.value)) return fail('blocked', context, {
575
659
  code: 'PR_SUMMARY_HEAD_RACE',
@@ -599,7 +683,7 @@ async function syncPullRequestSummary(
599
683
  })
600
684
  : unsupportedProviderOperation(loaded.value.provider, 'comments.write');
601
685
  if (!written.ok) return fail(providerStatus(written.error) === 'blocked' ? 'blocked' : 'failed', context, providerError(written.error, 'PLATFORM_PROVIDER_OPERATION_FAILED'), prNumber);
602
- const after = await inspectBoundPullRequest(context, resolved.repoRoot, prNumber, loaded.value);
686
+ const after = await inspectBoundPullRequest(context, resolved.repoRoot, prIdentity, loaded.value);
603
687
  const id = /^\d+$/.test(written.value.remoteId) ? Number(written.value.remoteId) : written.value.remoteId;
604
688
  const compensateWrittenSummary = async () => reconciliation.action === 'create'
605
689
  ? written.value.remoteId && loaded.value.provider.comments?.delete
@@ -651,7 +735,10 @@ async function syncPullRequestSummary(
651
735
  operations: [{ name: `summary:${reconciliation.action}`, status: 'applied', reasonCode: null }],
652
736
  result: null, warnings: [], ...info
653
737
  };
654
- });
738
+ };
739
+ return await (options.lockAlreadyHeld
740
+ ? execute()
741
+ : withTaskExecutionLock(resolved.repoRoot, resolved.taskId, options.agent, execute));
655
742
  } catch (error) {
656
743
  const lockError = error instanceof TaskExecutionLockError
657
744
  ? { code: error.code, message: error.message, retryable: error.code === 'ORCHESTRATION_LOCK_BUSY' }
@@ -664,9 +751,10 @@ export {
664
751
  buildPullRequestSummary,
665
752
  reconcileSummaryComment,
666
753
  reportWrite,
754
+ summaryCommentState,
667
755
  summaryContext,
668
756
  summaryMarker,
669
757
  syncPullRequestSummary,
670
758
  warningResultForPrimary
671
759
  };
672
- export type { PullRequestSummaryResult, ReportWriteOptions, ReportWriteResult, SummaryContextResult };
760
+ export type { ManualValidationSummaryOptions, PullRequestSummaryResult, ReportWriteOptions, ReportWriteResult, SummaryCommentStateResult, SummaryContextResult };
@@ -1,3 +1,4 @@
1
+ import { normalizePullRequest, type RemotePullRequest } from './github-data.ts';
1
2
  import fs from 'node:fs';
2
3
  import path from 'node:path';
3
4
  import { execFileSync } from 'node:child_process';
@@ -6,7 +7,7 @@ import { parseTypedTaskFrontmatter } from '../task/frontmatter.ts';
6
7
  import { resolveTaskRef } from '../task/resolve-ref.ts';
7
8
  import { extractSection } from '../task/sections.ts';
8
9
  import { captureTaskWriteMetadata, writeTask } from '../task/write.ts';
9
- import type { PlatformChangeRequestSnapshot } from './adapters.ts';
10
+ import type { PlatformChangeRequestSnapshot } from './snapshots.ts';
10
11
  import { resolvePlatformProviderContext } from './context.ts';
11
12
  import type { PlatformClient } from './context.ts';
12
13
  import { inspectPlatformIssue } from './issues.ts';
@@ -61,7 +62,6 @@ type PullRequestResult = PlatformResult & {
61
62
  effect: 'no-op' | 'applied' | 'unknown';
62
63
  }>;
63
64
  };
64
- type GitHubClient = PlatformClient;
65
65
  type InspectionOptions = { cwd?: string; client?: PlatformClient; runtimeVersion?: string };
66
66
  type SharedOptions = { cwd?: string; client?: PlatformClient; runtimeVersion?: string };
67
67
  type CreateOptions = SharedOptions & {
@@ -93,54 +93,6 @@ type ExternalPullRequestResult = PullRequestResult & {
93
93
 
94
94
  type CreatePhase = 'before-post' | 'post-dispatched' | 'post-accepted';
95
95
 
96
- type RemotePullRequest = {
97
- number?: number;
98
- node_id?: string;
99
- html_url?: string;
100
- state?: string;
101
- title?: string;
102
- body?: string | null;
103
- draft?: boolean;
104
- pull_request?: unknown;
105
- head?: { ref?: string; sha?: string; repo?: { full_name?: string } | null };
106
- base?: { ref?: string; sha?: string; repo?: { full_name?: string } | null };
107
- merged_at?: string | null;
108
- merge_commit_sha?: string | null;
109
- labels?: Array<string | { name?: string }>;
110
- assignees?: Array<{ login?: string }>;
111
- milestone?: { title?: string } | null;
112
- mergeable?: boolean | null;
113
- mergeable_state?: string | null;
114
- };
115
-
116
- type ClosingPullRequestNode = {
117
- number?: number;
118
- id?: string;
119
- url?: string;
120
- state?: string;
121
- title?: string;
122
- body?: string | null;
123
- isDraft?: boolean;
124
- headRefName?: string;
125
- headRefOid?: string;
126
- headRepository?: { nameWithOwner?: string } | null;
127
- baseRefName?: string;
128
- baseRefOid?: string;
129
- baseRepository?: { nameWithOwner?: string } | null;
130
- mergedAt?: string | null;
131
- mergeCommit?: { oid?: string } | null;
132
- labels?: { nodes?: Array<{ name?: string }> };
133
- assignees?: { nodes?: Array<{ login?: string }> };
134
- milestone?: { title?: string } | null;
135
- };
136
-
137
- type ClosingPullRequestPage = {
138
- data?: { repository?: { issue?: { closedByPullRequestsReferences?: {
139
- nodes?: ClosingPullRequestNode[];
140
- pageInfo?: { hasNextPage?: boolean; endCursor?: string | null };
141
- } } } };
142
- };
143
-
144
96
  function warningResultForPrimary(primaryResult: PullRequestPrimaryResult): NonNullable<PullRequestResult['result']> {
145
97
  if (primaryResult === 'pr_created') return 'pr_created_with_warnings';
146
98
  if (primaryResult === 'pr_reused') return 'pr_reused_with_warnings';
@@ -189,57 +141,6 @@ const PRECONDITION_NOT_CREATED: CreationOutcome = {
189
141
  kind: 'not-created', reason: 'precondition-failed', createdByCurrentOperation: false
190
142
  };
191
143
 
192
- function normalizePullRequest(remote: RemotePullRequest, repository: string): PullRequestSnapshot | null {
193
- const number = Number(remote.number);
194
- const headRepository = remote.head?.repo?.full_name;
195
- const baseRepository = remote.base?.repo?.full_name;
196
- if (!Number.isInteger(number) || number <= 0 || !remote.node_id || !remote.html_url ||
197
- !remote.head?.ref || !remote.head.sha || !headRepository || !remote.base?.ref || !baseRepository) return null;
198
- const mergeabilityDetail = remote.mergeable_state?.trim().toLowerCase() || null;
199
- const mergeability = remote.mergeable === false
200
- ? { state: 'conflicting' as const, detail: mergeabilityDetail }
201
- : remote.mergeable === true && mergeabilityDetail !== 'dirty'
202
- ? { state: 'mergeable' as const, detail: mergeabilityDetail }
203
- : { state: 'unknown' as const, detail: mergeabilityDetail };
204
- return {
205
- repository,
206
- number,
207
- nodeId: remote.node_id,
208
- url: remote.html_url,
209
- state: remote.state === 'closed' ? 'closed' : 'open',
210
- title: remote.title || '',
211
- body: remote.body || '',
212
- draft: Boolean(remote.draft),
213
- head: { repository: headRepository, ref: remote.head.ref, sha: remote.head.sha },
214
- base: { repository: baseRepository, ref: remote.base.ref, sha: remote.base.sha || '' },
215
- mergedAt: remote.merged_at || null,
216
- mergeCommitSha: remote.merge_commit_sha || null,
217
- labels: (remote.labels || []).map((label) => typeof label === 'string' ? label : label.name || '').filter(Boolean).sort(),
218
- assignees: (remote.assignees || []).map((assignee) => assignee.login || '').filter(Boolean).sort(),
219
- milestone: remote.milestone?.title || null,
220
- mergeability
221
- };
222
- }
223
-
224
- function normalizeClosingPullRequest(node: ClosingPullRequestNode, repository: string): PullRequestSnapshot | null {
225
- return normalizePullRequest({
226
- number: node.number,
227
- node_id: node.id,
228
- html_url: node.url,
229
- state: node.state === 'OPEN' ? 'open' : 'closed',
230
- title: node.title,
231
- body: node.body,
232
- draft: node.isDraft,
233
- head: { ref: node.headRefName, sha: node.headRefOid, repo: node.headRepository ? { full_name: node.headRepository.nameWithOwner } : null },
234
- base: { ref: node.baseRefName, sha: node.baseRefOid, repo: node.baseRepository ? { full_name: node.baseRepository.nameWithOwner } : null },
235
- merged_at: node.mergedAt,
236
- merge_commit_sha: node.mergeCommit?.oid,
237
- labels: node.labels?.nodes,
238
- assignees: node.assignees?.nodes,
239
- milestone: node.milestone
240
- }, repository);
241
- }
242
-
243
144
  function repositoryKey(repository: string): string {
244
145
  return repository.trim().toLowerCase();
245
146
  }
@@ -329,51 +230,6 @@ function selectExternalPullRequest(
329
230
  return { status: 'selected', source: 'unique', selected: eligible[0]!, candidates, eligible };
330
231
  }
331
232
 
332
- 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}}}}}`;
333
-
334
- function inspectGitHubIssueClosingChangeRequests(
335
- client: GitHubClient,
336
- repository: string,
337
- issueNumber: number,
338
- cwd: string
339
- ) {
340
- const [owner, name] = repository.split('/');
341
- if (!owner || !name || !Number.isInteger(issueNumber) || issueNumber <= 0) return {
342
- ok: false as const,
343
- error: { code: 'PR_IDENTITY_INVALID', message: 'Repository or Issue identity is invalid', retryable: false }
344
- };
345
- const candidates: PullRequestSnapshot[] = [];
346
- let cursor: string | null = null;
347
- for (;;) {
348
- const args = [
349
- 'api', 'graphql', '-f', `query=${CLOSING_PULL_REQUESTS_QUERY}`,
350
- '-F', `owner=${owner}`, '-F', `name=${name}`, '-F', `issue=${issueNumber}`
351
- ];
352
- if (cursor) args.push('-F', `cursor=${cursor}`);
353
- const response = client.json<ClosingPullRequestPage>(args, { cwd });
354
- if (!response.ok) return response;
355
- const connection = response.value?.data?.repository?.issue?.closedByPullRequestsReferences;
356
- if (!connection || !Array.isArray(connection.nodes) || !connection.pageInfo) return {
357
- ok: false as const,
358
- error: { code: 'PR_IDENTITY_INVALID', message: 'Closing pull request response is incomplete', retryable: false }
359
- };
360
- for (const node of connection.nodes) {
361
- const normalized = normalizeClosingPullRequest(node, repository);
362
- if (!normalized) return {
363
- ok: false as const,
364
- error: { code: 'PR_IDENTITY_INVALID', message: 'Closing pull request identity is incomplete', retryable: false }
365
- };
366
- candidates.push(normalized);
367
- }
368
- if (!connection.pageInfo.hasNextPage) return { ok: true as const, value: candidates };
369
- if (!connection.pageInfo.endCursor || connection.pageInfo.endCursor === cursor) return {
370
- ok: false as const,
371
- error: { code: 'PR_IDENTITY_INVALID', message: 'Closing pull request pagination cursor is invalid', retryable: false }
372
- };
373
- cursor = connection.pageInfo.endCursor;
374
- }
375
- }
376
-
377
233
  function expectedHead(repository: string, head: string): { repository: string; ref: string } {
378
234
  const colon = head.indexOf(':');
379
235
  if (colon === -1) return { repository, ref: head };
@@ -520,7 +376,7 @@ async function resolvedContext(taskRef: string, options: InspectionOptions) {
520
376
  }) };
521
377
  const fact = factRead.status === 'valid' ? factRead.fact : null;
522
378
  let issueIdentity: ResourceIdentity | null;
523
- try { issueIdentity = taskIssueIdentity(frontmatter, undefined, options.runtimeVersion); }
379
+ try { issueIdentity = taskIssueIdentity(frontmatter); }
524
380
  catch (error) { return { ok: false as const, output: result('failed', resolved.taskId, null, null, { error: { ...taskIssueIdentityError(error), retryable: false } }) }; }
525
381
  const issueNumber = resourceIdentityNumber(issueIdentity);
526
382
  const prIdentity = fact?.state === 'bound' ? fact.identity.resource : null;
@@ -606,15 +462,6 @@ async function inspectExternalPullRequest(
606
462
  });
607
463
  }
608
464
 
609
- function inspectGitHubPullRequest(client: GitHubClient, repository: string, number: number, cwd: string) {
610
- const fetched = client.json<RemotePullRequest>(['api', `repos/${repository}/pulls/${number}`], { cwd });
611
- if (!fetched.ok) return fetched;
612
- const pullRequest = normalizePullRequest(fetched.value, repository);
613
- return pullRequest
614
- ? { ok: true as const, value: pullRequest }
615
- : { ok: false as const, error: { code: 'PR_IDENTITY_INVALID', message: 'Remote resource is not a valid pull request', retryable: false } };
616
- }
617
-
618
465
  async function inspectPlatformPullRequest(taskRef: string, options: InspectionOptions = {}): Promise<PullRequestResult> {
619
466
  const base = await resolvedContext(taskRef, options);
620
467
  if (!base.ok) return base.output;
@@ -1094,7 +941,7 @@ async function resolveExternalPullRequest(taskRef: string, options: ResolveExter
1094
941
  if (!base.issueIdentity) return externalResult(result('failed', base.resolved.taskId, null, base.prNumber, {
1095
942
  platform: base.context.platform,
1096
943
  capabilities: base.context.capabilities,
1097
- error: { code: 'EXTERNAL_DELIVERY_ISSUE_REQUIRED', message: 'External delivery requires a valid issue_number', retryable: false }
944
+ error: { code: 'EXTERNAL_DELIVERY_ISSUE_REQUIRED', message: 'External delivery requires a valid platform issue identity', retryable: false }
1098
945
  }));
1099
946
  const inspected = base.provider.changeRequests?.listClosing
1100
947
  ? await base.provider.changeRequests.listClosing({
@@ -1583,7 +1430,7 @@ function skipPlatformPullRequestFactUnlocked(
1583
1430
  });
1584
1431
  }
1585
1432
  let issueIdentity: ResourceIdentity | null;
1586
- try { issueIdentity = taskIssueIdentity(frontmatter, undefined, options.runtimeVersion); }
1433
+ try { issueIdentity = taskIssueIdentity(frontmatter); }
1587
1434
  catch (error) { return result('failed', resolved.taskId, null, null, { error: { ...taskIssueIdentityError(error), retryable: false } }); }
1588
1435
  const issueNumber = resourceIdentityNumber(issueIdentity);
1589
1436
  const existing = readPrDeliveryFact(frontmatter, options.runtimeVersion);
@@ -1656,20 +1503,6 @@ async function skipPlatformPullRequestFact(taskRef: string, options: SkipFactOpt
1656
1503
  }
1657
1504
  }
1658
1505
 
1659
- export {
1660
- bindPlatformPullRequest,
1661
- createPlatformPullRequest,
1662
- inspectGitHubPullRequest,
1663
- inspectGitHubIssueClosingChangeRequests,
1664
- inspectPlatformPullRequest,
1665
- inspectPlatformPullRequestByNumber,
1666
- normalizePullRequest,
1667
- resolveExternalPullRequest,
1668
- skipPlatformPullRequestFact,
1669
- selectExternalPullRequest,
1670
- selectPullRequest,
1671
- syncPlatformPullRequest,
1672
- syncPlatformPullRequestInLabels
1673
- };
1506
+ export { bindPlatformPullRequest, createPlatformPullRequest, inspectPlatformPullRequest, inspectPlatformPullRequestByNumber, resolveExternalPullRequest, skipPlatformPullRequestFact, selectExternalPullRequest, selectPullRequest, syncPlatformPullRequest, syncPlatformPullRequestInLabels };
1674
1507
  export type { BindOptions, CreateOptions, ExternalPullRequestResult, ExternalPullRequestSelection, PullRequestPrimaryResult, PullRequestResult, PullRequestSnapshot, ResolveExternalOptions, SkipFactOptions, SyncOptions };
1675
1508
  export { warningResultForPrimary };
@@ -0,0 +1,53 @@
1
+ import type { ResourceIdentity } from './resource-identity.ts';
2
+
3
+ export type PlatformIssueSnapshot = {
4
+ repository: string;
5
+ number: number;
6
+ identity?: ResourceIdentity;
7
+ databaseId: number | null;
8
+ nodeId: string;
9
+ url: string;
10
+ state: 'open' | 'closed';
11
+ title: string;
12
+ body: string;
13
+ labels: string[];
14
+ assignees: string[];
15
+ milestone: string | null;
16
+ issueType: string | null;
17
+ fields: Record<string, string | number | null>;
18
+ };
19
+
20
+ export type PlatformMergeability = {
21
+ state: 'mergeable' | 'conflicting' | 'unknown';
22
+ detail: string | null;
23
+ };
24
+
25
+ export type PlatformChangeRequestSnapshot = {
26
+ repository: string;
27
+ number: number;
28
+ nodeId: string;
29
+ url: string;
30
+ state: 'open' | 'closed';
31
+ title: string;
32
+ body: string;
33
+ draft: boolean;
34
+ head: { repository: string; ref: string; sha: string };
35
+ base: { repository: string; ref: string; sha: string };
36
+ mergedAt: string | null;
37
+ mergeCommitSha: string | null;
38
+ labels: string[];
39
+ assignees: string[];
40
+ milestone: string | null;
41
+ mergeability?: PlatformMergeability;
42
+ identity?: ResourceIdentity;
43
+ };
44
+
45
+ export type PlatformCheckSnapshot = {
46
+ name: string;
47
+ bucket: 'pass' | 'fail' | 'pending' | 'cancel';
48
+ workflow?: string | null;
49
+ conclusion?: string | null;
50
+ detailsUrl?: string | null;
51
+ startedAt?: string | null;
52
+ completedAt?: string | null;
53
+ };