@fitlab-ai/agent-infra 0.12.0 → 0.12.2

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 (331) hide show
  1. package/README.md +0 -2
  2. package/README.zh-CN.md +0 -2
  3. package/dist/bin/internal-cli.js +2 -17
  4. package/dist/lib/agent-clients/adapter.js +0 -13
  5. package/dist/lib/agent-clients/adapters/antigravity-cli.js +0 -5
  6. package/dist/lib/agent-clients/adapters/claude-code-credentials.js +0 -17
  7. package/dist/lib/agent-clients/adapters/claude-code.js +0 -9
  8. package/dist/lib/agent-clients/adapters/codex.js +0 -52
  9. package/dist/lib/agent-clients/adapters/opencode.js +0 -5
  10. package/dist/lib/agent-clients/adapters/traecli.js +0 -5
  11. package/dist/lib/agent-clients/config.js +19 -54
  12. package/dist/lib/agent-clients/registry.js +1 -12
  13. package/dist/lib/agent-clients/schema.js +0 -18
  14. package/dist/lib/agent-clients/tokens.js +1 -2
  15. package/dist/lib/agent-clients/types.js +0 -1
  16. package/dist/lib/decide.js +2 -0
  17. package/dist/lib/git/worktree-identity.js +1 -9
  18. package/dist/lib/internal/agent-client.js +2 -62
  19. package/dist/lib/internal/cli-route-inventory.js +1 -5
  20. package/dist/lib/internal/manual-validation.js +29 -27
  21. package/dist/lib/internal/platform-comment.js +56 -7
  22. package/dist/lib/internal/task-create.js +1 -1
  23. package/dist/lib/internal/task-event.js +1 -8
  24. package/dist/lib/internal/task-finalization.js +0 -1
  25. package/dist/lib/internal/task-lifecycle.js +2 -2
  26. package/dist/lib/internal/task-operation-registry.js +4 -20
  27. package/dist/lib/internal/task-review.js +4 -4
  28. package/dist/lib/platform/comment-safety.js +1 -7
  29. package/dist/lib/platform/in-label-sync.js +1 -14
  30. package/dist/lib/platform/issue-comments.js +373 -282
  31. package/dist/lib/platform/issue-summary-content.js +112 -0
  32. package/dist/lib/platform/issues.js +1 -1
  33. package/dist/lib/platform/operation-coordinator.js +4 -2
  34. package/dist/lib/platform/pr-change-report.js +176 -30
  35. package/dist/lib/platform/pr-checks.js +2 -8
  36. package/dist/lib/platform/pr-summary-content.js +54 -0
  37. package/dist/lib/platform/pr-summary.js +318 -284
  38. package/dist/lib/platform/provider-validation.js +0 -20
  39. package/dist/lib/platform/pull-requests.js +73 -84
  40. package/dist/lib/platform/releases.js +1 -43
  41. package/dist/lib/platform/summary-content-pipeline.js +18 -0
  42. package/dist/lib/platform/verification-required.js +0 -2
  43. package/dist/lib/platform/verification-sync.js +1 -79
  44. package/dist/lib/process-data/normalize.js +1 -185
  45. package/dist/lib/process-data/privacy.js +1 -16
  46. package/dist/lib/process-data/sources.js +2 -279
  47. package/dist/lib/render.js +0 -12
  48. package/dist/lib/run/index.js +15 -10
  49. package/dist/lib/run/skills.js +0 -3
  50. package/dist/lib/sandbox/commands/create.js +40 -53
  51. package/dist/lib/sandbox/commands/list-running.js +0 -18
  52. package/dist/lib/sandbox/commands/start.js +7 -8
  53. package/dist/lib/sandbox/constants.js +0 -6
  54. package/dist/lib/sandbox/control/client.js +0 -1
  55. package/dist/lib/sandbox/control/container-identity.js +1 -16
  56. package/dist/lib/sandbox/control/executor.js +0 -4
  57. package/dist/lib/sandbox/control/identity-sentinel.js +0 -7
  58. package/dist/lib/sandbox/control/lifecycle.js +21 -37
  59. package/dist/lib/sandbox/control/native-file-lock.js +0 -3
  60. package/dist/lib/sandbox/control/server.js +18 -38
  61. package/dist/lib/sandbox/control/state.js +1 -9
  62. package/dist/lib/sandbox/dockerfile.js +0 -6
  63. package/dist/lib/sandbox/engine.js +0 -14
  64. package/dist/lib/sandbox/engines/docker-exact-discovery.js +1 -4
  65. package/dist/lib/sandbox/engines/wsl2-paths.js +0 -3
  66. package/dist/lib/sandbox/recovery.js +24 -26
  67. package/dist/lib/sandbox/removal.js +67 -518
  68. package/dist/lib/sandbox/task-resolver.js +0 -41
  69. package/dist/lib/sandbox/tools.js +1 -4
  70. package/dist/lib/sandbox/workspace-view.js +1 -2
  71. package/dist/lib/server/display.js +0 -6
  72. package/dist/lib/task/activity-intent.js +45 -40
  73. package/dist/lib/task/activity-log.js +9 -16
  74. package/dist/lib/task/artifact-command.js +1 -1
  75. package/dist/lib/task/artifact-lifecycle.js +61 -63
  76. package/dist/lib/task/artifact-name.js +0 -6
  77. package/dist/lib/task/artifact-operations.js +3 -3
  78. package/dist/lib/task/capabilities.js +4 -5
  79. package/dist/lib/task/commands/log.js +0 -6
  80. package/dist/lib/task/commands/status.js +17 -60
  81. package/dist/lib/task/commit-operation.js +28 -39
  82. package/dist/lib/task/completion-facts.js +56 -43
  83. package/dist/lib/task/control-authority.js +23 -256
  84. package/dist/lib/task/control-recovery.js +3 -5
  85. package/dist/lib/task/create.js +2 -4
  86. package/dist/lib/task/decision-intents.js +8 -9
  87. package/dist/lib/task/delivery-target.js +1 -40
  88. package/dist/lib/task/events.js +84 -107
  89. package/dist/lib/task/finalization.js +60 -15
  90. package/dist/lib/task/ledger-intents.js +1 -1
  91. package/dist/lib/task/lifecycle-execution.js +38 -33
  92. package/dist/lib/task/lifecycle.js +49 -41
  93. package/dist/lib/task/local-artifact-finalization.js +1 -4
  94. package/dist/lib/task/manual-validation-evidence.js +1 -37
  95. package/dist/lib/task/platform-operation-recovery.js +151 -108
  96. package/dist/lib/task/pr-delivery-fact.js +2 -5
  97. package/dist/lib/task/qualification-confirmation.js +5 -7
  98. package/dist/lib/task/resolve-ref.js +1 -25
  99. package/dist/lib/task/review-command.js +2 -2
  100. package/dist/lib/task/review-finalization.js +1 -21
  101. package/dist/lib/task/review-fingerprint.js +0 -4
  102. package/dist/lib/task/secure-fs.js +0 -56
  103. package/dist/lib/task/short-id.js +1 -39
  104. package/dist/lib/task/skill-scope.js +0 -1
  105. package/dist/lib/task/verification-engine.js +58 -282
  106. package/dist/lib/task/verification.js +6 -3
  107. package/dist/lib/task/write.js +11 -1
  108. package/lib/agent-clients/adapter.ts +0 -76
  109. package/lib/agent-clients/adapters/antigravity-cli.ts +0 -5
  110. package/lib/agent-clients/adapters/claude-code-credentials.ts +0 -19
  111. package/lib/agent-clients/adapters/claude-code.ts +0 -9
  112. package/lib/agent-clients/adapters/codex.ts +0 -56
  113. package/lib/agent-clients/adapters/opencode.ts +0 -5
  114. package/lib/agent-clients/adapters/traecli.ts +0 -5
  115. package/lib/agent-clients/config.ts +20 -61
  116. package/lib/agent-clients/registry.ts +1 -34
  117. package/lib/agent-clients/schema.ts +0 -18
  118. package/lib/agent-clients/tokens.ts +1 -2
  119. package/lib/agent-clients/types.ts +0 -15
  120. package/lib/decide.ts +1 -0
  121. package/lib/git/worktree-identity.ts +0 -10
  122. package/lib/internal/agent-client.ts +2 -70
  123. package/lib/internal/cli-route-inventory.ts +1 -4
  124. package/lib/internal/manual-validation.ts +28 -27
  125. package/lib/internal/platform-comment.ts +46 -4
  126. package/lib/internal/task-create.ts +0 -1
  127. package/lib/internal/task-event.ts +1 -5
  128. package/lib/internal/task-finalization.ts +0 -1
  129. package/lib/internal/task-lifecycle.ts +3 -5
  130. package/lib/internal/task-operation-registry.ts +3 -19
  131. package/lib/internal/task-review.ts +4 -4
  132. package/lib/platform/comment-safety.ts +0 -10
  133. package/lib/platform/github-provider.ts +0 -2
  134. package/lib/platform/in-label-sync.ts +0 -16
  135. package/lib/platform/issue-comments.ts +478 -301
  136. package/lib/platform/issue-summary-content.ts +132 -0
  137. package/lib/platform/issues.ts +0 -2
  138. package/lib/platform/operation-coordinator.ts +4 -2
  139. package/lib/platform/pr-change-report.ts +165 -25
  140. package/lib/platform/pr-checks.ts +1 -10
  141. package/lib/platform/pr-review.ts +1 -1
  142. package/lib/platform/pr-summary-content.ts +60 -0
  143. package/lib/platform/pr-summary.ts +364 -257
  144. package/lib/platform/provider-bridge.ts +0 -1
  145. package/lib/platform/provider-loader.ts +1 -5
  146. package/lib/platform/provider-validation.ts +1 -49
  147. package/lib/platform/pull-requests.ts +64 -99
  148. package/lib/platform/releases.ts +1 -40
  149. package/lib/platform/summary-content-pipeline.ts +23 -0
  150. package/lib/platform/verification-required.ts +0 -2
  151. package/lib/platform/verification-sync.ts +0 -94
  152. package/lib/process-data/normalize.ts +1 -186
  153. package/lib/process-data/privacy.ts +2 -19
  154. package/lib/process-data/sources.ts +3 -283
  155. package/lib/render.ts +0 -15
  156. package/lib/run/index.ts +13 -7
  157. package/lib/run/skills.ts +0 -4
  158. package/lib/sandbox/commands/create.ts +37 -74
  159. package/lib/sandbox/commands/list-running.ts +0 -19
  160. package/lib/sandbox/commands/start.ts +7 -10
  161. package/lib/sandbox/constants.ts +0 -8
  162. package/lib/sandbox/control/client.ts +0 -1
  163. package/lib/sandbox/control/container-identity.ts +1 -16
  164. package/lib/sandbox/control/executor.ts +1 -5
  165. package/lib/sandbox/control/identity-sentinel.ts +0 -8
  166. package/lib/sandbox/control/lifecycle.ts +18 -51
  167. package/lib/sandbox/control/native-file-lock.ts +0 -8
  168. package/lib/sandbox/control/protocol.ts +0 -2
  169. package/lib/sandbox/control/server.ts +13 -39
  170. package/lib/sandbox/control/state.ts +1 -10
  171. package/lib/sandbox/dockerfile.ts +0 -11
  172. package/lib/sandbox/engine.ts +0 -17
  173. package/lib/sandbox/engines/docker-exact-discovery.ts +1 -5
  174. package/lib/sandbox/engines/wsl2-paths.ts +0 -4
  175. package/lib/sandbox/recovery.ts +24 -26
  176. package/lib/sandbox/removal.ts +58 -687
  177. package/lib/sandbox/shell.ts +1 -1
  178. package/lib/sandbox/task-resolver.ts +0 -48
  179. package/lib/sandbox/tools.ts +1 -5
  180. package/lib/sandbox/workspace-view.ts +2 -4
  181. package/lib/server/display.ts +0 -14
  182. package/lib/server/process-control.ts +1 -1
  183. package/lib/task/activity-intent.ts +36 -44
  184. package/lib/task/activity-log.ts +9 -22
  185. package/lib/task/artifact-command.ts +1 -1
  186. package/lib/task/artifact-lifecycle.ts +58 -67
  187. package/lib/task/artifact-name.ts +0 -7
  188. package/lib/task/artifact-operations.ts +3 -3
  189. package/lib/task/capabilities.ts +5 -6
  190. package/lib/task/commands/log.ts +0 -7
  191. package/lib/task/commands/status.ts +14 -79
  192. package/lib/task/commit-operation.ts +29 -51
  193. package/lib/task/completion-facts.ts +45 -42
  194. package/lib/task/control-authority.ts +27 -317
  195. package/lib/task/control-recovery.ts +2 -5
  196. package/lib/task/create.ts +2 -5
  197. package/lib/task/decision-intents.ts +5 -5
  198. package/lib/task/delivery-target.ts +0 -40
  199. package/lib/task/events.ts +89 -123
  200. package/lib/task/finalization.ts +52 -15
  201. package/lib/task/ledger-intents.ts +1 -1
  202. package/lib/task/lifecycle-execution.ts +37 -89
  203. package/lib/task/lifecycle.ts +48 -44
  204. package/lib/task/local-artifact-finalization.ts +0 -7
  205. package/lib/task/manual-validation-evidence.ts +1 -47
  206. package/lib/task/platform-operation-recovery.ts +117 -104
  207. package/lib/task/pr-delivery-fact.ts +2 -11
  208. package/lib/task/qualification-confirmation.ts +6 -6
  209. package/lib/task/resolve-ref.ts +0 -23
  210. package/lib/task/review-command.ts +2 -2
  211. package/lib/task/review-finalization.ts +1 -27
  212. package/lib/task/review-fingerprint.ts +0 -8
  213. package/lib/task/secure-fs.ts +0 -42
  214. package/lib/task/short-id.ts +0 -43
  215. package/lib/task/skill-scope.ts +0 -1
  216. package/lib/task/verification-engine.ts +56 -287
  217. package/lib/task/verification.ts +5 -5
  218. package/lib/task/write.ts +12 -1
  219. package/package.json +2 -2
  220. package/templates/.agents/README.en.md +1 -3
  221. package/templates/.agents/README.zh-CN.md +0 -2
  222. package/templates/.agents/rules/README.en.md +0 -1
  223. package/templates/.agents/rules/README.zh-CN.md +0 -1
  224. package/templates/.agents/rules/commit-and-pr.en.md +1 -1
  225. package/templates/.agents/rules/commit-and-pr.zh-CN.md +1 -1
  226. package/templates/.agents/rules/no-mid-flow-questions.en.md +0 -10
  227. package/templates/.agents/rules/no-mid-flow-questions.zh-CN.md +0 -10
  228. package/templates/.agents/rules/pr-sync.en.md +4 -2
  229. package/templates/.agents/rules/pr-sync.zh-CN.md +6 -9
  230. package/templates/.agents/rules/task-management.en.md +3 -3
  231. package/templates/.agents/rules/task-management.zh-CN.md +3 -3
  232. package/templates/.agents/skills/analyze-task/SKILL.en.md +3 -3
  233. package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +3 -3
  234. package/templates/.agents/skills/cancel-task/SKILL.en.md +2 -2
  235. package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +2 -2
  236. package/templates/.agents/skills/code-task/SKILL.en.md +2 -2
  237. package/templates/.agents/skills/code-task/SKILL.zh-CN.md +4 -4
  238. package/templates/.agents/skills/commit/SKILL.en.md +2 -2
  239. package/templates/.agents/skills/commit/SKILL.zh-CN.md +2 -2
  240. package/templates/.agents/skills/commit/reference/commit-orchestration.en.md +6 -6
  241. package/templates/.agents/skills/commit/reference/commit-orchestration.zh-CN.md +6 -6
  242. package/templates/.agents/skills/commit/reference/pr-summary-sync.en.md +1 -1
  243. package/templates/.agents/skills/commit/reference/pr-summary-sync.zh-CN.md +1 -1
  244. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.en.md +2 -2
  245. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.zh-CN.md +2 -2
  246. package/templates/.agents/skills/complete-task/SKILL.en.md +10 -25
  247. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +11 -26
  248. package/templates/.agents/skills/create-pr/SKILL.en.md +1 -1
  249. package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +1 -1
  250. package/templates/.agents/skills/create-pr/reference/change-report.en.md +9 -2
  251. package/templates/.agents/skills/create-pr/reference/change-report.zh-CN.md +9 -2
  252. package/templates/.agents/skills/create-pr/reference/comment-publish.en.md +2 -2
  253. package/templates/.agents/skills/create-pr/reference/comment-publish.zh-CN.md +2 -2
  254. package/templates/.agents/skills/create-task/SKILL.en.md +0 -18
  255. package/templates/.agents/skills/create-task/SKILL.zh-CN.md +0 -18
  256. package/templates/.agents/skills/plan-task/SKILL.en.md +2 -2
  257. package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +2 -2
  258. package/templates/.agents/skills/review-analysis/SKILL.en.md +3 -3
  259. package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +3 -3
  260. package/templates/.agents/skills/review-code/SKILL.en.md +5 -5
  261. package/templates/.agents/skills/review-code/SKILL.zh-CN.md +5 -5
  262. package/templates/.agents/skills/review-plan/SKILL.en.md +3 -3
  263. package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +3 -3
  264. package/templates/.agents/skills/review-pr/SKILL.en.md +4 -4
  265. package/templates/.agents/skills/review-pr/SKILL.zh-CN.md +4 -4
  266. package/templates/.agents/skills/run-manual-validation/SKILL.en.md +1 -1
  267. package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +1 -1
  268. package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +3 -75
  269. package/templates/.agents/skills/watch-pr/SKILL.en.md +1 -1
  270. package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +1 -1
  271. package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.en.md +3 -3
  272. package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.zh-CN.md +3 -3
  273. package/templates/.agents/templates/task.en.md +1 -0
  274. package/templates/.agents/templates/task.zh-CN.md +1 -0
  275. package/templates/.claude/settings.json +0 -24
  276. package/templates/.codex/hooks.json +0 -50
  277. package/dist/lib/agent-clients/adapters/codex-lifecycle/app-server.js +0 -582
  278. package/dist/lib/agent-clients/adapters/codex-lifecycle/binding.js +0 -114
  279. package/dist/lib/agent-clients/adapters/codex-lifecycle/build-identity.js +0 -237
  280. package/dist/lib/agent-clients/adapters/codex-lifecycle/cli.js +0 -443
  281. package/dist/lib/agent-clients/adapters/codex-lifecycle/evidence.js +0 -236
  282. package/dist/lib/agent-clients/adapters/codex-lifecycle/recovery.js +0 -292
  283. package/dist/lib/agent-clients/adapters/codex-lifecycle/store.js +0 -267
  284. package/dist/lib/agent-clients/adapters/codex-orchestration.js +0 -466
  285. package/dist/lib/internal/task-orchestration.js +0 -71
  286. package/dist/lib/platform/verification.js +0 -6
  287. package/dist/lib/run/prompt.js +0 -2
  288. package/dist/lib/sandbox/engines/authority.js +0 -289
  289. package/dist/lib/task/delegation-receipts.js +0 -410
  290. package/dist/lib/task/orchestration.js +0 -1233
  291. package/dist/lib/task/verification-checks.js +0 -9
  292. package/dist/lib/task/workspace-snapshot.js +0 -121
  293. package/dist/lifecycle-build-manifest.json +0 -6
  294. package/lib/agent-clients/adapters/codex-lifecycle/app-server.ts +0 -688
  295. package/lib/agent-clients/adapters/codex-lifecycle/binding.ts +0 -147
  296. package/lib/agent-clients/adapters/codex-lifecycle/build-identity.ts +0 -349
  297. package/lib/agent-clients/adapters/codex-lifecycle/cli.ts +0 -506
  298. package/lib/agent-clients/adapters/codex-lifecycle/evidence.ts +0 -385
  299. package/lib/agent-clients/adapters/codex-lifecycle/manifest-files.json +0 -41
  300. package/lib/agent-clients/adapters/codex-lifecycle/recovery.ts +0 -418
  301. package/lib/agent-clients/adapters/codex-lifecycle/store.ts +0 -354
  302. package/lib/agent-clients/adapters/codex-orchestration.ts +0 -630
  303. package/lib/internal/task-orchestration.ts +0 -74
  304. package/lib/platform/verification.ts +0 -11
  305. package/lib/run/prompt.ts +0 -1
  306. package/lib/sandbox/engines/authority.ts +0 -365
  307. package/lib/task/delegation-receipts.ts +0 -547
  308. package/lib/task/orchestration.ts +0 -1651
  309. package/lib/task/verification-checks.ts +0 -10
  310. package/lib/task/workspace-snapshot.ts +0 -144
  311. package/templates/.agents/hooks/lifecycle-delegation.js +0 -140
  312. package/templates/.agents/rules/lifecycle-orchestration.en.md +0 -44
  313. package/templates/.agents/rules/lifecycle-orchestration.zh-CN.md +0 -44
  314. package/templates/.agents/skills/run-task/SKILL.en.md +0 -29
  315. package/templates/.agents/skills/run-task/SKILL.zh-CN.md +0 -29
  316. package/templates/.agents/skills/run-task/config/verify.en.json +0 -10
  317. package/templates/.agents/skills/run-task/config/verify.zh-CN.json +0 -10
  318. package/templates/.agents/skills/run-task/reference/host-validation.en.md +0 -24
  319. package/templates/.agents/skills/run-task/reference/host-validation.zh-CN.md +0 -24
  320. package/templates/.claude/agents/agent-infra-lifecycle-executor.en.md +0 -6
  321. package/templates/.claude/agents/agent-infra-lifecycle-executor.zh-CN.md +0 -6
  322. package/templates/.claude/agents/agent-infra-lifecycle-reviewer.en.md +0 -6
  323. package/templates/.claude/agents/agent-infra-lifecycle-reviewer.zh-CN.md +0 -6
  324. package/templates/.claude/commands/run-task.en.md +0 -8
  325. package/templates/.claude/commands/run-task.zh-CN.md +0 -8
  326. package/templates/.codex/agents/agent-infra-lifecycle-executor.en.toml +0 -3
  327. package/templates/.codex/agents/agent-infra-lifecycle-executor.zh-CN.toml +0 -3
  328. package/templates/.codex/agents/agent-infra-lifecycle-reviewer.en.toml +0 -3
  329. package/templates/.codex/agents/agent-infra-lifecycle-reviewer.zh-CN.toml +0 -3
  330. package/templates/.opencode/commands/run-task.en.md +0 -11
  331. package/templates/.opencode/commands/run-task.zh-CN.md +0 -11
