@fitlab-ai/agent-infra 0.12.1 → 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 (303) 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 +18 -10
  21. package/dist/lib/internal/task-create.js +1 -1
  22. package/dist/lib/internal/task-event.js +1 -8
  23. package/dist/lib/internal/task-finalization.js +0 -1
  24. package/dist/lib/internal/task-lifecycle.js +2 -2
  25. package/dist/lib/internal/task-operation-registry.js +4 -20
  26. package/dist/lib/internal/task-review.js +4 -4
  27. package/dist/lib/platform/comment-safety.js +1 -7
  28. package/dist/lib/platform/in-label-sync.js +1 -14
  29. package/dist/lib/platform/issue-comments.js +1 -16
  30. package/dist/lib/platform/issues.js +1 -1
  31. package/dist/lib/platform/operation-coordinator.js +4 -2
  32. package/dist/lib/platform/pr-change-report.js +1 -4
  33. package/dist/lib/platform/pr-checks.js +2 -8
  34. package/dist/lib/platform/provider-validation.js +0 -20
  35. package/dist/lib/platform/pull-requests.js +73 -84
  36. package/dist/lib/platform/releases.js +1 -43
  37. package/dist/lib/platform/verification-required.js +0 -2
  38. package/dist/lib/platform/verification-sync.js +1 -79
  39. package/dist/lib/process-data/normalize.js +1 -185
  40. package/dist/lib/process-data/privacy.js +1 -16
  41. package/dist/lib/process-data/sources.js +2 -279
  42. package/dist/lib/render.js +0 -12
  43. package/dist/lib/run/index.js +15 -10
  44. package/dist/lib/run/skills.js +0 -3
  45. package/dist/lib/sandbox/commands/create.js +40 -53
  46. package/dist/lib/sandbox/commands/list-running.js +0 -18
  47. package/dist/lib/sandbox/commands/start.js +7 -8
  48. package/dist/lib/sandbox/constants.js +0 -6
  49. package/dist/lib/sandbox/control/client.js +0 -1
  50. package/dist/lib/sandbox/control/container-identity.js +1 -16
  51. package/dist/lib/sandbox/control/executor.js +0 -4
  52. package/dist/lib/sandbox/control/identity-sentinel.js +0 -7
  53. package/dist/lib/sandbox/control/lifecycle.js +21 -37
  54. package/dist/lib/sandbox/control/native-file-lock.js +0 -3
  55. package/dist/lib/sandbox/control/server.js +18 -38
  56. package/dist/lib/sandbox/control/state.js +1 -9
  57. package/dist/lib/sandbox/dockerfile.js +0 -6
  58. package/dist/lib/sandbox/engine.js +0 -14
  59. package/dist/lib/sandbox/engines/docker-exact-discovery.js +1 -4
  60. package/dist/lib/sandbox/engines/wsl2-paths.js +0 -3
  61. package/dist/lib/sandbox/recovery.js +24 -26
  62. package/dist/lib/sandbox/removal.js +11 -519
  63. package/dist/lib/sandbox/task-resolver.js +0 -41
  64. package/dist/lib/sandbox/tools.js +1 -4
  65. package/dist/lib/sandbox/workspace-view.js +1 -2
  66. package/dist/lib/server/display.js +0 -6
  67. package/dist/lib/task/activity-intent.js +45 -40
  68. package/dist/lib/task/activity-log.js +9 -16
  69. package/dist/lib/task/artifact-command.js +1 -1
  70. package/dist/lib/task/artifact-lifecycle.js +61 -63
  71. package/dist/lib/task/artifact-name.js +0 -6
  72. package/dist/lib/task/artifact-operations.js +3 -3
  73. package/dist/lib/task/capabilities.js +4 -5
  74. package/dist/lib/task/commands/log.js +0 -6
  75. package/dist/lib/task/commands/status.js +17 -60
  76. package/dist/lib/task/commit-operation.js +28 -39
  77. package/dist/lib/task/completion-facts.js +56 -43
  78. package/dist/lib/task/control-authority.js +23 -256
  79. package/dist/lib/task/control-recovery.js +3 -5
  80. package/dist/lib/task/create.js +2 -4
  81. package/dist/lib/task/decision-intents.js +8 -9
  82. package/dist/lib/task/delivery-target.js +1 -40
  83. package/dist/lib/task/events.js +84 -107
  84. package/dist/lib/task/finalization.js +44 -8
  85. package/dist/lib/task/ledger-intents.js +1 -1
  86. package/dist/lib/task/lifecycle-execution.js +38 -33
  87. package/dist/lib/task/lifecycle.js +49 -41
  88. package/dist/lib/task/local-artifact-finalization.js +1 -4
  89. package/dist/lib/task/manual-validation-evidence.js +1 -37
  90. package/dist/lib/task/platform-operation-recovery.js +67 -24
  91. package/dist/lib/task/pr-delivery-fact.js +2 -5
  92. package/dist/lib/task/qualification-confirmation.js +5 -7
  93. package/dist/lib/task/resolve-ref.js +1 -25
  94. package/dist/lib/task/review-command.js +2 -2
  95. package/dist/lib/task/review-finalization.js +1 -21
  96. package/dist/lib/task/review-fingerprint.js +0 -4
  97. package/dist/lib/task/secure-fs.js +0 -56
  98. package/dist/lib/task/short-id.js +1 -39
  99. package/dist/lib/task/skill-scope.js +0 -1
  100. package/dist/lib/task/verification-engine.js +58 -282
  101. package/dist/lib/task/verification.js +6 -3
  102. package/dist/lib/task/write.js +11 -1
  103. package/lib/agent-clients/adapter.ts +0 -76
  104. package/lib/agent-clients/adapters/antigravity-cli.ts +0 -5
  105. package/lib/agent-clients/adapters/claude-code-credentials.ts +0 -19
  106. package/lib/agent-clients/adapters/claude-code.ts +0 -9
  107. package/lib/agent-clients/adapters/codex.ts +0 -56
  108. package/lib/agent-clients/adapters/opencode.ts +0 -5
  109. package/lib/agent-clients/adapters/traecli.ts +0 -5
  110. package/lib/agent-clients/config.ts +20 -61
  111. package/lib/agent-clients/registry.ts +1 -34
  112. package/lib/agent-clients/schema.ts +0 -18
  113. package/lib/agent-clients/tokens.ts +1 -2
  114. package/lib/agent-clients/types.ts +0 -15
  115. package/lib/decide.ts +1 -0
  116. package/lib/git/worktree-identity.ts +0 -10
  117. package/lib/internal/agent-client.ts +2 -70
  118. package/lib/internal/cli-route-inventory.ts +1 -4
  119. package/lib/internal/manual-validation.ts +16 -9
  120. package/lib/internal/task-create.ts +0 -1
  121. package/lib/internal/task-event.ts +1 -5
  122. package/lib/internal/task-finalization.ts +0 -1
  123. package/lib/internal/task-lifecycle.ts +3 -5
  124. package/lib/internal/task-operation-registry.ts +3 -19
  125. package/lib/internal/task-review.ts +4 -4
  126. package/lib/platform/comment-safety.ts +0 -10
  127. package/lib/platform/github-provider.ts +0 -2
  128. package/lib/platform/in-label-sync.ts +0 -16
  129. package/lib/platform/issue-comments.ts +0 -21
  130. package/lib/platform/issues.ts +0 -2
  131. package/lib/platform/operation-coordinator.ts +4 -2
  132. package/lib/platform/pr-change-report.ts +0 -5
  133. package/lib/platform/pr-checks.ts +1 -10
  134. package/lib/platform/pr-review.ts +1 -1
  135. package/lib/platform/provider-bridge.ts +0 -1
  136. package/lib/platform/provider-loader.ts +1 -5
  137. package/lib/platform/provider-validation.ts +1 -49
  138. package/lib/platform/pull-requests.ts +64 -99
  139. package/lib/platform/releases.ts +1 -40
  140. package/lib/platform/verification-required.ts +0 -2
  141. package/lib/platform/verification-sync.ts +0 -94
  142. package/lib/process-data/normalize.ts +1 -186
  143. package/lib/process-data/privacy.ts +2 -19
  144. package/lib/process-data/sources.ts +3 -283
  145. package/lib/render.ts +0 -15
  146. package/lib/run/index.ts +13 -7
  147. package/lib/run/skills.ts +0 -4
  148. package/lib/sandbox/commands/create.ts +37 -74
  149. package/lib/sandbox/commands/list-running.ts +0 -19
  150. package/lib/sandbox/commands/start.ts +7 -10
  151. package/lib/sandbox/constants.ts +0 -8
  152. package/lib/sandbox/control/client.ts +0 -1
  153. package/lib/sandbox/control/container-identity.ts +1 -16
  154. package/lib/sandbox/control/executor.ts +1 -5
  155. package/lib/sandbox/control/identity-sentinel.ts +0 -8
  156. package/lib/sandbox/control/lifecycle.ts +18 -51
  157. package/lib/sandbox/control/native-file-lock.ts +0 -8
  158. package/lib/sandbox/control/protocol.ts +0 -2
  159. package/lib/sandbox/control/server.ts +13 -39
  160. package/lib/sandbox/control/state.ts +1 -10
  161. package/lib/sandbox/dockerfile.ts +0 -11
  162. package/lib/sandbox/engine.ts +0 -17
  163. package/lib/sandbox/engines/docker-exact-discovery.ts +1 -5
  164. package/lib/sandbox/engines/wsl2-paths.ts +0 -4
  165. package/lib/sandbox/recovery.ts +24 -26
  166. package/lib/sandbox/removal.ts +11 -696
  167. package/lib/sandbox/shell.ts +1 -1
  168. package/lib/sandbox/task-resolver.ts +0 -48
  169. package/lib/sandbox/tools.ts +1 -5
  170. package/lib/sandbox/workspace-view.ts +2 -4
  171. package/lib/server/display.ts +0 -14
  172. package/lib/server/process-control.ts +1 -1
  173. package/lib/task/activity-intent.ts +36 -44
  174. package/lib/task/activity-log.ts +9 -22
  175. package/lib/task/artifact-command.ts +1 -1
  176. package/lib/task/artifact-lifecycle.ts +58 -67
  177. package/lib/task/artifact-name.ts +0 -7
  178. package/lib/task/artifact-operations.ts +3 -3
  179. package/lib/task/capabilities.ts +5 -6
  180. package/lib/task/commands/log.ts +0 -7
  181. package/lib/task/commands/status.ts +14 -79
  182. package/lib/task/commit-operation.ts +29 -51
  183. package/lib/task/completion-facts.ts +45 -42
  184. package/lib/task/control-authority.ts +27 -317
  185. package/lib/task/control-recovery.ts +2 -5
  186. package/lib/task/create.ts +2 -5
  187. package/lib/task/decision-intents.ts +5 -5
  188. package/lib/task/delivery-target.ts +0 -40
  189. package/lib/task/events.ts +89 -123
  190. package/lib/task/finalization.ts +41 -9
  191. package/lib/task/ledger-intents.ts +1 -1
  192. package/lib/task/lifecycle-execution.ts +37 -89
  193. package/lib/task/lifecycle.ts +48 -44
  194. package/lib/task/local-artifact-finalization.ts +0 -7
  195. package/lib/task/manual-validation-evidence.ts +1 -47
  196. package/lib/task/platform-operation-recovery.ts +50 -26
  197. package/lib/task/pr-delivery-fact.ts +2 -11
  198. package/lib/task/qualification-confirmation.ts +6 -6
  199. package/lib/task/resolve-ref.ts +0 -23
  200. package/lib/task/review-command.ts +2 -2
  201. package/lib/task/review-finalization.ts +1 -27
  202. package/lib/task/review-fingerprint.ts +0 -8
  203. package/lib/task/secure-fs.ts +0 -42
  204. package/lib/task/short-id.ts +0 -43
  205. package/lib/task/skill-scope.ts +0 -1
  206. package/lib/task/verification-engine.ts +56 -287
  207. package/lib/task/verification.ts +5 -5
  208. package/lib/task/write.ts +12 -1
  209. package/package.json +2 -2
  210. package/templates/.agents/README.en.md +1 -3
  211. package/templates/.agents/README.zh-CN.md +0 -2
  212. package/templates/.agents/rules/README.en.md +0 -1
  213. package/templates/.agents/rules/README.zh-CN.md +0 -1
  214. package/templates/.agents/rules/commit-and-pr.en.md +1 -1
  215. package/templates/.agents/rules/commit-and-pr.zh-CN.md +1 -1
  216. package/templates/.agents/rules/no-mid-flow-questions.en.md +0 -10
  217. package/templates/.agents/rules/no-mid-flow-questions.zh-CN.md +0 -10
  218. package/templates/.agents/rules/pr-sync.en.md +1 -1
  219. package/templates/.agents/rules/pr-sync.zh-CN.md +1 -1
  220. package/templates/.agents/rules/task-management.en.md +3 -3
  221. package/templates/.agents/rules/task-management.zh-CN.md +3 -3
  222. package/templates/.agents/skills/analyze-task/SKILL.en.md +3 -3
  223. package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +3 -3
  224. package/templates/.agents/skills/cancel-task/SKILL.en.md +2 -2
  225. package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +2 -2
  226. package/templates/.agents/skills/code-task/SKILL.en.md +2 -2
  227. package/templates/.agents/skills/code-task/SKILL.zh-CN.md +4 -4
  228. package/templates/.agents/skills/commit/SKILL.en.md +2 -2
  229. package/templates/.agents/skills/commit/SKILL.zh-CN.md +2 -2
  230. package/templates/.agents/skills/commit/reference/commit-orchestration.en.md +6 -6
  231. package/templates/.agents/skills/commit/reference/commit-orchestration.zh-CN.md +6 -6
  232. package/templates/.agents/skills/plan-task/SKILL.en.md +2 -2
  233. package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +2 -2
  234. package/templates/.agents/skills/review-analysis/SKILL.en.md +3 -3
  235. package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +3 -3
  236. package/templates/.agents/skills/review-code/SKILL.en.md +5 -5
  237. package/templates/.agents/skills/review-code/SKILL.zh-CN.md +5 -5
  238. package/templates/.agents/skills/review-plan/SKILL.en.md +3 -3
  239. package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +3 -3
  240. package/templates/.agents/skills/review-pr/SKILL.en.md +4 -4
  241. package/templates/.agents/skills/review-pr/SKILL.zh-CN.md +4 -4
  242. package/templates/.agents/skills/run-manual-validation/SKILL.en.md +1 -1
  243. package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +1 -1
  244. package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +3 -75
  245. package/templates/.agents/templates/task.en.md +1 -0
  246. package/templates/.agents/templates/task.zh-CN.md +1 -0
  247. package/templates/.claude/settings.json +0 -24
  248. package/templates/.codex/hooks.json +0 -50
  249. package/dist/lib/agent-clients/adapters/codex-lifecycle/app-server.js +0 -582
  250. package/dist/lib/agent-clients/adapters/codex-lifecycle/binding.js +0 -114
  251. package/dist/lib/agent-clients/adapters/codex-lifecycle/build-identity.js +0 -237
  252. package/dist/lib/agent-clients/adapters/codex-lifecycle/cli.js +0 -443
  253. package/dist/lib/agent-clients/adapters/codex-lifecycle/evidence.js +0 -236
  254. package/dist/lib/agent-clients/adapters/codex-lifecycle/recovery.js +0 -292
  255. package/dist/lib/agent-clients/adapters/codex-lifecycle/store.js +0 -267
  256. package/dist/lib/agent-clients/adapters/codex-orchestration.js +0 -466
  257. package/dist/lib/internal/task-orchestration.js +0 -71
  258. package/dist/lib/platform/verification.js +0 -6
  259. package/dist/lib/run/prompt.js +0 -2
  260. package/dist/lib/sandbox/engines/authority.js +0 -289
  261. package/dist/lib/task/delegation-receipts.js +0 -410
  262. package/dist/lib/task/orchestration.js +0 -1233
  263. package/dist/lib/task/verification-checks.js +0 -9
  264. package/dist/lib/task/workspace-snapshot.js +0 -121
  265. package/dist/lifecycle-build-manifest.json +0 -6
  266. package/lib/agent-clients/adapters/codex-lifecycle/app-server.ts +0 -688
  267. package/lib/agent-clients/adapters/codex-lifecycle/binding.ts +0 -147
  268. package/lib/agent-clients/adapters/codex-lifecycle/build-identity.ts +0 -349
  269. package/lib/agent-clients/adapters/codex-lifecycle/cli.ts +0 -506
  270. package/lib/agent-clients/adapters/codex-lifecycle/evidence.ts +0 -385
  271. package/lib/agent-clients/adapters/codex-lifecycle/manifest-files.json +0 -41
  272. package/lib/agent-clients/adapters/codex-lifecycle/recovery.ts +0 -418
  273. package/lib/agent-clients/adapters/codex-lifecycle/store.ts +0 -354
  274. package/lib/agent-clients/adapters/codex-orchestration.ts +0 -630
  275. package/lib/internal/task-orchestration.ts +0 -74
  276. package/lib/platform/verification.ts +0 -11
  277. package/lib/run/prompt.ts +0 -1
  278. package/lib/sandbox/engines/authority.ts +0 -365
  279. package/lib/task/delegation-receipts.ts +0 -547
  280. package/lib/task/orchestration.ts +0 -1651
  281. package/lib/task/verification-checks.ts +0 -10
  282. package/lib/task/workspace-snapshot.ts +0 -144
  283. package/templates/.agents/hooks/lifecycle-delegation.js +0 -140
  284. package/templates/.agents/rules/lifecycle-orchestration.en.md +0 -44
  285. package/templates/.agents/rules/lifecycle-orchestration.zh-CN.md +0 -44
  286. package/templates/.agents/skills/run-task/SKILL.en.md +0 -29
  287. package/templates/.agents/skills/run-task/SKILL.zh-CN.md +0 -29
  288. package/templates/.agents/skills/run-task/config/verify.en.json +0 -10
  289. package/templates/.agents/skills/run-task/config/verify.zh-CN.json +0 -10
  290. package/templates/.agents/skills/run-task/reference/host-validation.en.md +0 -24
  291. package/templates/.agents/skills/run-task/reference/host-validation.zh-CN.md +0 -24
  292. package/templates/.claude/agents/agent-infra-lifecycle-executor.en.md +0 -6
  293. package/templates/.claude/agents/agent-infra-lifecycle-executor.zh-CN.md +0 -6
  294. package/templates/.claude/agents/agent-infra-lifecycle-reviewer.en.md +0 -6
  295. package/templates/.claude/agents/agent-infra-lifecycle-reviewer.zh-CN.md +0 -6
  296. package/templates/.claude/commands/run-task.en.md +0 -8
  297. package/templates/.claude/commands/run-task.zh-CN.md +0 -8
  298. package/templates/.codex/agents/agent-infra-lifecycle-executor.en.toml +0 -3
  299. package/templates/.codex/agents/agent-infra-lifecycle-executor.zh-CN.toml +0 -3
  300. package/templates/.codex/agents/agent-infra-lifecycle-reviewer.en.toml +0 -3
  301. package/templates/.codex/agents/agent-infra-lifecycle-reviewer.zh-CN.toml +0 -3
  302. package/templates/.opencode/commands/run-task.en.md +0 -11
  303. package/templates/.opencode/commands/run-task.zh-CN.md +0 -11
