@fitlab-ai/agent-infra 0.9.15 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (224) hide show
  1. package/README.md +2 -6
  2. package/README.zh-CN.md +2 -6
  3. package/bin/internal-cli.sh +0 -13
  4. package/dist/bin/internal-cli.js +11 -49
  5. package/dist/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.js +6 -2
  6. package/dist/lib/agent-clients/adapters/codex-lifecycle/store.js +43 -3
  7. package/dist/lib/fs/durable-write.js +2 -0
  8. package/dist/lib/internal/cli-route-inventory.js +3 -4
  9. package/dist/lib/internal/manual-validation.js +17 -8
  10. package/dist/lib/internal/platform-comment.js +60 -2
  11. package/dist/lib/internal/task-artifact.js +1 -1
  12. package/dist/lib/internal/task-finalization.js +2 -2
  13. package/dist/lib/internal/task-lifecycle.js +4 -4
  14. package/dist/lib/internal/task-operation-registry.js +22 -13
  15. package/dist/lib/internal/task-orchestration.js +2 -2
  16. package/dist/lib/internal/task-review.js +12 -7
  17. package/dist/lib/platform/activity-recovery.js +159 -0
  18. package/dist/lib/platform/issue-comments.js +89 -31
  19. package/dist/lib/platform/issues.js +1 -1
  20. package/dist/lib/platform/pr-change-report.js +5 -4
  21. package/dist/lib/platform/pr-summary.js +64 -32
  22. package/dist/lib/platform/pull-requests.js +3 -3
  23. package/dist/lib/platform/task-comment-projection.js +70 -0
  24. package/dist/lib/platform/task-identities.js +2 -9
  25. package/dist/lib/platform/task-recovery.js +55 -0
  26. package/dist/lib/platform/verification-sync.js +45 -7
  27. package/dist/lib/pr-review/evidence-grading.js +4 -8
  28. package/dist/lib/sandbox/commands/rm.js +21 -2
  29. package/dist/lib/sandbox/control/executor.js +4 -2
  30. package/dist/lib/sandbox/control/server.js +29 -9
  31. package/dist/lib/sandbox/control/state.js +7 -1
  32. package/dist/lib/sandbox/control/task-workflow.js +1 -1
  33. package/dist/lib/sandbox/control/workflow-executor.js +87 -35
  34. package/dist/lib/sandbox/index.js +1 -2
  35. package/dist/lib/sandbox/removal.js +116 -1011
  36. package/dist/lib/sandbox/workspace-identity.js +17 -1
  37. package/dist/lib/server/process-state.js +17 -5
  38. package/dist/lib/task/artifact-command.js +11 -30
  39. package/dist/lib/task/artifact-operations.js +17 -231
  40. package/dist/lib/task/artifact-recovery.js +476 -0
  41. package/dist/lib/task/artifact-repair-intent.js +90 -39
  42. package/dist/lib/task/commit-operation.js +31 -4
  43. package/dist/lib/task/control-authority.js +39 -8
  44. package/dist/lib/task/control-recovery.js +14 -2
  45. package/dist/lib/task/create-service.js +1 -8
  46. package/dist/lib/task/delegation-receipts.js +64 -3
  47. package/dist/lib/task/events.js +165 -23
  48. package/dist/lib/task/human-override.js +5 -8
  49. package/dist/lib/task/intermediate-cleanup.js +91 -8
  50. package/dist/lib/task/lifecycle-recovery.js +549 -0
  51. package/dist/lib/task/lifecycle.js +12 -3
  52. package/dist/lib/task/local-artifact-finalization.js +212 -123
  53. package/dist/lib/task/manual-validation-receipt.js +5 -1
  54. package/dist/lib/task/orchestration.js +52 -2
  55. package/dist/lib/task/pr-delivery-fact.js +12 -56
  56. package/dist/lib/task/recovery-warning.js +19 -0
  57. package/dist/lib/task/review-command.js +3 -2
  58. package/dist/lib/task/review-finalization.js +294 -74
  59. package/dist/lib/{host-control → task}/secure-fs.js +8 -19
  60. package/dist/lib/task/short-id.js +15 -7
  61. package/dist/lib/task/task-execution-lock.js +1 -1
  62. package/dist/lib/task/verification-engine.js +3 -14
  63. package/dist/lib/task/workflow-command.js +2 -1
  64. package/dist/lib/task/workflow-dispatch.js +161 -0
  65. package/dist/lib/version.js +1 -11
  66. package/dist/lifecycle-build-manifest.json +3 -3
  67. package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.ts +7 -2
  68. package/lib/agent-clients/adapters/codex-lifecycle/store.ts +55 -3
  69. package/lib/fs/durable-write.ts +1 -0
  70. package/lib/internal/cli-route-inventory.ts +3 -4
  71. package/lib/internal/manual-validation.ts +17 -8
  72. package/lib/internal/platform-comment.ts +53 -2
  73. package/lib/internal/task-artifact.ts +1 -1
  74. package/lib/internal/task-finalization.ts +1 -1
  75. package/lib/internal/task-lifecycle.ts +5 -4
  76. package/lib/internal/task-operation-registry.ts +22 -13
  77. package/lib/internal/task-orchestration.ts +1 -1
  78. package/lib/internal/task-review.ts +11 -7
  79. package/lib/platform/activity-recovery.ts +179 -0
  80. package/lib/platform/issue-comments.ts +105 -30
  81. package/lib/platform/issues.ts +1 -1
  82. package/lib/platform/pr-change-report.ts +7 -5
  83. package/lib/platform/pr-summary.ts +64 -32
  84. package/lib/platform/pull-requests.ts +3 -3
  85. package/lib/platform/task-comment-projection.ts +79 -0
  86. package/lib/platform/task-identities.ts +3 -14
  87. package/lib/platform/task-recovery.ts +54 -0
  88. package/lib/platform/verification-sync.ts +48 -11
  89. package/lib/pr-review/evidence-grading.ts +4 -9
  90. package/lib/sandbox/commands/rm.ts +34 -2
  91. package/lib/sandbox/control/executor.ts +4 -2
  92. package/lib/sandbox/control/protocol.ts +6 -1
  93. package/lib/sandbox/control/server.ts +30 -10
  94. package/lib/sandbox/control/state.ts +9 -1
  95. package/lib/sandbox/control/task-workflow.ts +1 -1
  96. package/lib/sandbox/control/workflow-executor.ts +106 -35
  97. package/lib/sandbox/index.ts +1 -2
  98. package/lib/sandbox/removal.ts +126 -1128
  99. package/lib/sandbox/workspace-identity.ts +19 -3
  100. package/lib/server/process-state.ts +26 -5
  101. package/lib/task/artifact-command.ts +12 -29
  102. package/lib/task/artifact-operations.ts +22 -252
  103. package/lib/task/artifact-recovery.ts +572 -0
  104. package/lib/task/artifact-repair-intent.ts +126 -51
  105. package/lib/task/commit-operation.ts +35 -4
  106. package/lib/task/control-authority.ts +48 -15
  107. package/lib/task/control-recovery.ts +13 -1
  108. package/lib/task/create-service.ts +1 -10
  109. package/lib/task/delegation-receipts.ts +73 -2
  110. package/lib/task/events.ts +188 -24
  111. package/lib/task/human-override.ts +5 -8
  112. package/lib/task/intermediate-cleanup.ts +66 -12
  113. package/lib/task/lifecycle-recovery.ts +691 -0
  114. package/lib/task/lifecycle.ts +13 -4
  115. package/lib/task/local-artifact-finalization.ts +249 -131
  116. package/lib/task/manual-validation-receipt.ts +5 -1
  117. package/lib/task/orchestration.ts +65 -0
  118. package/lib/task/pr-delivery-fact.ts +15 -62
  119. package/lib/task/recovery-warning.ts +21 -0
  120. package/lib/task/review-command.ts +2 -1
  121. package/lib/task/review-finalization.ts +298 -84
  122. package/lib/{host-control → task}/secure-fs.ts +10 -32
  123. package/lib/task/short-id.ts +27 -5
  124. package/lib/task/task-execution-lock.ts +1 -1
  125. package/lib/task/verification-engine.ts +3 -17
  126. package/lib/task/verification-types.ts +0 -2
  127. package/lib/task/workflow-command.ts +2 -1
  128. package/lib/task/workflow-dispatch.ts +171 -0
  129. package/lib/version.ts +1 -15
  130. package/package.json +1 -1
  131. package/templates/.agents/rules/create-issue.en.md +1 -1
  132. package/templates/.agents/rules/create-issue.zh-CN.md +1 -1
  133. package/templates/.agents/rules/issue-sync.en.md +12 -2
  134. package/templates/.agents/rules/issue-sync.zh-CN.md +13 -5
  135. package/templates/.agents/rules/local-artifact-repair.en.md +47 -39
  136. package/templates/.agents/rules/local-artifact-repair.zh-CN.md +45 -36
  137. package/templates/.agents/skills/analyze-task/SKILL.en.md +6 -6
  138. package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +6 -6
  139. package/templates/.agents/skills/analyze-task/config/verify.en.json +1 -1
  140. package/templates/.agents/skills/analyze-task/config/verify.zh-CN.json +1 -1
  141. package/templates/.agents/skills/block-task/SKILL.en.md +2 -2
  142. package/templates/.agents/skills/block-task/SKILL.zh-CN.md +2 -2
  143. package/templates/.agents/skills/block-task/config/verify.json +1 -1
  144. package/templates/.agents/skills/cancel-task/SKILL.en.md +3 -3
  145. package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +3 -3
  146. package/templates/.agents/skills/cancel-task/config/verify.json +1 -1
  147. package/templates/.agents/skills/code-task/SKILL.en.md +7 -7
  148. package/templates/.agents/skills/code-task/SKILL.zh-CN.md +8 -8
  149. package/templates/.agents/skills/code-task/config/verify.en.json +1 -1
  150. package/templates/.agents/skills/code-task/config/verify.zh-CN.json +1 -1
  151. package/templates/.agents/skills/commit/reference/issue-metadata-sync.en.md +1 -1
  152. package/templates/.agents/skills/commit/reference/issue-metadata-sync.zh-CN.md +1 -1
  153. package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +1 -1
  154. package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +1 -1
  155. package/templates/.agents/skills/complete-task/SKILL.en.md +2 -2
  156. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +2 -2
  157. package/templates/.agents/skills/complete-task/config/verify.en.json +2 -2
  158. package/templates/.agents/skills/complete-task/config/verify.zh-CN.json +2 -2
  159. package/templates/.agents/skills/complete-task/reference/external-delivery.en.md +1 -1
  160. package/templates/.agents/skills/complete-task/reference/external-delivery.zh-CN.md +1 -1
  161. package/templates/.agents/skills/create-pr/SKILL.en.md +2 -2
  162. package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +2 -2
  163. package/templates/.agents/skills/create-pr/config/verify.json +1 -1
  164. package/templates/.agents/skills/create-pr/reference/pr-body-template.en.md +2 -2
  165. package/templates/.agents/skills/create-pr/reference/pr-body-template.zh-CN.md +2 -2
  166. package/templates/.agents/skills/create-task/SKILL.en.md +5 -5
  167. package/templates/.agents/skills/create-task/SKILL.zh-CN.md +5 -5
  168. package/templates/.agents/skills/create-task/config/verify.json +1 -1
  169. package/templates/.agents/skills/import-issue/SKILL.en.md +8 -8
  170. package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +8 -8
  171. package/templates/.agents/skills/import-issue/config/verify.json +2 -2
  172. package/templates/.agents/skills/plan-task/SKILL.en.md +4 -4
  173. package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +4 -4
  174. package/templates/.agents/skills/plan-task/config/verify.en.json +1 -1
  175. package/templates/.agents/skills/plan-task/config/verify.zh-CN.json +1 -1
  176. package/templates/.agents/skills/restore-task/SKILL.en.md +11 -6
  177. package/templates/.agents/skills/restore-task/SKILL.zh-CN.md +11 -6
  178. package/templates/.agents/skills/restore-task/config/verify.json +1 -1
  179. package/templates/.agents/skills/review-analysis/SKILL.en.md +3 -3
  180. package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +3 -3
  181. package/templates/.agents/skills/review-analysis/config/verify.en.json +1 -1
  182. package/templates/.agents/skills/review-analysis/config/verify.zh-CN.json +1 -1
  183. package/templates/.agents/skills/review-analysis/reference/report-template.en.md +7 -7
  184. package/templates/.agents/skills/review-analysis/reference/report-template.zh-CN.md +6 -6
  185. package/templates/.agents/skills/review-code/SKILL.en.md +5 -4
  186. package/templates/.agents/skills/review-code/SKILL.zh-CN.md +5 -4
  187. package/templates/.agents/skills/review-code/config/verify.en.json +1 -1
  188. package/templates/.agents/skills/review-code/config/verify.zh-CN.json +1 -1
  189. package/templates/.agents/skills/review-code/reference/report-template.en.md +12 -12
  190. package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +12 -12
  191. package/templates/.agents/skills/review-plan/SKILL.en.md +3 -3
  192. package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +3 -3
  193. package/templates/.agents/skills/review-plan/config/verify.en.json +1 -1
  194. package/templates/.agents/skills/review-plan/config/verify.zh-CN.json +1 -1
  195. package/templates/.agents/skills/review-plan/reference/report-template.en.md +7 -7
  196. package/templates/.agents/skills/review-plan/reference/report-template.zh-CN.md +7 -7
  197. package/templates/.agents/skills/review-pr/config/verify.en.json +1 -1
  198. package/templates/.agents/skills/review-pr/config/verify.zh-CN.json +1 -1
  199. package/templates/.agents/skills/review-pr/reference/evidence-grading.en.md +3 -3
  200. package/templates/.agents/skills/review-pr/reference/evidence-grading.zh-CN.md +3 -3
  201. package/templates/.agents/skills/run-manual-validation/config/verify.en.json +1 -1
  202. package/templates/.agents/skills/run-manual-validation/config/verify.zh-CN.json +1 -1
  203. package/templates/.agents/skills/run-task/SKILL.en.md +1 -1
  204. package/templates/.agents/skills/run-task/SKILL.zh-CN.md +1 -1
  205. package/templates/.agents/templates/task.en.md +1 -1
  206. package/templates/.agents/templates/task.zh-CN.md +1 -1
  207. package/templates/.claude/commands/import-issue.en.md +1 -1
  208. package/templates/.claude/commands/import-issue.zh-CN.md +1 -1
  209. package/templates/.claude/commands/restore-task.en.md +1 -1
  210. package/templates/.claude/commands/restore-task.zh-CN.md +1 -1
  211. package/dist/lib/host-control/audit.js +0 -19
  212. package/dist/lib/host-control/client.js +0 -142
  213. package/dist/lib/host-control/command.js +0 -49
  214. package/dist/lib/host-control/path.js +0 -160
  215. package/dist/lib/host-control/server.js +0 -159
  216. package/dist/lib/host-control/service.js +0 -112
  217. package/dist/lib/internal/host-control.js +0 -28
  218. package/lib/host-control/audit.ts +0 -22
  219. package/lib/host-control/client.ts +0 -172
  220. package/lib/host-control/command.ts +0 -60
  221. package/lib/host-control/path.ts +0 -167
  222. package/lib/host-control/server.ts +0 -180
  223. package/lib/host-control/service.ts +0 -133
  224. package/lib/internal/host-control.ts +0 -28