@@ -2,13 +2,15 @@ import fs from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import { createHash } from 'node:crypto';
4
4
  import { execFileSync } from 'node:child_process';
5
- import { inspectPlatformCommentOperation, normalizeCommentContent, syncPlatformComment } from '../platform/issue-comments.js';
5
+ import { inspectPlatformCommentOperation, syncPlatformComment } from '../platform/issue-comments.js';
6
+ import { parseDeliverySummaryContent, renderDeliverySummaryContent, parseCancelSummaryContent, renderCancelSummaryContent } from '../platform/issue-summary-content.js';
7
+ import { enumerateArtifacts } from './artifacts.js';
6
8
  import { inspectPlatformIssue, syncPlatformIssue } from '../platform/issues.js';
7
- import { resolvePlatformProviderContext } from '../platform/context.js';
8
- import { providerOperationContext, providerError, unsupportedProviderOperation } from '../platform/provider-bridge.js';
9
+ import '../platform/context.js';
10
+ import '../platform/provider-bridge.js';
9
11
  import { inLabelMappingDigest } from '../platform/in-label-sync.js';
10
- import { bindPlatformPullRequest, inspectPlatformPullRequest, recoverCreatedPullRequest, syncPlatformPullRequest } from '../platform/pull-requests.js';
11
- import { syncPullRequestSummary } from '../platform/pr-summary.js';
12
+ import { bindPlatformPullRequest, recoverCreatedPullRequest, syncPlatformPullRequest } from '../platform/pull-requests.js';
13
+ import { replayPullRequestSummary } from '../platform/pr-summary.js';
12
14
  import { publishPrReview, readReviewBodyFile } from '../platform/pr-review.js';