@@ -1,37 +1,42 @@
1
- import { inspectOrchestrationStage, pauseOrchestration, planOrchestrationStageCompletion } from './orchestration.js';
2
- import { resolveTaskRef } from './resolve-ref.js';
3
- function failure(mode, code, message) {
4
- return { ok: false, mode, completionPlan: null, error: { code, message } };
5
- }
6
- function inspectLifecycleExecution(taskRef, request, options = {}) {
7
- const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
8
- if (!resolved.ok)
9
- return failure(request.mode, resolved.code, resolved.message);
10
- if (request.mode === 'standalone') {
11
- return { ok: true, mode: request.mode, completionPlan: null, error: null };
12
- }
13
- if (request.agent) {
14
- const planned = planOrchestrationStageCompletion(taskRef, {
15
- ...request.identity,
16
- agent: request.agent
17
- }, options);
18
- if (planned.plan) {
19
- return { ok: true, mode: request.mode, completionPlan: planned.plan, error: null };
20
- }
21
- return failure(request.mode, planned.result.error?.code ?? 'ORCHESTRATION_PROVENANCE_MISMATCH', planned.result.error?.message ?? 'orchestration provenance validation failed');
1
+ import { parseTypedTaskFrontmatter } from './frontmatter.js';
2
+ const labels = {
3
+ 'Analyze Task': 'analyze', 'Review Analysis': 'review-analysis', 'Plan Task': 'plan',
4
+ 'Review Plan': 'review-plan', 'Code Task': 'code', 'Review Code': 'review-code',
5
+ 'Run Manual Validation': 'validation-run'
6
+ };
7
+ export function lifecycleExecutions(content) {
8
+ const value = parseTypedTaskFrontmatter(content).lifecycle_execution;
9
+ if (value !== undefined) {
10
+ if (typeof value !== 'string')
11
+ throw new Error('invalid lifecycle execution state');
12
+ const state = JSON.parse(value);
13
+ if (!state || typeof state !== 'object' || !('version' in state) || state.version !== 1 || !('open' in state) || !Array.isArray(state.open))
14
+ throw new Error('invalid lifecycle execution state');
15
+ return state.open.map((item) => {
16
+ if (!item || typeof item !== 'object' || !('family' in item) || typeof item.family !== 'string' || ![...Object.values(labels), 'manual-validation', 'other'].includes(item.family) || !('agent' in item) || typeof item.agent !== 'string' || !('startedAt' in item) || typeof item.startedAt !== 'string')
17
+ throw new Error('invalid lifecycle execution identity');
18
+ return item;
19
+ });
22
20
  }
23
- const inspected = inspectOrchestrationStage(taskRef, request.identity, options);
24
- if (inspected.status !== 'failed') {
25
- return { ok: true, mode: request.mode, completionPlan: null, error: null };
26
- }
27
- return failure(request.mode, inspected.error?.code ?? 'ORCHESTRATION_PROVENANCE_MISMATCH', inspected.error?.message ?? 'orchestration provenance validation failed');
21
+ return [];
22
+ }
23
+ export function prReviewExecution(content) {
24
+ const value = parseTypedTaskFrontmatter(content).lifecycle_execution;
25
+ if (value === undefined)
26
+ return { open: null, terminals: [] };
27
+ if (typeof value !== 'string')
28
+ throw new Error('invalid lifecycle execution state');
29
+ const state = JSON.parse(value);
30
+ if (state.version !== 1 || !Array.isArray(state.open))
31
+ throw new Error('invalid lifecycle execution state');
32
+ const pr = state.prReview ?? { open: null, terminals: [] };
33
+ const valid = (item) => item && typeof item.artifact === 'string' && typeof item.head === 'string' && typeof item.agent === 'string';
34
+ if (!Array.isArray(pr.terminals) || (pr.open !== null && (!valid(pr.open) || typeof pr.open.startedAt !== 'string')) || !pr.terminals.every((item) => valid(item) && typeof item.note === 'string'))
35
+ throw new Error('invalid PR review execution state');
36
+ return pr;
28
37
  }
29
- function validateLifecycleExecution(taskRef, request, options = {}) {
30
- const inspected = inspectLifecycleExecution(taskRef, request, options);
31
- if (inspected.ok || request.mode === 'standalone' || request.dryRun)
32
- return inspected;
33
- const paused = pauseOrchestration(taskRef, inspected.error?.code ?? 'ORCHESTRATION_PROVENANCE_MISMATCH', inspected.error?.message ?? 'orchestration provenance validation failed', true, options);
34
- return failure(request.mode, paused.run?.pause?.code ?? inspected.error?.code ?? 'ORCHESTRATION_PROVENANCE_MISMATCH', paused.run?.pause?.message ?? inspected.error?.message ?? 'orchestration provenance validation failed');
38
+ export function serializeLifecycleExecution(open, content, prReview) {
39
+ const pr = prReview ?? (content ? prReviewExecution(content) : { open: null, terminals: [] });
40
+ return JSON.stringify({ version: 1, open, prReview: pr });
35
41
  }
36
- export { inspectLifecycleExecution, validateLifecycleExecution };
37
42
  //# sourceMappingURL=lifecycle-execution.js.map
@@ -1,7 +1,7 @@
1
1
  import { createHash } from 'node:crypto';
2
2
  import fs from 'node:fs';
3
3
  import path from 'node:path';
4
- import { appendActivityEntry, locateActivityLog } from './activity-log.js';
4
+ import { activityWarnings, appendActivityEntry, locateActivityLog } from './activity-log.js';
5
5
  import { artifactFamilyCatalog, inspectArtifactDirectory } from './artifact-lifecycle.js';
6
6
  import { parseArtifactName } from './artifact-name.js';
7
7
  import { parseTypedTaskFrontmatter, updateTaskFrontmatter } from './frontmatter.js';
@@ -15,7 +15,7 @@ import { taskIssueIdentity } from '../platform/task-identities.js';
15
15
  import { resourceIdentityEquals } from '../platform/resource-identity.js';
16
16
  import { resolveProjectTaskWorkspaceRoot } from './workspace-root.js';
17
17
  const lifecycleIntentCatalog = [
18
- 'block', 'activate', 'cancel', 'complete', 'close-codescan', 'close-dependabot', 'restore', 'recover-started'
18
+ 'block', 'activate', 'cancel', 'complete', 'close-codescan', 'close-dependabot', 'restore'
19
19
  ];
20
20
  const lifecycleFailureCatalog = [
21
21
  'LIFECYCLE_DIRECTORY_TRANSFER_FAILED',
@@ -82,9 +82,6 @@ function failed(request, error, extra = {}) {
82
82
  error, ...extra
83
83
  };
84
84
  }
85
- export function taskLifecycleFailure(request, error, taskId = null) {
86
- return failed(request, error, { taskId });
87
- }
88
85
  function normalizedRequest(request) {
89
86
  if (!request || typeof request !== 'object' || !lifecycleIntentCatalog.includes(request.intent)) {
90
87
  return { code: 'LIFECYCLE_PAYLOAD_INVALID', message: 'intent is not in the lifecycle catalog' };
@@ -300,26 +297,11 @@ function taskBoundControlMoveSafety(request, controlRoot, options, taskId, repoR
300
297
  }
301
298
  return null;
302
299
  }
303
- function orchestrationMoveSafety(sourcePath) {
304
- const orchestrationPath = path.join(sourcePath, '.runtime', 'orchestration.json');
305
- if (!pathExists(orchestrationPath))
306
- return null;
307
- try {
308
- const run = JSON.parse(fs.readFileSync(orchestrationPath, 'utf8'));
309
- if (run.status === 'running' || run.pendingDelegation !== null && run.pendingDelegation !== undefined) {
310
- return { code: 'LIFECYCLE_RUNTIME_MOVE_UNSAFE', message: 'task orchestration has an active or pending delegation; settle it before moving the task directory' };
311
- }
312
- }
313
- catch {
314
- return { code: 'LIFECYCLE_RUNTIME_MOVE_UNSAFE', message: 'task orchestration state is unreadable; refusing to move the task directory' };
315
- }
316
- return null;
317
- }
318
- function runtimeMoveSafety(request, movingTaskDirectory, options, taskId, repoRoot, sourcePath, validateControlManifestIdentity) {
300
+ function runtimeMoveSafety(request, movingTaskDirectory, options, taskId, repoRoot, validateControlManifestIdentity) {
319
301
  if (!movingTaskDirectory)
320
302
  return null;
321
303
  const controlSafety = taskBoundControlMoveSafety(request, options.controlRoot, options, taskId, repoRoot, validateControlManifestIdentity);
322
- return controlSafety ?? orchestrationMoveSafety(sourcePath);
304
+ return controlSafety;
323
305
  }
324
306
  function targetContainsTask(targetPath, taskId, request) {
325
307
  try {
@@ -356,16 +338,26 @@ function actionAndNote(request, restoredFiles = 0) {
356
338
  return { action: 'Close Codescan', note: `Code Scanning alert #${request.alertNumber} dismissed: ${request.reason}` };
357
339
  if (request.intent === 'close-dependabot')
358
340
  return { action: 'Close Dependabot', note: `Dependabot alert #${request.alertNumber} dismissed: ${request.reason}` };
359
- if (request.intent === 'recover-started')
360
- return { action: 'Recover Started', note: 'automatic recovery' };
361
341
  return { action: 'Restore Task', note: `Restored ${restoredFiles} files from Issue #${request.issueNumber}` };
362
342
  }
343
+ function completionKeys(content) {
344
+ const value = parseTypedTaskFrontmatter(content).lifecycle_transition;
345
+ if (value === undefined)
346
+ return [];
347
+ if (typeof value !== 'string')
348
+ throw new Error('invalid lifecycle transition');
349
+ const state = JSON.parse(value);
350
+ if (!state || typeof state !== 'object' || !('completedKeys' in state) || !Array.isArray(state.completedKeys))
351
+ throw new Error('invalid lifecycle transition');
352
+ return state.completedKeys.map((key) => {
353
+ if (!key || typeof key !== 'object' || !('action' in key) || typeof key.action !== 'string' || !('agent' in key) || typeof key.agent !== 'string' || !('note' in key) || typeof key.note !== 'string')
354
+ throw new Error('invalid lifecycle completion identity');
355
+ return key;
356
+ });
357
+ }
363
358
  function matchingCompletion(content, request, restoredFiles = 0) {
364
- const section = locateActivityLog(content);
365
- if (!section)
366
- return false;
367
359
  const identity = actionAndNote(request, restoredFiles);
368
- return section.entries.some((entry) => entry.step === identity.action && entry.agent === request.agent && entry.note === identity.note);
360
+ return completionKeys(content).some((entry) => entry.action === identity.action && entry.agent === request.agent && entry.note === identity.note);
369
361
  }
370
362
  function validateRestoreStaging(request, repoRoot, taskId) {
371
363
  let workspace;
@@ -424,13 +416,17 @@ function validateRestoreStaging(request, repoRoot, taskId) {
424
416
  }
425
417
  return { stagingDir, fileCount: entries.filter((entry) => !entry.name.startsWith('.')).length };
426
418
  }
427
- function mutationsFor(request, content, metadata, restoredFiles, allowMissingActivityLog = false) {
428
- const section = locateActivityLog(content) ?? (allowMissingActivityLog ? { heading: 'Activity Log', body: '', entries: [] } : null);
419
+ function lifecycleActivityMutation(content, request, metadata, identity, allowMissing) {
420
+ const section = locateActivityLog(content) ?? (allowMissing ? { heading: 'Activity Log', body: '', entries: [] } : null);
429
421
  if (!section)
430
- return { code: 'LIFECYCLE_LOG_MISSING', message: 'task has no unique Activity Log section' };
431
- const identity = actionAndNote(request, restoredFiles);
422
+ return [];
432
423
  let body = appendActivityEntry(section, { time: metadata.timestamp, step: `${identity.action} [started]`, agent: request.agent, note: 'started' });
433
424
  body = appendActivityEntry({ ...section, body }, { time: metadata.timestamp, step: identity.action, agent: request.agent, note: identity.note });
425
+ return [{ kind: 'section', aliases: ['活动日志', 'Activity Log'], heading: section.heading, body }];
426
+ }
427
+ function mutationsFor(request, content, metadata, restoredFiles, allowMissingActivityLog = false) {
428
+ const identity = actionAndNote(request, restoredFiles);
429
+ const activityMutation = lifecycleActivityMutation(content, request, metadata, identity, allowMissingActivityLog);
434
430
  const set = {};
435
431
  const remove = [];
436
432
  if (request.intent === 'block') {
@@ -458,9 +454,13 @@ function mutationsFor(request, content, metadata, restoredFiles, allowMissingAct
458
454
  if (frontmatter.target_date === '')
459
455
  set.target_date = metadata.timestamp.slice(0, 10);
460
456
  }
457
+ const completedKeys = completionKeys(content);
458
+ if (!matchingCompletion(content, request, restoredFiles))
459
+ completedKeys.push({ ...identity, agent: request.agent });
460
+ set.lifecycle_transition = JSON.stringify({ completedKeys });
461
461
  const mutations = [
462
462
  { kind: 'frontmatter', set, remove: [...new Set(remove)] },
463
- { kind: 'section', aliases: ['活动日志', 'Activity Log'], heading: section.heading, body }
463
+ ...activityMutation
464
464
  ];
465
465
  if (request.intent === 'block') {
466
466
  mutations.splice(1, 0, {
@@ -595,9 +595,6 @@ export function inspectTaskLifecycleProgress(repoRoot, taskId, agent) {
595
595
  }
596
596
  }
597
597
  function applyTaskLifecycle(requestInput, options = {}) {
598
- if (requestInput.intent === 'recover-started') {
599
- return failed(requestInput, { code: 'LIFECYCLE_RECOVERY_REQUIRES_AUTHORITY', message: 'recover-started must be dispatched through task control authority' });
600
- }
601
598
  const normalized = normalizedRequest(requestInput);
602
599
  if ('code' in normalized)
603
600
  return failed(requestInput, normalized);
@@ -674,7 +671,7 @@ function applyTaskLifecycle(requestInput, options = {}) {
674
671
  && !(request.intent === 'complete' && targetContainsTask(targetPath, taskId, request))) {
675
672
  return failed(request, { code: 'LIFECYCLE_TARGET_CONFLICT', message: `target directory already exists: ${targetPath}` }, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath });
676
673
  }
677
- const moveSafety = runtimeMoveSafety(request, movingTaskDirectory, options, taskId, repoRoot, sourcePath, true);
674
+ const moveSafety = runtimeMoveSafety(request, movingTaskDirectory, options, taskId, repoRoot, true);
678
675
  if (moveSafety)
679
676
  return failed(request, moveSafety, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath });
680
677
  const content = io.readFileSync(currentTaskFile);
@@ -687,8 +684,6 @@ function applyTaskLifecycle(requestInput, options = {}) {
687
684
  }
688
685
  if (frontmatter.id !== taskId)
689
686
  return failed(request, { code: 'LIFECYCLE_IDENTITY_INVALID', message: 'task.md id does not match its lifecycle task id' }, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath });
690
- if (!locateActivityLog(content))
691
- return failed(request, { code: 'LIFECYCLE_LOG_MISSING', message: 'task has no unique Activity Log section' }, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath });
692
687
  if (spec.target === 'active') {
693
688
  const contract = validateCurrentTaskContract(content);
694
689
  if (!contract.ok)
@@ -832,7 +827,7 @@ function applyTaskLifecycle(requestInput, options = {}) {
832
827
  if (fs.existsSync(targetPath) && !fs.existsSync(path.join(sourcePath, 'task.md')))
833
828
  completed.add('directory-moved');
834
829
  if (!completed.has('directory-moved')) {
835
- const moveSafety = runtimeMoveSafety(request, movingTaskDirectory, options, taskId, repoRoot, sourcePath, false);
830
+ const moveSafety = runtimeMoveSafety(request, movingTaskDirectory, options, taskId, repoRoot, false);
836
831
  if (moveSafety)
837
832
  return failed(request, moveSafety, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath, journalPath, completedSteps: [...completed], changed: completed.size > 0, timestamp: journal.metadata.timestamp, agentInfraVersion: journal.metadata.agentInfraVersion });
838
833
  fs.mkdirSync(path.dirname(targetPath), { recursive: true });
@@ -967,5 +962,18 @@ function applyTaskLifecycle(requestInput, options = {}) {
967
962
  journalPath, completedSteps: [...completed], pendingSteps: []
968
963
  };
969
964
  }
970
- export { lifecycleIntentCatalog, lifecycleFailureCatalog, lifecycleProducerCatalog, applyTaskLifecycle, previewTaskCompletion };
965
+ function applyTaskLifecycleWithWarnings(request, options = {}) {
966
+ const resolved = resolveTaskRef(request.taskRef, { repoRoot: options.repoRoot });
967
+ const taskFile = resolved.ok ? resolved.taskMdPath : request.intent === 'restore' ? path.join(request.stagingDir, 'task.md') : null;
968
+ let warnings = [];
969
+ if (taskFile) {
970
+ try {
971
+ warnings = activityWarnings(fs.readFileSync(taskFile, 'utf8'));
972
+ }
973
+ catch { /* The lifecycle operation reports its own read failure. */ }
974
+ }
975
+ const result = applyTaskLifecycle(request, options);
976
+ return result.status === 'failed' || !warnings.length ? result : { ...result, warnings };
977
+ }
978
+ export { lifecycleIntentCatalog, lifecycleFailureCatalog, lifecycleProducerCatalog, applyTaskLifecycleWithWarnings as applyTaskLifecycle, previewTaskCompletion };
971
979
  //# sourceMappingURL=lifecycle.js.map
@@ -113,9 +113,6 @@ function prepareLocalArtifact(request, candidate) {
113
113
  repoRoot: resolved.repoRoot
114
114
  };
115
115
  }
116
- function commitLocalArtifactProvenance(prepared) {
117
- return prepared.result;
118
- }
119
116
  function finalizeLocalArtifact(request) {
120
117
  return prepareLocalArtifact(request).result;
121
118
  }
@@ -124,5 +121,5 @@ function preflightLocalArtifact(request) {
124
121
  const prepared = prepareLocalArtifact(request);
125
122
  return prepared.result;
126
123
  }
127
- export { LOCAL_ARTIFACT_REQUIRED_SECTIONS, finalizeLocalArtifact, preflightLocalArtifact, prepareLocalArtifact, commitLocalArtifactProvenance, canonicalSemanticDigest as semanticDigest, sha256Content, validateLocalArtifact };
124
+ export { LOCAL_ARTIFACT_REQUIRED_SECTIONS, finalizeLocalArtifact, preflightLocalArtifact, prepareLocalArtifact, canonicalSemanticDigest as semanticDigest, sha256Content, validateLocalArtifact };
128
125
  //# sourceMappingURL=local-artifact-finalization.js.map
@@ -1,5 +1,3 @@
1
- import fs from 'node:fs';
2
- import { createHash } from 'node:crypto';
3
1
  import { MANUAL_VALIDATION_SHA40 as SHA40, MANUAL_VALIDATION_TASK_ID as TASK_ID, exactKeys, isRecord, validTimestamp } from './manual-validation-shared.js';
4
2
  const MANUAL_VALIDATION_EVIDENCE_SCHEMA = 'agent-infra/branch-only-validation-evidence';
5
3
  const MANUAL_VALIDATION_EVIDENCE_VERSION = 1;
@@ -92,39 +90,5 @@ function manualValidationEvidenceSummary(evidence) {
92
90
  cleanup: evidence.result.cleanup
93
91
  };
94
92
  }
95
- function validateManualValidationEvidence(value, expected) {
96
- const shape = validateShape(value);
97
- if (!shape.ok)
98
- return shape;
99
- const evidence = shape.value;
100
- if (evidence.result.exitCode !== 0 || evidence.result.signal !== null || evidence.result.cleanup !== 'completed') {
101
- return { ok: false, error: { code: 'MANUAL_VALIDATION_EVIDENCE_UNSUCCESSFUL', message: 'validation must exit 0 without a signal and complete cleanup' } };
102
- }
103
- if (expected) {
104
- if (evidence.taskId !== expected.taskId || evidence.branch !== expected.branch) {
105
- return { ok: false, error: { code: 'MANUAL_VALIDATION_EVIDENCE_IDENTITY_MISMATCH', message: 'evidence task or branch does not match the expected identity' } };
106
- }
107
- if (expected.commit !== undefined && evidence.commit !== expected.commit) {
108
- return { ok: false, error: { code: 'MANUAL_VALIDATION_EVIDENCE_STALE', message: 'evidence commit does not match the current head' } };
109
- }
110
- }
111
- return shape;
112
- }
113
- function readManualValidationEvidence(file, expected) {
114
- let parsed;
115
- try {
116
- parsed = JSON.parse(fs.readFileSync(file, 'utf8'));
117
- }
118
- catch (error) {
119
- const code = error.code === 'ENOENT'
120
- ? 'MANUAL_VALIDATION_EVIDENCE_MISSING'
121
- : 'MANUAL_VALIDATION_EVIDENCE_INVALID';
122
- return { ok: false, error: { code, message: error instanceof Error ? error.message : String(error) } };
123
- }
124
- return validateManualValidationEvidence(parsed, expected);
125
- }
126
- function manualValidationEvidenceDigest(evidence) {
127
- return createHash('sha256').update(JSON.stringify(evidence), 'utf8').digest('hex');
128
- }
129
- export { MANUAL_VALIDATION_EVIDENCE_SCHEMA, MANUAL_VALIDATION_EVIDENCE_VERSION, createManualValidationEvidence, manualValidationEvidenceSummary, manualValidationEvidenceDigest, readManualValidationEvidence, validateManualValidationEvidence };
93
+ export { MANUAL_VALIDATION_EVIDENCE_SCHEMA, MANUAL_VALIDATION_EVIDENCE_VERSION, createManualValidationEvidence, manualValidationEvidenceSummary };
130
94
  //# sourceMappingURL=manual-validation-evidence.js.map
@@ -6,8 +6,8 @@ import { inspectPlatformCommentOperation, syncPlatformComment } from '../platfor
6
6
  import { parseDeliverySummaryContent, renderDeliverySummaryContent, parseCancelSummaryContent, renderCancelSummaryContent } from '../platform/issue-summary-content.js';
7
7
  import { enumerateArtifacts } from './artifacts.js';
8
8
  import { inspectPlatformIssue, syncPlatformIssue } from '../platform/issues.js';
9
- import { resolvePlatformProviderContext } from '../platform/context.js';
10
- import { providerOperationContext, providerError, unsupportedProviderOperation } from '../platform/provider-bridge.js';
9
+ import '../platform/context.js';
10
+ import '../platform/provider-bridge.js';
11
11
  import { inLabelMappingDigest } from '../platform/in-label-sync.js';
12
12
  import { bindPlatformPullRequest, recoverCreatedPullRequest, syncPlatformPullRequest } from '../platform/pull-requests.js';
13
13
  import { replayPullRequestSummary } from '../platform/pr-summary.js';
@@ -18,9 +18,10 @@ import { parseResourceIdentity, resourceIdentityEquals } from '../platform/resou
18
18
  import { operationId } from './platform-operation-journal.js';
19
19
  import { canonicalizeSummaryBody } from '../platform/comment-safety.js';
20
20
  import { platformResult } from '../platform/types.js';
21
+ import { mergeOperationWarnings } from './operation-outcome.js';
21
22
  import { resolveTaskRef } from './resolve-ref.js';
22
23
  import { readPlatformOperationJournal, recordPlatformOperation } from './platform-operation-journal.js';
23
- import { providerErrorRetryable } from '../platform/provider-validation.js';
24
+ import '../platform/provider-validation.js';
24
25
  const DEFAULT_RECOVERY_ATTEMPTS = 3;
25
26
  function labelsMatchOwnedPrefix(actual, expected, prefix) {
26
27
  return actual.filter((label) => label.startsWith(prefix)).sort().join('\0')
@@ -29,8 +30,8 @@ function labelsMatchOwnedPrefix(actual, expected, prefix) {
29
30
  function fieldsMatchExpected(actual, expected) {
30
31
  return Object.entries(expected).every(([name, value]) => actual[name] === value);
31
32
  }
32
- function result(status, recovered, pending, error = null) {
33
- 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 } : {}) };
34
35
  }
35
36
  function summaryPayload(taskDir, taskId) {
36
37
  const file = path.join(taskDir, '.delivery-summary.json');
@@ -170,7 +171,8 @@ async function replayIssueMetadata(taskId, operation, agent, cwd, client) {
170
171
  }
171
172
  const issue = after.issue;
172
173
  const metadata = operation.issueMetadata;
173
- 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')));
174
176
  if (failedOperation)
175
177
  return platformResult('blocked', { error: {
176
178
  code: failedOperation.reasonCode || 'PLATFORM_OPERATION_METADATA_UNCONFIRMED',
@@ -204,7 +206,7 @@ async function replayIssueMetadata(taskId, operation, agent, cwd, client) {
204
206
  code: 'PLATFORM_OPERATION_METADATA_UNCONFIRMED',
205
207
  message: 'Issue metadata differs from the requested recovery target after reread', retryable: true
206
208
  } });
207
- return platformResult('no-op', { changed: sync.changed || after.changed });
209
+ return platformResult('no-op', { changed: sync.changed || after.changed, operations: sync.operations });
208
210
  }
209
211
  async function verifyIssueCreate(taskId, operation, cwd, client) {
210
212
  if (!operation.issueCreate)
@@ -291,6 +293,12 @@ function isRecoveryEligible(operation, excludeId) {
291
293
  return operation.state === 'failed'
292
294
  && operation.lastCode !== 'PLATFORM_OPERATION_SUPERSEDED';
293
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
+ }
294
302
  async function recoverPlatformOperationsOnce(taskRef, selection, options) {
295
303
  const resolved = resolveTaskRef(taskRef, options.cwd ? { repoRoot: options.cwd } : {});
296
304
  if (!resolved.ok)
@@ -303,7 +311,14 @@ async function recoverPlatformOperationsOnce(taskRef, selection, options) {
303
311
  const value = error;
304
312
  return result('failed', [], [], { code: value.code || 'PLATFORM_OPERATION_JOURNAL_INVALID', message: value.message || String(error), retryable: false });
305
313
  }
306
- const candidates = journal.operations.filter((operation) => isRecoveryEligible(operation, options.excludeId));
314
+ let candidates;
315
+ try {
316
+ candidates = recoveryCandidates(journal, options);
317
+ }
318
+ catch (error) {
319
+ const value = error;
320
+ return result('blocked', [], [], value);
321
+ }
307
322
  if (!candidates.length)
308
323
  return result('no-op', [], []);
309
324
  const head = candidates[0];
@@ -317,8 +332,9 @@ async function recoverPlatformOperationsOnce(taskRef, selection, options) {
317
332
  const limit = options.limit === undefined ? candidates.length : Math.max(1, options.limit);
318
333
  const recovered = [];
319
334
  const pending = [];
335
+ let warnings = [];
320
336
  const selected = candidates.slice(0, limit);
321
- for (const [index, operation] of selected.entries()) {
337
+ for (const operation of selected) {
322
338
  {
323
339
  try {
324
340
  recordPlatformOperation({
@@ -437,7 +453,7 @@ async function recoverPlatformOperationsOnce(taskRef, selection, options) {
437
453
  ...(operation.pullRequestReview ? { pullRequestReview: operation.pullRequestReview } : {}),
438
454
  dependency: operation.dependency,
439
455
  state: succeeded ? 'succeeded' : 'unknown',
440
- lastCode: remote.error?.code ?? latestOperation?.lastCode ?? null
456
+ lastCode: remote.error?.code ?? remote.operations.find((item) => item.status === 'skipped' && item.reasonCode === 'PUSH_REQUIRED')?.reasonCode ?? (succeeded ? null : latestOperation?.lastCode) ?? null
441
457
  });
442
458
  }
443
459
  catch (error) {
@@ -448,11 +464,12 @@ async function recoverPlatformOperationsOnce(taskRef, selection, options) {
448
464
  pending.push(targetOperation.id);
449
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 });
450
466
  }
467
+ warnings = mergeOperationWarnings(warnings, remote.warnings ?? []);
451
468
  recovered.push(targetOperation.id);
452
469
  }
453
470
  const remaining = candidates.slice(limit).map((operation) => operation.id);
454
471
  pending.push(...remaining);
455
- 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);
456
473
  }
457
474
  async function recoverPlatformOperations(taskRef, selection, options) {
458
475
  const resolved = resolveTaskRef(taskRef, options.cwd ? { repoRoot: options.cwd } : {});
@@ -466,8 +483,14 @@ async function recoverPlatformOperations(taskRef, selection, options) {
466
483
  const value = error;
467
484
  return result('failed', [], [], { code: value.code || 'PLATFORM_OPERATION_JOURNAL_INVALID', message: value.message || String(error), retryable: false });
468
485
  }
469
- const eligible = (operation) => isRecoveryEligible(operation, options.excludeId);
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