@@ -11,6 +11,7 @@ import type { LedgerDocument } from './ledger.ts';
11
11
  import { resolveTaskRef } from './resolve-ref.ts';
12
12
  import {
13
13
  activateDelegation,
14
+ abortActivatedDelegation,
14
15
  abortPreparedDelegation,
15
16
  completeDelegationStage,
16
17
  consumeDelegation,
@@ -59,6 +60,7 @@ import { hasOpenLifecycleExecution } from './activity-log.ts';
59
60
  import { invalidationBlocks } from './invalidation.ts';
60
61
  import type { InvalidationDocument } from './invalidation.ts';
61
62
  import type { LifecycleAction, LifecycleFacts } from './capabilities.ts';
63
+ import { normalizeAgentToken } from '../agent-clients/tokens.ts';
62
64
 
63
65
  type OrchestrationStatus = 'running' | 'paused' | 'completed';
64
66
  type ModelPolicySource = Readonly<{
@@ -1444,6 +1446,68 @@ function recoverPreparedOrchestrationDelegation(
1444
1446
  }
1445
1447
  }
1446
1448
 
1449
+ function recoverActivatedOrchestrationDelegationUnderLock(
1450
+ taskRef: string,
1451
+ event: Readonly<{
1452
+ receiptId: string;
1453
+ stage: DelegationStage;
1454
+ round: number;
1455
+ artifact: string;
1456
+ startedAgent: string;
1457
+ childId: string;
1458
+ stopRevision: number;
1459
+ consumer: string;
1460
+ consumedAt: string;
1461
+ }>,
1462
+ options: OrchestrationOptions = {}
1463
+ ): OrchestrationResult {
1464
+ const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
1465
+ if (!resolved.ok) return failed(resolved.code, resolved.message, resolved.taskId);
1466
+ const run = readRun(resolved.taskDir, options);
1467
+ if (!run) return failed('ORCHESTRATION_RUN_MISSING', 'no orchestration run exists', resolved.taskId);
1468
+ const matches = [
1469
+ ...(run.pendingDelegation ? [run.pendingDelegation] : []),
1470
+ ...run.receipts.filter((receipt) => receipt.id === event.receiptId)
1471
+ ];
1472
+ const receipt = matches.find((candidate) => candidate.id === event.receiptId);
1473
+ if (!receipt) return failed('ORCHESTRATION_DELEGATION_MISSING', 'recovery receipt does not exist', resolved.taskId);
1474
+ if (
1475
+ receipt.taskId !== resolved.taskId
1476
+ || receipt.stage !== event.stage
1477
+ || receipt.round !== event.round
1478
+ || receipt.artifact !== event.artifact
1479
+ || receipt.childId !== event.childId
1480
+ || normalizeAgentToken(event.startedAgent) !== normalizeAgentToken(receipt.client)
1481
+ ) {
1482
+ return failed('ORCHESTRATION_PROVENANCE_MISMATCH', 'recovery selector does not match the delegation receipt', resolved.taskId);
1483
+ }
1484
+ if (receipt.status === 'aborted' && run.pendingDelegation === null) {
1485
+ return { status: run.status, changed: false, taskId: resolved.taskId, run, next: null, error: null };
1486
+ }
1487
+ if (run.pendingDelegation?.id !== receipt.id || receipt.status !== 'activated') {
1488
+ return failed('ORCHESTRATION_RECOVERY_UNSAFE', 'recovery requires one matching activated pending delegation', resolved.taskId);
1489
+ }
1490
+ if (run.receipts.some((candidate) => candidate.id === receipt.id)) {
1491
+ return failed('ORCHESTRATION_RECEIPT_DUPLICATE', 'recovery receipt already exists in the completed receipt history', resolved.taskId);
1492
+ }
1493
+ const aborted = abortActivatedDelegation(receipt, {
1494
+ childId: event.childId,
1495
+ stopRevision: event.stopRevision,
1496
+ consumer: event.consumer,
1497
+ consumedAt: event.consumedAt
1498
+ });
1499
+ if (!aborted.ok) return failed(aborted.code, aborted.message, resolved.taskId);
1500
+ const updated = withUpdatedRun(run, {
1501
+ status: 'running',
1502
+ pause: null,
1503
+ nextStage: null,
1504
+ pendingDelegation: null,
1505
+ receipts: Object.freeze([...run.receipts, aborted.receipt])
1506
+ }, options.now);
1507
+ saveRun(resolved.taskDir, updated);
1508
+ return { status: 'running', changed: true, taskId: resolved.taskId, run: updated, next: null, error: null };
1509
+ }
1510
+
1447
1511
  function completeOrchestrationStage(
1448
1512
  taskRef: string,
1449
1513
  event: Parameters<typeof completeDelegationStage>[1],
@@ -1598,6 +1662,7 @@ export {
1598
1662
  readRun,
1599
1663
  reconcileMatchingOrchestrationDelegation,
1600
1664
  recoverPreparedOrchestrationDelegation,
1665
+ recoverActivatedOrchestrationDelegationUnderLock,
1601
1666
  routeOrchestration,
1602
1667
  sealMatchingOrchestrationDelegation,
1603
1668
  sealMatchingOrchestrationDelegationWithHostEvidence,
@@ -1,5 +1,4 @@
1
1
  import type { ResourceIdentity } from '../platform/resource-identity.ts';
2
- import { isLegacyCompatibilityEnabled, legacyCompatibilityError, VERSION } from '../version.ts';
3
2
  import {
4
3
  isResourceIdentity,
5
4
  parseResourceIdentity,
@@ -17,14 +16,6 @@ type PrDeliveryIdentity = {
17
16
  base: { repository: string; ref: string; sha: string };
18
17
  };
19
18
 
20
- type LegacyPrDeliveryIdentity = {
21
- repository: string;
22
- number: number;
23
- nodeId: string;
24
- url: string;
25
- head: { repository: string; ref: string; sha: string };
26
- base: { repository: string; ref: string; sha: string };
27
- };
28
19
  type CurrentPrDeliveryIdentityInput = {
29
20
  resource: ResourceIdentity;
30
21
  repository: string;
@@ -93,10 +84,6 @@ function timestamp(value: unknown, label: string): string {
93
84
  if (Number.isNaN(Date.parse(result))) throw factError(`${label} must be a valid timestamp`);
94
85
  return new Date(result).toISOString();
95
86
  }
96
- function positiveNumber(value: unknown, label: string): number {
97
- if (!Number.isSafeInteger(value) || (value as number) <= 0) throw factError(`${label} must be a positive integer`);
98
- return value as number;
99
- }
100
87
  function parseRef(value: unknown, label: string): { repository: string; ref: string; sha: string } {
101
88
  if (!isRecord(value)) throw factError(`${label} must be an object`);
102
89
  exactKeys(value, ['repository', 'ref', 'sha'], label);
@@ -115,35 +102,19 @@ function canonicalIdentity(value: unknown): PrDeliveryIdentity {
115
102
  };
116
103
  }
117
104
 
118
- function decodeLegacyIdentity(value: unknown): PrDeliveryIdentity {
119
- if (!isRecord(value)) throw factError('legacy identity must be an object');
120
- // TODO(compat): Remove the v1 identity decoder before the first stable v1.0.0 release.
121
- exactKeys(value, ['repository', 'number', 'nodeId', 'url', 'head', 'base'], 'legacy identity');
122
- const legacy = {
123
- number: positiveNumber(value.number, 'legacy identity.number'),
124
- repository: text(value.repository, 'identity.repository'),
125
- nodeId: text(value.nodeId, 'legacy identity.nodeId'),
126
- url: text(value.url, 'identity.url'),
127
- head: parseRef(value.head, 'identity.head'),
128
- base: parseRef(value.base, 'identity.base')
129
- } satisfies LegacyPrDeliveryIdentity;
130
- return {
131
- resource: { kind: 'number', value: legacy.number },
132
- repository: legacy.repository,
133
- url: legacy.url,
134
- head: legacy.head,
135
- base: legacy.base
136
- };
137
- }
138
-
139
105
  function provenanceForSource(source: PrDeliveryBindingSource): PrDeliveryProvenance { return source === 'created' ? 'create-post' : source === 'reused' ? 'reuse' : source; }
140
106
 
141
- function parseFact(value: unknown, runtimeVersion = VERSION): PrDeliveryFact {
107
+ function legacyFactError(): PrDeliveryFactError {
108
+ return Object.assign(
109
+ new Error('PLATFORM_IDENTITY_LEGACY_UNSUPPORTED: v1 PR delivery fact is unsupported; use the previous tool version to view this task or migrate its fact to version 2 before using this version'),
110
+ { code: 'PLATFORM_IDENTITY_LEGACY_UNSUPPORTED' }
111
+ );
112
+ }
113
+
114
+ function parseFact(value: unknown): PrDeliveryFact {
142
115
  if (!isRecord(value)) throw factError('fact must be an object');
143
- if ((value.version !== 1 && value.version !== 2) || typeof value.state !== 'string') throw factError('version or state is invalid');
144
- if (value.version === 1 && !isLegacyCompatibilityEnabled(runtimeVersion)) {
145
- throw legacyCompatibilityError('v1 PR delivery fact', runtimeVersion);
146
- }
116
+ if (value.version === 1) throw legacyFactError();
117
+ if (value.version !== 2 || typeof value.state !== 'string') throw factError('version or state is invalid');
147
118
  if (value.state === 'unbound') {
148
119
  exactKeys(value, ['version', 'state', 'reason'], 'unbound fact');
149
120
  if (value.reason !== 'initial') throw factError('unbound reason is invalid');
@@ -155,24 +126,6 @@ function parseFact(value: unknown, runtimeVersion = VERSION): PrDeliveryFact {
155
126
  return { version: 2, state: 'skipped', reason: 'explicit', decidedAt: timestamp(value.decidedAt, 'skipped.decidedAt') };
156
127
  }
157
128
  if (value.state !== 'bound') throw factError('state is invalid');
158
- if (value.version === 1) {
159
- // TODO(compat): Remove this v1-to-v2 shape conversion before the first stable v1.0.0 release.
160
- exactKeys(value, ['version', 'state', 'identity', 'binding', 'provenance'], 'legacy bound fact');
161
- if (!isRecord(value.binding) || !isRecord(value.provenance)) throw factError('legacy binding or provenance is invalid');
162
- exactKeys(value.binding, ['status', 'source', 'verifiedAt', 'issueNumber', 'remoteState', 'mergedAt', 'mergeCommitSha'], 'legacy binding');
163
- const issueNumber = value.binding.issueNumber === null ? null : positiveNumber(value.binding.issueNumber, 'legacy binding.issueNumber');
164
- return parseFact({
165
- version: 2,
166
- state: 'bound',
167
- identity: decodeLegacyIdentity(value.identity),
168
- binding: {
169
- status: 'verified', source: value.binding.source, verifiedAt: timestamp(value.binding.verifiedAt, 'binding.verifiedAt'),
170
- issueIdentity: issueNumber === null ? null : { kind: 'number', value: issueNumber }, remoteState: value.binding.remoteState,
171
- mergedAt: value.binding.mergedAt === null ? null : timestamp(value.binding.mergedAt, 'binding.mergedAt'), mergeCommitSha: nullableText(value.binding.mergeCommitSha, 'binding.mergeCommitSha')
172
- },
173
- provenance: value.provenance
174
- }, runtimeVersion);
175
- }
176
129
  exactKeys(value, ['version', 'state', 'identity', 'binding', 'provenance'], 'bound fact');
177
130
  if (!isRecord(value.binding) || !isRecord(value.provenance)) throw factError('binding or provenance is invalid');
178
131
  exactKeys(value.binding, ['status', 'source', 'verifiedAt', 'issueIdentity', 'remoteState', 'mergedAt', 'mergeCommitSha'], 'binding');
@@ -195,19 +148,19 @@ function parseFact(value: unknown, runtimeVersion = VERSION): PrDeliveryFact {
195
148
  };
196
149
  }
197
150
 
198
- function decodePrDeliveryFact(value: unknown, runtimeVersion = VERSION): PrDeliveryFact {
151
+ function decodePrDeliveryFact(value: unknown, _runtimeVersion?: string): PrDeliveryFact {
199
152
  if (typeof value === 'string') {
200
153
  let decoded: unknown;
201
154
  try { decoded = JSON.parse(value); }
202
155
  catch { throw factError('value is not valid JSON'); }
203
- return parseFact(decoded, runtimeVersion);
156
+ return parseFact(decoded);
204
157
  }
205
- return parseFact(value, runtimeVersion);
158
+ return parseFact(value);
206
159
  }
207
160
  function encodePrDeliveryFact(fact: PrDeliveryFact): string { return JSON.stringify(parseFact(fact)); }
208
- function readPrDeliveryFact(metadata: Record<string, unknown>, runtimeVersion = VERSION): PrDeliveryFactReadResult {
161
+ function readPrDeliveryFact(metadata: Record<string, unknown>, _runtimeVersion?: string): PrDeliveryFactReadResult {
209
162
  if (!Object.hasOwn(metadata, PR_DELIVERY_FACT_KEY) || metadata[PR_DELIVERY_FACT_KEY] === '') return { status: 'missing', fact: null };
210
- try { return { status: 'valid', fact: decodePrDeliveryFact(metadata[PR_DELIVERY_FACT_KEY], runtimeVersion) }; }
163
+ try { return { status: 'valid', fact: decodePrDeliveryFact(metadata[PR_DELIVERY_FACT_KEY]) }; }
211
164
  catch (error) { return { status: 'invalid', fact: null, error: asFactError(error) }; }
212
165
  }
213
166
  function buildUnboundFact(): PrDeliveryFact { return { version: 2, state: 'unbound', reason: 'initial' }; }
@@ -0,0 +1,21 @@
1
+ export type RecoveryWarning = Readonly<{ code: string; message: string; action: string }>;
2
+
3
+ function warningText(value: unknown): value is string {
4
+ return typeof value === 'string' && value.length > 0 && value.trim() === value && !/[\r\n]/u.test(value);
5
+ }
6
+
7
+ export function isRecoveryWarning(value: unknown): value is RecoveryWarning {
8
+ if (!value || typeof value !== 'object' || Array.isArray(value)) return false;
9
+ const warning = value as Record<string, unknown>;
10
+ return Object.keys(warning).sort().join(',') === 'action,code,message'
11
+ && warningText(warning.code)
12
+ && warningText(warning.message)
13
+ && warningText(warning.action);
14
+ }
15
+
16
+ export function sameRecoveryWarning(left: unknown, right: unknown): boolean {
17
+ return isRecoveryWarning(left) && isRecoveryWarning(right)
18
+ && left.code === right.code
19
+ && left.message === right.message
20
+ && left.action === right.action;
21
+ }
@@ -3,10 +3,11 @@ import type { ReviewFinalizationRequest } from './review-finalization.ts';
3
3
  export function parseReviewCommand(args: readonly string[]): ReviewFinalizationRequest & {
4
4
  overrideTicket?: string; overrideTarget?: string; overrideScope?: string;
5
5
  } {
6
- if (!args[0] || args[1] !== 'finalize-summary') throw new Error('task ref and finalize-summary are required');
6
+ if (!args[0] || !['preflight', 'finalize-summary'].includes(args[1] ?? '')) throw new Error('task ref and a supported review operation are required');
7
7
  const values: Record<string, string | boolean> = {};
8
8
  const flags = {
9
9
  '--stage': 'stage', '--artifact': 'artifact', '--orchestrated': 'orchestrated', '--dry-run': 'dryRun',
10
+ '--recovery-id': 'recoveryId',
10
11
  '--override-ticket': 'overrideTicket', '--override-target': 'overrideTarget', '--override-scope': 'overrideScope'
11
12
  } as const;
12
13
  for (let index = 2; index < args.length; index += 1) {