13
15
  import { parseTaskFrontmatter } from './frontmatter.js';
14
16
  import { taskIssueIdentity } from '../platform/task-identities.js';
@@ -16,9 +18,10 @@ import { parseResourceIdentity, resourceIdentityEquals } from '../platform/resou
16
18
  import { operationId } from './platform-operation-journal.js';
17
19
  import { canonicalizeSummaryBody } from '../platform/comment-safety.js';
18
20
  import { platformResult } from '../platform/types.js';
21
+ import { mergeOperationWarnings } from './operation-outcome.js';
19
22
  import { resolveTaskRef } from './resolve-ref.js';
20
23
  import { readPlatformOperationJournal, recordPlatformOperation } from './platform-operation-journal.js';
21
- import { providerErrorRetryable } from '../platform/provider-validation.js';
24
+ import '../platform/provider-validation.js';
22
25
  const DEFAULT_RECOVERY_ATTEMPTS = 3;
23
26
  function labelsMatchOwnedPrefix(actual, expected, prefix) {
24
27
  return actual.filter((label) => label.startsWith(prefix)).sort().join('\0')
@@ -27,40 +30,78 @@ function labelsMatchOwnedPrefix(actual, expected, prefix) {
27
30
  function fieldsMatchExpected(actual, expected) {
28
31
  return Object.entries(expected).every(([name, value]) => actual[name] === value);
29
32
  }
30
- function result(status, recovered, pending, error = null) {
31
- return { status, changed: recovered.length > 0, recovered, pending, error: error ? { code: error.code, message: error.message } : null };
33
+ function result(status, recovered, pending, error = null, warnings = []) {
34
+ return { status, changed: recovered.length > 0, recovered, pending, error: error ? { code: error.code, message: error.message } : null, ...(warnings.length ? { warnings } : {}) };
32
35
  }
33
36
  function summaryPayload(taskDir, taskId) {
34
37
  const file = path.join(taskDir, '.delivery-summary.json');
35
38
  if (!fs.existsSync(file))
36
39
  return null;
37
40
  const data = JSON.parse(fs.readFileSync(file, 'utf8'));
38
- const canonical = typeof data.body === 'string' ? canonicalizeSummaryBody(data.body) : null;
39
- if (data.taskId !== taskId || typeof data.body !== 'string' || typeof data.sha256 !== 'string'
40
- || !canonical?.ok || canonical.value !== data.body
41
- || createHash('sha256').update(data.body).digest('hex') !== data.sha256) {
41
+ let content;
42
+ try {
43
+ content = parseDeliverySummaryContent(data.content);
44
+ }
45
+ catch {
46
+ content = null;
47
+ }
48
+ if (data.taskId !== taskId || !content || content.taskId !== taskId || typeof data.contentSha256 !== 'string'
49
+ || createHash('sha256').update(JSON.stringify(content)).digest('hex') !== data.contentSha256) {
42
50
  throw Object.assign(new Error('delivery summary staging record is invalid'), { code: 'SUMMARY_STAGING_INVALID' });
43
51
  }
44
- return { body: data.body, sha256: data.sha256 };
52
+ const canonical = canonicalizeSummaryBody(renderDeliverySummaryContent(content));
53
+ if (!canonical?.ok)
54
+ throw Object.assign(new Error('delivery summary staging body is not canonical'), { code: 'SUMMARY_STAGING_INVALID' });
55
+ return { body: canonical.value, sha256: createHash('sha256').update(canonical.value).digest('hex') };
56
+ }
57
+ function cancellationPayload(taskDir, taskId) {
58
+ const taskPath = path.join(taskDir, 'task.md');
59
+ if (!fs.existsSync(taskPath))
60
+ return null;
61
+ const taskContent = fs.readFileSync(taskPath, 'utf8');
62
+ const frontmatter = parseTaskFrontmatter(taskContent);
63
+ const title = /^#\s+(.+)$/mu.exec(taskContent)?.[1]?.trim() || taskId;
64
+ const artifacts = enumerateArtifacts(taskDir).filter((artifact) => artifact.name !== 'task.md').map((artifact) => artifact.name);
65
+ const content = parseCancelSummaryContent({
66
+ taskId,
67
+ title,
68
+ outcome: 'cancelled',
69
+ reason: frontmatter.cancel_reason,
70
+ cancelledAt: frontmatter.cancelled_at,
71
+ lastCompletedArtifact: artifacts.at(-1) ?? null
72
+ });
73
+ return renderCancelSummaryContent(content);
45
74
  }
46
75
  async function replayComment(taskId, taskDir, operation, agent, cwd, client) {
47
76
  if (operation.kind === 'task-comment') {
48
- return syncPlatformComment(taskId, { kind: 'task', agent, cwd, client, dependency: operation.dependency, skipQueue: true });
77
+ return syncPlatformComment(taskId, { kind: 'task', agent, cwd, client, dependency: operation.dependency, queuedOperation: operation });
49
78
  }
50
79
  if (operation.kind === 'artifact-comment') {
51
- return syncPlatformComment(taskId, { kind: 'artifact', artifact: operation.target, agent, cwd, client, dependency: operation.dependency, skipQueue: true });
80
+ return syncPlatformComment(taskId, { kind: 'artifact', artifact: operation.target, agent, cwd, client, dependency: operation.dependency, queuedOperation: operation });
52
81
  }
53
82
  if (operation.kind === 'summary-comment') {
54
- const summary = summaryPayload(taskDir, taskId);
83
+ let summary;
84
+ try {
85
+ summary = summaryPayload(taskDir, taskId);
86
+ }
87
+ catch (error) {
88
+ return platformResult('failed', { error: { code: 'SUMMARY_STAGING_INVALID', message: error instanceof Error ? error.message : String(error), retryable: false } });
89
+ }
55
90
  if (!summary)
56
- return platformResult('failed', { error: { code: 'SUMMARY_STAGING_MISSING', message: 'delivery summary staging record is unavailable', retryable: false } });
57
- return syncPlatformComment(taskId, {
58
- kind: 'summary', body: summary.body, agent, cwd,
59
- summaryAuthorization: { sha256: summary.sha256 }, dependency: operation.dependency, client, skipQueue: true
60
- });
91
+ return platformResult('blocked', { error: { code: 'SUMMARY_STAGING_MISSING', message: 'delivery summary staging record is unavailable', retryable: true } });
92
+ return syncPlatformComment(taskId, { kind: 'summary', body: summary.body, agent, cwd, client, summaryAuthorization: { sha256: summary.sha256 }, dependency: operation.dependency, queuedOperation: operation });
61
93
  }
62
94
  if (operation.kind === 'cancel-comment') {
63
- return syncPlatformComment(taskId, { kind: 'cancel', agent, cwd, client, dependency: operation.dependency, skipQueue: true });
95
+ let body;
96
+ try {
97
+ body = cancellationPayload(taskDir, taskId);
98
+ }
99
+ catch (error) {
100
+ return platformResult('failed', { error: { code: 'CANCEL_SUMMARY_INVALID', message: error instanceof Error ? error.message : String(error), retryable: false } });
101
+ }
102
+ if (!body)
103
+ return platformResult('blocked', { error: { code: 'CANCEL_SUMMARY_INVALID', message: 'Cancellation facts are unavailable for recovery', retryable: true } });
104
+ return syncPlatformComment(taskId, { kind: 'cancel', body, agent, cwd, client, dependency: operation.dependency, queuedOperation: operation });
64
105
  }
65
106
  return platformResult('failed', { error: { code: 'PLATFORM_OPERATION_UNSUPPORTED', message: `operation kind '${operation.kind}' has no registered recovery handler`, retryable: false } });
66
107
  }
@@ -130,7 +171,8 @@ async function replayIssueMetadata(taskId, operation, agent, cwd, client) {
130
171
  }
131
172
  const issue = after.issue;
132
173
  const metadata = operation.issueMetadata;
133
- const failedOperation = sync.operations.find((item) => item.status === 'failed' || item.status === 'skipped');
174
+ const failedOperation = sync.operations.find((item) => item.status === 'failed' || (item.status === 'skipped'
175
+ && !(sync.capabilities.push === false && ['fields', 'issue-type'].includes(item.name) && item.reasonCode === 'PUSH_REQUIRED')));
134
176
  if (failedOperation)
135
177
  return platformResult('blocked', { error: {
136
178
  code: failedOperation.reasonCode || 'PLATFORM_OPERATION_METADATA_UNCONFIRMED',
@@ -164,7 +206,7 @@ async function replayIssueMetadata(taskId, operation, agent, cwd, client) {
164
206
  code: 'PLATFORM_OPERATION_METADATA_UNCONFIRMED',
165
207
  message: 'Issue metadata differs from the requested recovery target after reread', retryable: true
166
208
  } });
167
- return platformResult('no-op', { changed: sync.changed || after.changed });
209
+ return platformResult('no-op', { changed: sync.changed || after.changed, operations: sync.operations });
168
210
  }
169
211
  async function verifyIssueCreate(taskId, operation, cwd, client) {
170
212
  if (!operation.issueCreate)
@@ -185,64 +227,6 @@ async function verifyIssueCreate(taskId, operation, cwd, client) {
185
227
  } });
186
228
  return platformResult('no-op');
187
229
  }
188
- async function replayPullRequestSummary(taskId, operation, agent, cwd, client) {
189
- let identity;
190
- try {
191
- identity = parseResourceIdentity(JSON.parse(operation.target), 'queued pull-request summary identity');
192
- }
193
- catch {
194
- return platformResult('failed', { error: { code: 'PLATFORM_OPERATION_PAYLOAD_INVALID', message: 'Queued pull-request summary identity is invalid', retryable: false } });
195
- }
196
- const summary = operation.pullRequestSummary;
197
- if (!summary)
198
- return platformResult('blocked', { error: {
199
- code: 'PLATFORM_OPERATION_PAYLOAD_INVALID', message: 'Pull-request summary recovery content is missing', retryable: false
200
- } });
201
- const manualValidationHeading = /^###\s+(?:⏳\s+(?:Manual Validation Pending|人工验证待收尾)|✅\s+(?:Manual Validation Passed|人工验证已通过))\s*$/mu.test(summary.body);
202
- if (manualValidationHeading && !summary.manualValidation)
203
- return platformResult('blocked', { error: {
204
- code: 'MANUAL_VALIDATION_TRANSACTION_REQUIRED', message: 'Queued manual-validation summary has no persisted transaction context', retryable: false
205
- } });
206
- const resolved = await resolvePlatformProviderContext({ cwd, client });
207
- if (!resolved.ok)
208
- return resolved.context;
209
- const listed = resolved.value.provider.comments?.list
210
- ? await resolved.value.provider.comments.list({ context: providerOperationContext(resolved.value), parent: identity })
211
- : unsupportedProviderOperation(resolved.value.provider, 'comments.list');
212
- if (!listed.ok)
213
- return platformResult(providerErrorRetryable(listed.error.code) ? 'blocked' : 'failed', { error: providerError(listed.error, 'PLATFORM_PROVIDER_OPERATION_FAILED') });
214
- const marker = `<!-- sync-pr:${taskId}:summary -->`;
215
- const summaries = listed.value.filter((comment) => normalizeCommentContent(comment.body).split('\n', 1)[0] === marker);
216
- if (summaries.length > 1)
217
- return platformResult('blocked', { error: {
218
- code: 'PR_SUMMARY_MARKER_AMBIGUOUS', message: 'Multiple pull-request comments contain the queued summary marker', retryable: false
219
- } });
220
- if (!summary.manualValidation && summaries.length === 1 && createHash('sha256').update(normalizeCommentContent(summaries[0].body)).digest('hex') === operation.expectedDigest) {
221
- return platformResult('no-op');
222
- }
223
- const bound = await inspectPlatformPullRequest(taskId, { cwd, client });
224
- if (!bound.pullRequest || !resourceIdentityEquals(bound.pullRequest.identity, identity))
225
- return platformResult('blocked', { error: bound.error ?? {
226
- code: 'PLATFORM_OPERATION_IDENTITY_MISMATCH', message: 'Bound pull request differs from the queued summary target', retryable: false
227
- } });
228
- const replayed = await syncPullRequestSummary(taskId, {
229
- agent,
230
- cwd,
231
- client,
232
- body: summary.body,
233
- changeReportFile: summary.changeReportFile,
234
- ...(summary.manualValidation ? { manualValidation: summary.manualValidation } : {}),
235
- primaryResult: 'no_op',
236
- strict: true,
237
- skipQueue: true
238
- });
239
- if ((replayed.status === 'applied' || replayed.status === 'no-op') && !replayed.error && replayed.warnings.length === 0) {
240
- return platformResult(replayed.status, { changed: replayed.changed });
241
- }
242
- return platformResult('blocked', { error: replayed.error ?? {
243
- code: 'PR_SUMMARY_RECOVERY_UNCONFIRMED', message: 'Pull-request summary replay did not confirm the requested remote state', retryable: true
244
- } });
245
- }
246
230
  async function replayPullRequestReview(operation, agent, cwd, client) {
247
231
  const intent = operation.pullRequestReview;
248
232
  if (!intent)
@@ -301,6 +285,20 @@ async function replayPullRequest(taskId, operation, agent, cwd) {
301
285
  }
302
286
  return recoverCreatedPullRequest(taskId, { agent, cwd, base: intent.baseRef || '', head: intent.headRef || '', skipQueue: true });
303
287
  }
288
+ function isRecoveryEligible(operation, excludeId) {
289
+ if (operation.id === excludeId)
290
+ return false;
291
+ if (operation.state === 'queued' || operation.state === 'pending' || operation.state === 'unknown')
292
+ return true;
293
+ return operation.state === 'failed'
294
+ && operation.lastCode !== 'PLATFORM_OPERATION_SUPERSEDED';
295
+ }
296
+ function recoveryCandidates(journal, options) {
297
+ const boundary = options.beforeId ? journal.operations.findIndex((item) => item.id === options.beforeId) : journal.operations.length;
298
+ if (boundary < 0)
299
+ throw Object.assign(new Error('Current platform operation boundary is unavailable'), { code: 'PLATFORM_OPERATION_BOUNDARY_MISSING' });
300
+ return journal.operations.slice(0, boundary).filter((item) => isRecoveryEligible(item, options.excludeId));
301
+ }
304
302
  async function recoverPlatformOperationsOnce(taskRef, selection, options) {
305
303
  const resolved = resolveTaskRef(taskRef, options.cwd ? { repoRoot: options.cwd } : {});
306
304
  if (!resolved.ok)
@@ -313,9 +311,14 @@ async function recoverPlatformOperationsOnce(taskRef, selection, options) {
313
311
  const value = error;
314
312
  return result('failed', [], [], { code: value.code || 'PLATFORM_OPERATION_JOURNAL_INVALID', message: value.message || String(error), retryable: false });
315
313
  }
316
- const candidates = journal.operations.filter((operation) => operation.id !== options.excludeId
317
- && (operation.state === 'queued' || operation.state === 'pending' || operation.state === 'unknown'
318
- || (operation.state === 'failed' && operation.lastCode !== 'PLATFORM_OPERATION_SUPERSEDED')));
314
+ let candidates;
315
+ try {
316
+ candidates = recoveryCandidates(journal, options);
317
+ }
318
+ catch (error) {
319
+ const value = error;
320
+ return result('blocked', [], [], value);
321
+ }
319
322
  if (!candidates.length)
320
323
  return result('no-op', [], []);
321
324
  const head = candidates[0];
@@ -329,8 +332,9 @@ async function recoverPlatformOperationsOnce(taskRef, selection, options) {
329
332
  const limit = options.limit === undefined ? candidates.length : Math.max(1, options.limit);
330
333
  const recovered = [];
331
334
  const pending = [];
335
+ let warnings = [];
332
336
  const selected = candidates.slice(0, limit);
333
- for (const [index, operation] of selected.entries()) {
337
+ for (const operation of selected) {
334
338
  {
335
339
  try {
336
340
  recordPlatformOperation({
@@ -382,9 +386,22 @@ async function recoverPlatformOperationsOnce(taskRef, selection, options) {
382
386
  : operation.kind === 'artifact-comment'
383
387
  ? { kind: 'artifact', artifact: operation.target, agent: options.agent, cwd: resolved.repoRoot, dependency: operation.dependency }
384
388
  : operation.kind === 'summary-comment' ? (() => {
385
- const summary = summaryPayload(resolved.taskDir, resolved.taskId);
386
- return summary ? { kind: 'summary', body: summary.body, agent: options.agent, cwd: resolved.repoRoot, summaryAuthorization: { sha256: summary.sha256 }, dependency: operation.dependency } : null;
387
- })() : { kind: 'cancel', agent: options.agent, cwd: resolved.repoRoot, dependency: operation.dependency };
389
+ try {
390
+ const summary = summaryPayload(resolved.taskDir, resolved.taskId);
391
+ return summary ? { kind: 'summary', body: summary.body, agent: options.agent, cwd: resolved.repoRoot, summaryAuthorization: { sha256: summary.sha256 }, dependency: operation.dependency } : null;
392
+ }
393
+ catch {
394
+ return null;
395
+ }
396
+ })() : (() => {
397
+ try {
398
+ const body = cancellationPayload(resolved.taskDir, resolved.taskId);
399
+ return body ? { kind: 'cancel', body, agent: options.agent, cwd: resolved.repoRoot, dependency: operation.dependency } : null;
400
+ }
401
+ catch {
402
+ return null;
403
+ }
404
+ })();
388
405
  const current = commentOptions ? inspectPlatformCommentOperation(resolved.taskId, commentOptions) : null;
389
406
  if (!current) {
390
407
  pending.push(operation.id);
@@ -407,20 +424,21 @@ async function recoverPlatformOperationsOnce(taskRef, selection, options) {
407
424
  remote = operation.kind === 'issue-create'
408
425
  ? await verifyIssueCreate(resolved.taskId, operation, resolved.repoRoot)
409
426
  : operation.kind === 'pull-request-summary'
410
- ? await replayPullRequestSummary(resolved.taskId, operation, options.agent, resolved.repoRoot, options.client)
427
+ ? await replayPullRequestSummary(resolved.taskId, targetOperation, { agent: options.agent, cwd: resolved.repoRoot, client: options.client })
411
428
  : operation.kind === 'pull-request-review'
412
429
  ? await replayPullRequestReview(operation, options.agent, resolved.repoRoot, options.client)
413
430
  : operation.kind === 'issue-metadata'
414
431
  ? await replayIssueMetadata(resolved.taskId, targetOperation, options.agent, resolved.repoRoot)
415
432
  : operation.kind === 'pull-request'
416
433
  ? await replayPullRequest(resolved.taskId, targetOperation, options.agent, resolved.repoRoot)
417
- : await replayComment(resolved.taskId, resolved.taskDir, operation, options.agent, resolved.repoRoot);
434
+ : await replayComment(resolved.taskId, resolved.taskDir, targetOperation, options.agent, resolved.repoRoot, options.client);
418
435
  }
419
436
  catch (error) {
420
437
  const value = error;
421
438
  remote = platformResult('blocked', { error: { code: value.code || 'PLATFORM_OPERATION_RECOVERY_FAILED', message: value.message || String(error), retryable: true } });
422
439
  }
423
440
  const succeeded = (remote.status === 'applied' || remote.status === 'no-op') && !remote.error;
441
+ const latestOperation = readPlatformOperationJournal(resolved.taskId, resolved.repoRoot).operations.find((item) => item.id === operation.id);
424
442
  try {
425
443
  recordPlatformOperation({
426
444
  taskRef: resolved.taskId,
@@ -435,7 +453,7 @@ async function recoverPlatformOperationsOnce(taskRef, selection, options) {
435
453
  ...(operation.pullRequestReview ? { pullRequestReview: operation.pullRequestReview } : {}),
436
454
  dependency: operation.dependency,
437
455
  state: succeeded ? 'succeeded' : 'unknown',
438
- lastCode: remote.error?.code ?? null
456
+ lastCode: remote.error?.code ?? remote.operations.find((item) => item.status === 'skipped' && item.reasonCode === 'PUSH_REQUIRED')?.reasonCode ?? (succeeded ? null : latestOperation?.lastCode) ?? null
439
457
  });
440
458
  }
441
459
  catch (error) {
@@ -446,11 +464,12 @@ async function recoverPlatformOperationsOnce(taskRef, selection, options) {
446
464
  pending.push(targetOperation.id);
447
465
  return result(remote.status === 'failed' ? 'failed' : 'blocked', recovered, pending, remote.error ?? { code: 'PLATFORM_OPERATION_RECOVERY_UNKNOWN', message: 'Platform operation outcome remains unknown', retryable: true });
448
466
  }
467
+ warnings = mergeOperationWarnings(warnings, remote.warnings ?? []);
449
468
  recovered.push(targetOperation.id);
450
469
  }
451
470
  const remaining = candidates.slice(limit).map((operation) => operation.id);
452
471
  pending.push(...remaining);
453
- return result(pending.length ? 'blocked' : recovered.length ? 'applied' : 'no-op', recovered, pending, pending.length ? { code: 'PLATFORM_OPERATION_RECOVERY_PENDING', message: 'Platform operations remain pending after the recovery budget', retryable: true } : null);
472
+ return result(pending.length ? 'blocked' : recovered.length ? 'applied' : 'no-op', recovered, pending, pending.length ? { code: 'PLATFORM_OPERATION_RECOVERY_PENDING', message: 'Platform operations remain pending after the recovery budget', retryable: true } : null, warnings);
454
473
  }
455
474
  async function recoverPlatformOperations(taskRef, selection, options) {
456
475
  const resolved = resolveTaskRef(taskRef, options.cwd ? { repoRoot: options.cwd } : {});
@@ -464,10 +483,14 @@ async function recoverPlatformOperations(taskRef, selection, options) {
464
483
  const value = error;
465
484
  return result('failed', [], [], { code: value.code || 'PLATFORM_OPERATION_JOURNAL_INVALID', message: value.message || String(error), retryable: false });
466
485
  }
467
- const eligible = (operation) => operation.id !== options.excludeId
468
- && (operation.state === 'queued' || operation.state === 'pending' || operation.state === 'unknown'
469
- || (operation.state === 'failed' && operation.lastCode !== 'PLATFORM_OPERATION_SUPERSEDED'));
470
- const first = initial.operations.filter(eligible);
486
+ let first;
487
+ try {
488
+ first = recoveryCandidates(initial, options);
489
+ }
490
+ catch (error) {
491
+ const value = error;
492
+ return result('blocked', [], [], value);
493
+ }
471
494
  if (!first.length)
472
495
  return result('no-op', [], []);
473
496
  const operationLimit = options.limit === undefined ? first.length : Math.max(1, options.limit);
@@ -476,6 +499,7 @@ async function recoverPlatformOperations(taskRef, selection, options) {
476
499
  return result('failed', [], [], { code: 'PLATFORM_OPERATION_RECOVERY_PAYLOAD_INVALID', message: 'Recovery attempts must be a positive safe integer', retryable: false });
477
500
  }
478
501
  const recovered = [];
502
+ let warnings = [];
479
503
  for (let operationIndex = 0; operationIndex < operationLimit; operationIndex += 1) {
480
504
  let latest;
481
505
  try {
@@ -483,23 +507,32 @@ async function recoverPlatformOperations(taskRef, selection, options) {
483
507
  }
484
508
  catch (error) {
485
509
  const value = error;
486
- return result('failed', recovered, [], { code: value.code || 'PLATFORM_OPERATION_JOURNAL_INVALID', message: value.message || String(error), retryable: false });
510
+ return result('failed', recovered, [], { code: value.code || 'PLATFORM_OPERATION_JOURNAL_INVALID', message: value.message || String(error), retryable: false }, warnings);
511
+ }
512
+ let candidates;
513
+ try {
514
+ candidates = recoveryCandidates(latest, options);
515
+ }
516
+ catch (error) {
517
+ const value = error;
518
+ return result('blocked', recovered, [], value, warnings);
487
519
  }
488
- const head = latest.operations.find(eligible);
520
+ const head = candidates[0];
489
521
  if (!head)
490
522
  break;
491
523
  if (selection !== 'all' && head.dependency !== selection) {
492
- const pending = latest.operations.filter(eligible).map((operation) => operation.id);
524
+ const pending = candidates.map((operation) => operation.id);
493
525
  return result('blocked', recovered, pending, {
494
526
  code: 'PLATFORM_OPERATION_RECOVERY_PENDING',
495
527
  message: 'The first unresolved platform operation belongs to another recovery selection',
496
528
  retryable: true
497
- });
529
+ }, warnings);
498
530
  }
499
531
  let last = null;
500
532
  for (let attempt = 0; attempt < attemptLimit; attempt += 1) {
501
533
  last = await recoverPlatformOperationsOnce(taskRef, selection, { ...options, limit: 1 });
502
534
  recovered.push(...last.recovered.filter((id) => !recovered.includes(id)));
535
+ warnings = mergeOperationWarnings(warnings, last.warnings ?? []);
503
536
  if (last.recovered.length > 0 || last.status === 'applied' || last.status === 'no-op')
504
537
  break;
505
538
  }
@@ -509,24 +542,34 @@ async function recoverPlatformOperations(taskRef, selection, options) {
509
542
  current = readPlatformOperationJournal(resolved.taskId, resolved.repoRoot);
510
543
  }
511
544
  catch {
512
- return result('blocked', recovered, [head.id], last?.error ?? null);
545
+ return result('blocked', recovered, [head.id], last?.error ?? null, warnings);
546
+ }
547
+ let pending;
548
+ try {
549
+ pending = recoveryCandidates(current, options).map((operation) => operation.id);
550
+ }
551
+ catch (error) {
552
+ const value = error;
553
+ return result('blocked', recovered, [], value, warnings);
513
554
  }
514
- const pending = current.operations.filter(eligible).map((operation) => operation.id);
515
555
  return result(last?.status === 'failed' ? 'failed' : 'blocked', recovered, pending, last?.error ?? {
516
556
  code: 'PLATFORM_OPERATION_RECOVERY_PENDING',
517
557
  message: 'The first queued platform operation remains unresolved after this recovery run',
518
558
  retryable: true
519
- });
559
+ }, warnings);
520
560
  }
521
561
  }
522
562
  let remaining;
523
563
  try {
524
- remaining = readPlatformOperationJournal(resolved.taskId, resolved.repoRoot).operations.filter(eligible).map((operation) => operation.id);
564
+ remaining = recoveryCandidates(readPlatformOperationJournal(resolved.taskId, resolved.repoRoot), options).map((operation) => operation.id);
525
565
  }
526
- catch {
527
- remaining = [];
566
+ catch (error) {
567
+ const value = error;
568
+ return result('blocked', recovered, [], { code: value.code || 'PLATFORM_OPERATION_JOURNAL_INVALID', message: value.message || 'Unable to read platform operations' }, warnings);
528
569
  }
529
- return result(remaining.length ? 'blocked' : recovered.length ? 'applied' : 'no-op', recovered, remaining, remaining.length ? { code: 'PLATFORM_OPERATION_RECOVERY_PENDING', message: 'Platform operations remain queued after this recovery run', retryable: true } : null);
570
+ const output = result(remaining.length ? 'blocked' : recovered.length ? 'applied' : 'no-op', recovered, remaining, remaining.length ? { code: 'PLATFORM_OPERATION_RECOVERY_PENDING', message: 'Platform operations remain queued after this recovery run', retryable: true } : null, warnings);
571
+ const degraded = readPlatformOperationJournal(resolved.taskId, resolved.repoRoot).operations.filter((item) => recovered.includes(item.id) && item.lastCode === 'PUSH_REQUIRED').map((item) => ({ id: item.id, reasonCode: item.lastCode }));
572
+ return degraded.length ? { ...output, degraded } : output;
530
573
  }
531
574
  export { fieldsMatchExpected, labelsMatchOwnedPrefix, recoverPlatformOperations };
532
575
  //# sourceMappingURL=platform-operation-recovery.js.map
@@ -1,4 +1,4 @@
1
- import { isResourceIdentity, parseResourceIdentity, resourceIdentityNumber, serializeResourceIdentity } from '../platform/resource-identity.js';
1
+ import { parseResourceIdentity, serializeResourceIdentity } from '../platform/resource-identity.js';
2
2
  const PR_DELIVERY_FACT_KEY = 'pr_delivery_fact';
3
3
  function factError(message) {
4
4
  return Object.assign(new Error(`PR_DELIVERY_FACT_INVALID: ${message}`), { code: 'PR_DELIVERY_FACT_INVALID' });
@@ -132,8 +132,5 @@ function buildBoundFact(input) {
132
132
  }, provenance: { establishedBy: provenanceForSource(input.source) } });
133
133
  }
134
134
  function factFrontmatterMutation(fact) { return { set: { [PR_DELIVERY_FACT_KEY]: encodePrDeliveryFact(fact) } }; }
135
- function identityFromPullRequest(pullRequest) {
136
- return canonicalIdentity(pullRequest);
137
- }
138
- export { PR_DELIVERY_FACT_KEY, buildBoundFact, buildSkippedFact, buildUnboundFact, decodePrDeliveryFact, encodePrDeliveryFact, factFrontmatterMutation, identityFromPullRequest, readPrDeliveryFact, serializeResourceIdentity };
135
+ export { PR_DELIVERY_FACT_KEY, buildBoundFact, buildSkippedFact, buildUnboundFact, decodePrDeliveryFact, encodePrDeliveryFact, factFrontmatterMutation, readPrDeliveryFact, serializeResourceIdentity };
139
136
  //# sourceMappingURL=pr-delivery-fact.js.map
@@ -1,6 +1,6 @@
1
1
  import { createHash } from 'node:crypto';
2
2
  import fs from 'node:fs';
3
- import { appendActivityEntry, locateActivityLog } from './activity-log.js';
3
+ import { activityWarnings, appendActivityEntry, locateActivityLog } from './activity-log.js';
4
4
  import { CONFIRMATION_COLUMNS, CONFIRMATION_HEADINGS, constraintDigest, parseQualificationConfirmations, parseTaskQualification } from './qualification-audit.js';
5
5
  import { findSectionHeading } from './sections.js';
6
6
  import { resolveTaskRef } from './resolve-ref.js';
@@ -58,13 +58,13 @@ function applyQualificationConfirmation(request, options = {}) {
58
58
  const previous = existing.confirmations.find((row) => row.qcrId === constraint.approvalEvidence);
59
59
  if (operation === 'confirm' && constraint.status === 'confirmed') {
60
60
  if (previous?.constraintId === constraint.constraintId && previous.approvedDigest === constraint.digest && previous.rationale === request.rationale.trim()) {
61
- return { status: 'no-op', changed: false, taskId: resolved.taskId, qcrId: previous.qcrId, requestId: previous.requestId, operations: [], error: null };
61
+ return { status: 'no-op', changed: false, taskId: resolved.taskId, qcrId: previous.qcrId, requestId: previous.requestId, operations: [], error: null, warnings: activityWarnings(content) };
62
62
  }
63
63
  return failed('QUALIFICATION_CONFIRMATION_CONFLICT', `constraint '${request.constraintId}' is already confirmed with different evidence`, resolved.taskId);
64
64
  }
65
65
  const transitionStatus = operation === 'supersede' ? 'superseded' : 'open';
66
66
  if (operation !== 'confirm' && constraint.status === transitionStatus && !constraint.approvalEvidence) {
67
- return { status: 'no-op', changed: false, taskId: resolved.taskId, qcrId: null, requestId: null, operations: [], error: null };
67
+ return { status: 'no-op', changed: false, taskId: resolved.taskId, qcrId: null, requestId: null, operations: [], error: null, warnings: activityWarnings(content) };
68
68
  }
69
69
  if (operation !== 'confirm' && constraint.status !== 'confirmed')
70
70
  return failed('QUALIFICATION_CONFIRMATION_REQUIRED', `constraint '${request.constraintId}' is not confirmed`, resolved.taskId);
@@ -90,20 +90,18 @@ function applyQualificationConfirmation(request, options = {}) {
90
90
  } : null;
91
91
  const confirmations = confirmation ? [...existing.confirmations, confirmation] : existing.confirmations;
92
92
  const activity = locateActivityLog(content);
93
- if (!activity)
94
- return failed('QUALIFICATION_ACTIVITY_MISSING', 'task has no unique Activity Log section', resolved.taskId);
95
93
  const mutations = [
96
94
  {
97
95
  kind: 'table-row', action: 'upsert', sectionAliases: ['约束', 'Constraints'], columns: ['constraint_id', 'statement', 'status', 'authority', 'source', 'evidence', 'derived_from', 'approval_evidence'], keyColumn: 'constraint_id', key: constraint.constraintId,
98
96
  values: { statement: constraint.statement, status: operation === 'confirm' ? 'confirmed' : transitionStatus, authority: operation === 'confirm' ? 'human-declared' : constraint.authority, source: constraint.source, evidence: constraint.evidence, derived_from: constraint.derivedFrom.join(','), approval_evidence: qcrId ?? '' }
99
97
  },
100
98
  ...(operation === 'confirm' ? [{ kind: 'section', aliases: CONFIRMATION_HEADINGS, heading: findSectionHeading(content, [...CONFIRMATION_HEADINGS]), body: renderConfirmations(confirmations) }] : []),
101
- { kind: 'section', aliases: ['活动日志', 'Activity Log'], heading: activity.heading, body: appendActivityEntry(activity, { time: metadata.timestamp, step: 'Qualification Confirmation', agent: 'human', note: operation === 'confirm' ? `${request.constraintId} confirmed → ${qcrId}` : `${request.constraintId} ${operation}d` }) }
99
+ ...(activity ? [{ kind: 'section', aliases: ['活动日志', 'Activity Log'], heading: activity.heading, body: appendActivityEntry(activity, { time: metadata.timestamp, step: 'Qualification Confirmation', agent: 'human', note: operation === 'confirm' ? `${request.constraintId} confirmed → ${qcrId}` : `${request.constraintId} ${operation}d` }) }] : [])
102
100
  ];
103
101
  const result = writeTask({ taskRef: resolved.taskId, expectedState: 'active', mutations, dryRun: request.dryRun }, { ...options, taskLocation: { repoRoot: resolved.repoRoot, taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, state: resolved.state }, metadataProvider: () => metadata });
104
102
  if (result.status === 'failed')
105
103
  return failed(result.error.code, result.error.message, result.taskId);
106
- return { status: result.status, changed: result.changed, taskId: result.taskId, qcrId, requestId, operations: result.operations, error: null };
104
+ return { status: result.status, changed: result.changed, taskId: result.taskId, qcrId, requestId, operations: result.operations, error: null, warnings: activityWarnings(content) };
107
105
  }
108
106
  export { applyQualificationConfirmation };
109
107
  //# sourceMappingURL=qualification-confirmation.js.map
@@ -70,30 +70,6 @@ function findTaskMd(workspaceRoot, taskId) {
70
70
  const archived = findInArchive(workspaceRoot, taskId);
71
71
  return archived ? { taskMdPath: archived, state: 'archive' } : null;
72
72
  }
73
- /**
74
- * Enumerate every task directory under the flat workspace states
75
- * (active / blocked / completed) — archive is intentionally excluded so a
76
- * full-tree scan never pulls in cold data. Ordered by state, then task id
77
- * ascending, giving callers a deterministic traversal.
78
- */
79
- function enumerateTaskDirs(repoRoot) {
80
- const out = [];
81
- const workspaceRoot = taskRoot(repoRoot);
82
- for (const sub of FLAT_WORKSPACE_DIRS) {
83
- const base = path.join(workspaceRoot, sub);
84
- if (!fs.existsSync(base))
85
- continue;
86
- for (const entry of fs.readdirSync(base).sort()) {
87
- if (!TASK_ID_RE.test(entry))
88
- continue;
89
- const taskDir = path.join(base, entry);
90
- if (!fs.existsSync(path.join(taskDir, 'task.md')))
91
- continue;
92
- out.push({ taskId: entry, taskDir });
93
- }
94
- }
95
- return out;
96
- }
97
73
  /**
98
74
  * Enumerate every task directory, including the cold YYYY/MM/DD archive.
99
75
  * Invalid directory names, symlinks and entries without task.md are ignored;
@@ -316,5 +292,5 @@ function resolveTaskContext(taskRef, options = {}) {
316
292
  const match = matches[0];
317
293
  return { ok: true, repoRoot, ...match, state: 'active' };
318
294
  }
319
- export { resolveTaskRef, resolveTaskContext, detectRepoRoot, enumerateTaskDirs, enumerateAllTaskDirs, locateHotTaskDirs, TASK_ID_RE };
295
+ export { resolveTaskRef, resolveTaskContext, detectRepoRoot, enumerateAllTaskDirs, locateHotTaskDirs, TASK_ID_RE };
320
296
  //# sourceMappingURL=resolve-ref.js.map
@@ -3,7 +3,7 @@ export function parseReviewCommand(args) {
3
3
  throw new Error('task ref and a supported review operation are required');
4
4
  const values = {};
5
5
  const flags = {
6
- '--stage': 'stage', '--artifact': 'artifact', '--orchestrated': 'orchestrated', '--dry-run': 'dryRun'
6
+ '--stage': 'stage', '--artifact': 'artifact', '--dry-run': 'dryRun'
7
7
  };
8
8
  for (let index = 2; index < args.length; index += 1) {
9
9
  const flag = args[index];
@@ -12,7 +12,7 @@ export function parseReviewCommand(args) {
12
12
  const key = flags[flag];
13
13
  if (Object.hasOwn(values, key))
14
14
  throw new Error(`duplicate option '${flag}'`);
15
- const value = flag === '--orchestrated' || flag === '--dry-run' ? true : args[++index];
15
+ const value = flag === '--dry-run' ? true : args[++index];
16
16
  if (!value || typeof value === 'string' && value.startsWith('--'))
17
17
  throw new Error(`option '${flag}' requires a value`);
18
18
  values[key] = value;
@@ -7,7 +7,6 @@ import { LEDGER_SECTION_MISSING_CODE, LEDGER_SECTION_MISSING_MESSAGE, parseLedge
7
7
  import { finalizeReviewSummaryContent } from './review-artifacts.js';
8
8
  import { inspectDecisionDetailDuplicates } from './decision-details.js';
9
9
  import { resolveTaskRef } from './resolve-ref.js';
10
- import { validateLifecycleExecution } from './lifecycle-execution.js';
11
10
  import { getArtifactSchema } from './artifact-schema.js';
12
11
  import { validateQualificationAudit } from './qualification-audit.js';
13
12
  import { canonicalSemanticDigest, inspectArtifactContract, sha256Content } from './artifact-operations.js';
@@ -74,13 +73,6 @@ function preflightReviewSummary(request, options = {}) {
74
73
  catch (error) {
75
74
  return preflightFailed(request, 'REVIEW_ARTIFACT_NOT_REGULAR', String(error), resolved.taskId);
76
75
  }
77
- const execution = validateLifecycleExecution(request.taskRef, {
78
- mode: request.orchestrated ? 'orchestrated' : 'standalone',
79
- identity: { stage: spec.family, round: parsed.round, artifact: request.artifact, role: 'reviewer' },
80
- dryRun: request.dryRun
81
- }, { repoRoot: options.repoRoot });
82
- if (!execution.ok)
83
- return preflightFailed(request, 'REVIEW_PROVENANCE_INVALID', `${execution.error?.code ?? 'ORCHESTRATION_PROVENANCE_MISMATCH'}: ${execution.error?.message ?? 'orchestration provenance validation failed'}`, resolved.taskId);
84
76
  const artifactSha256 = sha256Content(content);
85
77
  const semanticDigest = canonicalSemanticDigest(content);
86
78
  const detail = inspectDecisionDetailDuplicates(content);
@@ -140,15 +132,6 @@ function prepareReviewSummaryCandidate(request, artifactContent, options = {}) {
140
132
  }
141
133
  const artifactSha256 = sha256Content(artifactContent);
142
134
  const semanticDigest = canonicalSemanticDigest(artifactContent);
143
- const digests = { artifactSha256, semanticDigest };
144
- const execution = validateLifecycleExecution(request.taskRef, {
145
- mode: request.orchestrated ? 'orchestrated' : 'standalone',
146
- identity: { stage: spec.family, round: parsed.round, artifact: request.artifact, role: 'reviewer' },
147
- dryRun: request.dryRun
148
- }, { repoRoot: options.repoRoot });
149
- if (!execution.ok) {
150
- return reject('REVIEW_PROVENANCE_INVALID', `${execution.error?.code ?? 'ORCHESTRATION_PROVENANCE_MISMATCH'}: ${execution.error?.message ?? 'orchestration provenance validation failed'}`);
151
- }
152
135
  try {
153
136
  const ledger = parseLedgerDocument(taskContent);
154
137
  if (!ledger.present)
@@ -194,9 +177,6 @@ function prepareReviewSummaryCandidate(request, artifactContent, options = {}) {
194
177
  },
195
178
  };
196
179
  }
197
- function commitReviewSummaryProvenance(prepared, repoRoot, options = {}) {
198
- return prepared.result;
199
- }
200
180
  function replaceCurrentArtifactAtomically(file, content) {
201
181
  const temporary = path.join(path.dirname(file), `.${path.basename(file)}.${process.pid}.${randomUUID()}.tmp`);
202
182
  try {
@@ -250,5 +230,5 @@ function finalizeReviewSummary(request, options = {}) {
250
230
  return failed(request, 'REVIEW_RECOVERY_COMMIT_FAILED', `cannot update current review artifact: ${error instanceof Error ? error.message : String(error)}`, resolved.taskId);
251
231
  }
252
232
  }
253
- export { finalizeReviewSummary, preflightReviewSummary, prepareReviewSummaryCandidate, commitReviewSummaryProvenance };
233
+ export { finalizeReviewSummary, preflightReviewSummary, prepareReviewSummaryCandidate };
254
234
  //# sourceMappingURL=review-finalization.js.map
@@ -1,10 +1,6 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
3
  import { inspectArtifactDirectory } from "./artifact-lifecycle.js";
4
- const REVIEWED_TREE_RE = /^[a-f0-9]{40,64}$/;
5
- export function parseReviewedGitTree(value) {
6
- return REVIEWED_TREE_RE.test(value) ? value : null;
7
- }
8
4
  // Paths excluded from the post-review coverage gate. Fail-closed: the gate
9
5
  // covers ALL tracked changes by default. This default denylist is EMPTY —
10
6
  // projects opt into exclusions explicitly via review.post_review_exclude_globs.