@fitlab-ai/agent-infra 0.11.2 → 0.11.3

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 (79) hide show
  1. package/dist/bin/internal-cli.js +7 -2
  2. package/dist/lib/agent-clients/adapter.js +3 -0
  3. package/dist/lib/agent-clients/adapters/codex-lifecycle/recovery.js +234 -0
  4. package/dist/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.js +8 -35
  5. package/dist/lib/agent-clients/adapters/codex-lifecycle/store.js +1 -34
  6. package/dist/lib/agent-clients/adapters/codex.js +10 -0
  7. package/dist/lib/internal/manual-validation.js +22 -27
  8. package/dist/lib/internal/sandbox-control.js +1 -43
  9. package/dist/lib/internal/task-lifecycle.js +1 -1
  10. package/dist/lib/internal/task-operation-registry.js +6 -2
  11. package/dist/lib/platform/issue-comments.js +2 -31
  12. package/dist/lib/platform/pr-summary.js +7 -2
  13. package/dist/lib/sandbox/control/client.js +3 -44
  14. package/dist/lib/sandbox/control/executor.js +14 -79
  15. package/dist/lib/sandbox/control/finalization-response.js +3 -5
  16. package/dist/lib/sandbox/control/protocol.js +5 -64
  17. package/dist/lib/sandbox/control/server.js +29 -206
  18. package/dist/lib/sandbox/control/state.js +1 -7
  19. package/dist/lib/sandbox/control/task-view.js +1 -2
  20. package/dist/lib/task/control-authority.js +34 -36
  21. package/dist/lib/task/control-recovery.js +3 -42
  22. package/dist/lib/task/delegation-receipts.js +2 -2
  23. package/dist/lib/task/finalization-artifacts.js +1 -3
  24. package/dist/lib/task/finalization-state.js +1 -1
  25. package/dist/lib/task/finalization.js +53 -126
  26. package/dist/lib/task/lifecycle.js +1 -1
  27. package/dist/lib/task/manual-validation-receipt.js +8 -3
  28. package/dist/lib/task/orchestration.js +12 -6
  29. package/dist/lifecycle-build-manifest.json +3 -3
  30. package/lib/agent-clients/adapter.ts +46 -0
  31. package/lib/agent-clients/adapters/codex-lifecycle/recovery.ts +297 -0
  32. package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.ts +13 -33
  33. package/lib/agent-clients/adapters/codex-lifecycle/store.ts +1 -44
  34. package/lib/agent-clients/adapters/codex.ts +10 -0
  35. package/lib/internal/manual-validation.ts +22 -26
  36. package/lib/internal/sandbox-control.ts +0 -42
  37. package/lib/internal/task-lifecycle.ts +2 -2
  38. package/lib/internal/task-operation-registry.ts +7 -4
  39. package/lib/platform/issue-comments.ts +1 -35
  40. package/lib/platform/pr-summary.ts +10 -2
  41. package/lib/sandbox/control/client.ts +5 -68
  42. package/lib/sandbox/control/executor.ts +14 -81
  43. package/lib/sandbox/control/finalization-response.ts +3 -5
  44. package/lib/sandbox/control/protocol.ts +7 -83
  45. package/lib/sandbox/control/server.ts +30 -220
  46. package/lib/sandbox/control/state.ts +1 -9
  47. package/lib/sandbox/control/task-view.ts +1 -2
  48. package/lib/task/control-authority.ts +38 -47
  49. package/lib/task/control-recovery.ts +3 -43
  50. package/lib/task/delegation-receipts.ts +2 -2
  51. package/lib/task/finalization-artifacts.ts +1 -3
  52. package/lib/task/finalization-state.ts +1 -1
  53. package/lib/task/finalization.ts +57 -121
  54. package/lib/task/lifecycle.ts +2 -2
  55. package/lib/task/manual-validation-receipt.ts +8 -2
  56. package/lib/task/orchestration.ts +30 -19
  57. package/package.json +1 -1
  58. package/templates/.agents/rules/lifecycle-orchestration.en.md +19 -5
  59. package/templates/.agents/rules/lifecycle-orchestration.zh-CN.md +19 -5
  60. package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +2 -2
  61. package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +2 -2
  62. package/templates/.agents/skills/complete-task/SKILL.en.md +6 -7
  63. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +6 -7
  64. package/templates/.agents/skills/run-manual-validation/SKILL.en.md +2 -2
  65. package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +2 -2
  66. package/templates/.agents/skills/run-task/SKILL.en.md +9 -8
  67. package/templates/.agents/skills/run-task/SKILL.zh-CN.md +9 -8
  68. package/dist/lib/sandbox/control/task-workflow.js +0 -74
  69. package/dist/lib/sandbox/control/workflow-executor.js +0 -142
  70. package/dist/lib/task/lifecycle-recovery.js +0 -549
  71. package/dist/lib/task/recovery-warning.js +0 -19
  72. package/dist/lib/task/workflow-command.js +0 -21
  73. package/dist/lib/task/workflow-dispatch.js +0 -213
  74. package/lib/sandbox/control/task-workflow.ts +0 -95
  75. package/lib/sandbox/control/workflow-executor.ts +0 -156
  76. package/lib/task/lifecycle-recovery.ts +0 -691
  77. package/lib/task/recovery-warning.ts +0 -21
  78. package/lib/task/workflow-command.ts +0 -22
  79. package/lib/task/workflow-dispatch.ts +0 -214
@@ -56,11 +56,10 @@ function receiptIdentity(value: unknown, generation: string, requestId?: string)
56
56
  const binding = receipt.controlBinding;
57
57
  if (!binding || typeof binding !== 'object' || Array.isArray(binding)) return null;
58
58
  const controlBinding = binding as Record<string, unknown>;
59
- if (receipt.version !== 3 || typeof receipt.receiptId !== 'string' || receipt.receiptId.length === 0
59
+ if (receipt.version !== 4 || typeof receipt.receiptId !== 'string' || receipt.receiptId.length === 0
60
60
  || !Number.isSafeInteger(receipt.revision) || (receipt.revision as number) < 0
61
61
  || receipt.lifecycle !== 'done'
62
62
  || !['pending', 'done', 'skipped'].includes(String(receipt.summary))
63
- || !['pending', 'done', 'skipped'].includes(String(receipt.postSummaryVerification))
64
63
  || controlBinding.generation !== generation
65
64
  || typeof controlBinding.requestId !== 'string'
66
65
  || !/^[a-f0-9-]{16,64}$/u.test(controlBinding.requestId)
@@ -5,6 +5,7 @@ import fs from 'node:fs';
5
5
  import { normalizeAgentToken } from '../agent-clients/tokens.ts';
6
6
  import { isAgentClientId } from '../agent-clients/types.ts';
7
7
  import type { AgentClientId } from '../agent-clients/types.ts';
8
+ import { getAgentClientAdapter } from '../agent-clients/registry.ts';
8
9
  import { consumeHumanOverride, failureId, overrideDryRunConflict } from './human-override.ts';
9
10
  import {
10
11
  activateMatchingOrchestrationDelegation,
@@ -22,12 +23,7 @@ import {
22
23
  sealOrchestrationDelegation,
23
24
  statusOrchestration
24
25
  } from './orchestration.ts';
25
- import type {
26
- OrchestrationDiagnosticLogger,
27
- OrchestrationOptions,
28
- OrchestrationResult
29
- } from './orchestration.ts';
30
- import { prepareCodexOrchestrationDelegation } from './codex-orchestration.ts';
26
+ import type { OrchestrationOptions, OrchestrationResult } from './orchestration.ts';
31
27
  import {
32
28
  applyTaskFinalization,
33
29
  type TaskFinalizationRequest,
@@ -39,12 +35,10 @@ import {
39
35
  type TaskLifecycleRequest,
40
36
  type TaskLifecycleResult
41
37
  } from './lifecycle.ts';
42
- import {
43
- recoverStartedLifecycleUnderLock,
44
- recoveryFailure,
45
- type LifecycleRecoveryResult
46
- } from './lifecycle-recovery.ts';
47
- import type { LifecycleRecoveryRequest } from './lifecycle-recovery.ts';
38
+ import type {
39
+ AgentClientLifecycleRecoveryRequest as LifecycleRecoveryRequest,
40
+ AgentClientLifecycleRecoveryResult as LifecycleRecoveryResult
41
+ } from '../agent-clients/adapter.ts';
48
42
  import { locateActivityLog } from './activity-log.ts';
49
43
  import { resolveTaskRef, TASK_ID_RE } from './resolve-ref.ts';
50
44
  import { TaskExecutionLockError, withTaskExecutionLock } from './task-execution-lock.ts';
@@ -542,7 +536,6 @@ export type TaskControlExecutionContext =
542
536
  generation: string;
543
537
  manifestPath: string;
544
538
  requestId: string;
545
- diagnosticLog?: OrchestrationDiagnosticLogger;
546
539
  controllerBinding?: TaskControlControllerBinding | null;
547
540
  lifecycleRecoveryAttestation?: LifecycleRecoveryAttestationV1 | null;
548
541
  }>;
@@ -631,7 +624,6 @@ export function createSandboxExecutorExecutionContext(params: Readonly<{
631
624
  generation: string;
632
625
  manifestPath: string;
633
626
  requestId: string;
634
- diagnosticLog?: OrchestrationDiagnosticLogger;
635
627
  controllerBinding?: TaskControlControllerBinding | null;
636
628
  lifecycleRecoveryAttestation?: LifecycleRecoveryAttestationV1 | null;
637
629
  }>): TaskControlExecutionContext {
@@ -652,7 +644,6 @@ export function createSandboxExecutorExecutionContext(params: Readonly<{
652
644
  generation: params.generation,
653
645
  manifestPath: absolute('manifestPath', params.manifestPath),
654
646
  requestId: params.requestId,
655
- ...(params.diagnosticLog === undefined ? {} : { diagnosticLog: params.diagnosticLog }),
656
647
  ...(params.controllerBinding === undefined ? {} : { controllerBinding: binding(params.controllerBinding) }),
657
648
  ...(params.lifecycleRecoveryAttestation === undefined ? {} : {
658
649
  lifecycleRecoveryAttestation: params.lifecycleRecoveryAttestation === null
@@ -697,11 +688,7 @@ function domainOptions(
697
688
  ): OrchestrationOptions {
698
689
  return {
699
690
  ...options,
700
- repoRoot: context.repoRoot,
701
- ...(context.source === 'sandbox-executor' && context.diagnosticLog !== undefined
702
- ? { diagnosticLog: context.diagnosticLog }
703
- : {}),
704
- ...(context.source === 'sandbox-executor' ? { gitWorktreeRoot: context.worktreeRoot } : {})
691
+ repoRoot: context.repoRoot
705
692
  };
706
693
  }
707
694
 
@@ -725,7 +712,17 @@ async function applyLifecycleWithAuthority(
725
712
  }
726
713
  const execute = async (): Promise<(TaskLifecycleResult | LifecycleRecoveryResult) & { humanOverride?: unknown }> => {
727
714
  if (request.intent === 'recover-started') {
728
- return recoverStartedLifecycleUnderLock(request as LifecycleRecoveryRequest, { repoRoot: context.repoRoot });
715
+ const adapter = isAgentClientId(request.agent)
716
+ ? getAgentClientAdapter(request.agent).orchestrationAdapter
717
+ : undefined;
718
+ if (!adapter?.recoverStarted) {
719
+ return {
720
+ status: 'no-op', changed: false, targetState: 'active',
721
+ requestRef: request.taskRef, intent: 'recover-started', taskId: null,
722
+ receiptId: null, childId: null, error: null
723
+ };
724
+ }
725
+ return adapter.recoverStarted(request as LifecycleRecoveryRequest, { repoRoot: context.repoRoot });
729
726
  }
730
727
  const lifecycleResult = applyTaskLifecycle(request, { repoRoot: context.repoRoot });
731
728
  if (lifecycleResult.status !== 'failed' || !request.overrideTicket) return lifecycleResult;
@@ -761,7 +758,12 @@ async function applyLifecycleWithAuthority(
761
758
  } catch (error) {
762
759
  if (!(error instanceof TaskExecutionLockError)) throw error;
763
760
  if (request.intent === 'recover-started') {
764
- return recoveryFailure(request as LifecycleRecoveryRequest, 'owner-unknown', error.code, error.message);
761
+ return {
762
+ status: 'owner-unknown', changed: false, targetState: 'active',
763
+ requestRef: request.taskRef, intent: 'recover-started', taskId,
764
+ receiptId: null, childId: null,
765
+ error: { code: error.code, message: error.message }
766
+ };
765
767
  }
766
768
  return taskLifecycleFailure(request, { code: error.code, message: error.message }, taskId);
767
769
  }
@@ -790,8 +792,10 @@ function orchestration(
790
792
  requestedModel: input.requestedModel as string | undefined,
791
793
  requestedReasoningEffort: input.requestedReasoningEffort as string | undefined,
792
794
  };
793
- if (prepareInput.client === 'codex') {
794
- return prepareCodexOrchestrationDelegation(operation.taskRef, prepareInput, {
795
+ const prepareDelegation = getAgentClientAdapter(prepareInput.client)
796
+ .orchestrationAdapter?.prepareDelegation;
797
+ if (prepareDelegation) {
798
+ return prepareDelegation(operation.taskRef, prepareInput, {
795
799
  repoRoot: context.repoRoot,
796
800
  orchestrationOptions: options,
797
801
  });
@@ -898,7 +902,7 @@ function operationInvalid(message: string): never {
898
902
 
899
903
  const LIFECYCLE_FLAGS = new Set([
900
904
  '--agent', '--reason', '--unblock-condition', '--note', '--alert-number', '--staging-dir', '--issue-number',
901
- '--stage', '--round', '--artifact', '--override-ticket', '--override-target', '--override-scope', '--dry-run'
905
+ '--auto', '--override-ticket', '--override-target', '--override-scope', '--dry-run'
902
906
  ]);
903
907
 
904
908
  const FINALIZATION_FLAGS = new Set(['--agent']);
@@ -909,7 +913,7 @@ const ORCHESTRATION_FLAGS = new Set([
909
913
  '--parent-id', '--before-fingerprint', '--stage', '--round', '--artifact', '--role',
910
914
  '--native-agent', '--child-id', '--spawn-mode', '--actual-model', '--actual-reasoning-effort',
911
915
  '--model-fallback-reason', '--reasoning-effort-fallback-reason', '--exit-code', '--after-fingerprint',
912
- '--changed-paths', '--code', '--message', '--recoverable', '--git-worktree-root'
916
+ '--changed-paths', '--code', '--message', '--recoverable'
913
917
  ]);
914
918
 
915
919
  function parseValues(
@@ -920,7 +924,7 @@ function parseValues(
920
924
  const values: Record<string, string | boolean> = {};
921
925
  for (let index = start; index < args.length; index += 1) {
922
926
  const flag = args[index]!;
923
- if (flag === '--dry-run') {
927
+ if (flag === '--dry-run' || flag === '--auto') {
924
928
  if (!flags.has(flag)) operationInvalid(`unknown option '${flag}'`);
925
929
  if (values[flag] !== undefined) operationInvalid(`duplicate option '${flag}'`);
926
930
  values[flag] = true;
@@ -978,28 +982,18 @@ export function parseTaskControlOperation(
978
982
  ...(value(values, '--override-scope') ? { overrideScope: value(values, '--override-scope') } : {}),
979
983
  ...(value(values, '--alert-number') ? { alertNumber: Number(value(values, '--alert-number')) } : {}),
980
984
  ...(value(values, '--issue-number') ? { issueNumber: Number(value(values, '--issue-number')) } : {}),
981
- ...(value(values, '--stage') ? { stage: value(values, '--stage') } : {}),
982
- ...(value(values, '--round') ? { round: Number(value(values, '--round')) } : {}),
983
- ...(value(values, '--artifact') ? { artifact: value(values, '--artifact') } : {}),
985
+ ...(values['--auto'] === true ? { auto: true } : {}),
984
986
  ...(values['--dry-run'] === true ? { dryRun: true } : {})
985
987
  };
986
988
  if (intent === 'recover-started') {
987
- const recoveryFlags = new Set(['--agent', '--stage', '--round', '--artifact', '--reason']);
989
+ const recoveryFlags = new Set(['--agent', '--auto']);
988
990
  for (const flag of Object.keys(values)) {
989
991
  if (!recoveryFlags.has(flag)) operationInvalid(`option '${flag}' is not supported for recover-started`);
990
992
  }
991
- required(values, ['--stage', '--round', '--artifact', '--reason']);
992
- const stage = value(values, '--stage');
993
- if (!['analysis', 'review-analysis', 'plan', 'review-plan', 'code', 'review-code'].includes(stage ?? '')) {
994
- operationInvalid('--stage must be a supported lifecycle stage');
995
- }
996
- const round = Number(value(values, '--round'));
997
- if (!Number.isSafeInteger(round) || round < 1) operationInvalid('--round must be a positive integer');
998
- if (value(values, '--reason')!.includes('\n') || value(values, '--reason')!.includes('\r')) {
999
- operationInvalid('--reason must be a single line');
1000
- }
1001
- } else if (values['--stage'] !== undefined || values['--round'] !== undefined || values['--artifact'] !== undefined) {
1002
- operationInvalid('--stage, --round, and --artifact are only supported for recover-started');
993
+ if (values['--auto'] !== true) operationInvalid('--auto is required for recover-started');
994
+ return { family, request: input as unknown as TaskLifecycleControlRequest };
995
+ } else if (values['--auto'] !== undefined) {
996
+ operationInvalid('--auto is only supported for recover-started');
1003
997
  }
1004
998
  return { family, request: input as unknown as TaskLifecycleControlRequest };
1005
999
  }
@@ -1092,8 +1086,5 @@ export function parseTaskControlOperation(
1092
1086
  if (recoverable !== 'true' && recoverable !== 'false') operationInvalid('--recoverable must be true or false');
1093
1087
  input.code = value(values, '--code'); input.message = value(values, '--message'); input.recoverable = recoverable === 'true';
1094
1088
  }
1095
- const options = value(values, '--git-worktree-root') === undefined
1096
- ? undefined
1097
- : { gitWorktreeRoot: value(values, '--git-worktree-root') };
1098
- return { family, taskRef, intent: parsedIntent, input, ...(options ? { options } : {}) };
1089
+ return { family, taskRef, intent: parsedIntent, input };
1099
1090
  }
@@ -1,7 +1,4 @@
1
1
  import { createHash } from 'node:crypto';
2
- import { isCompletionEvidence } from './orchestration.ts';
3
- import { TASK_WORKFLOW_OPERATIONS } from './workflow-command.ts';
4
- import { sameRecoveryWarning } from './recovery-warning.ts';
5
2
 
6
3
  export type ControlRecoveryOutcome = 'not-executed' | 'in-progress' | 'success' | 'failure' | 'unknown' | 'rejected';
7
4
 
@@ -16,7 +13,7 @@ export function parseControlOutput(output: string | null): Record<string, unknow
16
13
  }
17
14
 
18
15
  export type ControlRecoveryOperation = Readonly<{
19
- family: 'task-lifecycle' | 'task-finalization' | 'task-orchestration' | 'task-create' | 'codex-controller' | 'task-workflow';
16
+ family: 'task-lifecycle' | 'task-finalization' | 'task-create' | 'codex-controller';
20
17
  intent: string;
21
18
  class: string;
22
19
  }>;
@@ -55,21 +52,10 @@ export type ControlRecoveryDecision = Readonly<{
55
52
  }>;
56
53
 
57
54
  const LIFECYCLE_INTENTS = ['block', 'activate', 'cancel', 'complete', 'close-codescan', 'close-dependabot', 'restore', 'recover-started'] as const;
58
- const ORCHESTRATION_INTENTS = [
59
- 'begin-or-resume', 'route.read', 'route.clean-completion', 'status', 'prepare', 'dispatch',
60
- 'await-activation', 'recover-prepared', 'hook-start', 'hook-stop', 'advance', 'pause'
61
- ] as const;
62
-
63
55
  export const SANDBOX_CONTROL_RECOVERY_OPERATIONS: readonly ControlRecoveryOperation[] = Object.freeze([
64
56
  ...LIFECYCLE_INTENTS.map((intent) => ({ family: 'task-lifecycle' as const, intent, class: 'lifecycle-mutation' })),
65
57
  { family: 'task-finalization', intent: 'complete', class: 'finalization' },
66
- ...ORCHESTRATION_INTENTS.map((intent) => ({
67
- family: 'task-orchestration' as const,
68
- intent,
69
- class: intent === 'route.clean-completion' ? 'route.clean-completion' : intent === 'route.read' || intent === 'status' ? 'read-only' : 'orchestration'
70
- })),
71
58
  { family: 'task-create', intent: 'create', class: 'task-create' },
72
- ...TASK_WORKFLOW_OPERATIONS.map((intent) => ({ family: 'task-workflow' as const, intent, class: intent === 'artifact-inspect' || intent === 'decision-next-id' ? 'read-only' : 'workflow' })),
73
59
  ...(['open', 'close', 'verify'] as const).map((intent) => ({ family: 'codex-controller' as const, intent, class: 'codex-controller' }))
74
60
  ]);
75
61
 
@@ -95,29 +81,7 @@ function domainEvidenceMatches(
95
81
  ): boolean {
96
82
  if (!domain) return false;
97
83
  if (domain.consistent !== true) return false;
98
- if (operation.class === 'route.clean-completion') {
99
- const completion = domain.completionEvidence as Record<string, unknown> | undefined;
100
- const snapshot = domain.snapshot as Record<string, unknown> | undefined;
101
- const lastReviewedCommit = domain.lastReviewedCommit;
102
- return result.status === 'completed'
103
- && domain.status === 'completed'
104
- && domain.pendingDelegation === null
105
- && isCompletionEvidence(completion)
106
- && snapshot?.head === completion.head
107
- && snapshot.headTree === completion.headTree
108
- && snapshot.worktreeTree === completion.worktreeTree
109
- && lastReviewedCommit === completion.lastReviewedCommit;
110
- }
111
- if (operation.family === 'task-lifecycle' && operation.intent === 'recover-started') {
112
- const retryRequired = domain.recoveryState === 'retry-required'
113
- && sameRecoveryWarning(domain.warning, result.warning);
114
- return result.targetState === 'active'
115
- && ['applied', 'no-op'].includes(String(result.status))
116
- && domain.recovery === true
117
- && domain.targetState === 'active'
118
- && (domain.recoveryState === 'released' || retryRequired);
119
- }
120
- if (operation.class === 'read-only') return result.changed === false && domain.snapshotValid === true;
84
+ if (operation.family === 'task-lifecycle' && operation.intent === 'recover-started') return false;
121
85
  return true;
122
86
  }
123
87
 
@@ -167,11 +131,7 @@ export function classifySandboxControlRecovery(input: ControlRecoveryInput): Con
167
131
  return {
168
132
  outcome: 'success',
169
133
  responseReconstructable: true,
170
- reasonCode: input.operation.class === 'route.clean-completion' && result.changed === false
171
- ? 'RECOVERY_ROUTE_COMPLETION_NOOP'
172
- : input.operation.intent === 'recover-started' && input.domain?.recoveryState === 'retry-required'
173
- ? 'RECOVERY_RELEASE_RETRY_REQUIRED'
174
- : 'RECOVERY_TERMINAL_AND_DOMAIN_MATCH'
134
+ reasonCode: 'RECOVERY_TERMINAL_AND_DOMAIN_MATCH'
175
135
  };
176
136
  }
177
137
 
@@ -248,7 +248,7 @@ function hasStatusBoundEvidence(receipt: DelegationReceipt): boolean {
248
248
  && host !== undefined
249
249
  && Number.isSafeInteger(host.stopRevision)
250
250
  && (host.stopRevision as number) > host.startRevision
251
- && host.consumer === `lifecycle-recovery:${receipt.taskId}:${receipt.id}`
251
+ && host.consumer === receipt.id
252
252
  && exactText(host.consumedAt);
253
253
  }
254
254
 
@@ -562,7 +562,7 @@ function abortActivatedDelegation(
562
562
  event.childId !== receipt.childId
563
563
  || !Number.isSafeInteger(event.stopRevision)
564
564
  || event.stopRevision <= receipt.hostEvidence.startRevision
565
- || event.consumer !== `lifecycle-recovery:${receipt.taskId}:${receipt.id}`
565
+ || event.consumer !== receipt.id
566
566
  || !exactText(event.consumedAt)
567
567
  ) {
568
568
  return fail('DELEGATION_RECOVERY_EVIDENCE_INVALID', 'recovery stop evidence does not match the activated delegation');
@@ -15,9 +15,7 @@ const COMPLETION_BACKFILL_FAMILIES = [
15
15
  'plan',
16
16
  'review-plan',
17
17
  'code',
18
- 'review-code',
19
- 'manual-validation',
20
- 'validation-run'
18
+ 'review-code'
21
19
  ] as const satisfies readonly ArtifactFamily[];
22
20
 
23
21
  type CompletionArtifactResult = {
@@ -6,7 +6,7 @@ export type TaskControlBindingEvidence = (taskId: string, binding: TaskControlBi
6
6
  export function taskFinalizationReceiptState(receipt: TaskFinalizationReceipt): 'pending' | 'unresolved' | 'complete' {
7
7
  if (receipt.lifecycle !== 'done' || receipt.taskComment === 'pending'
8
8
  || receipt.verification === 'pending' || receipt.summary === 'pending'
9
- || receipt.postSummaryVerification === 'pending' || receipt.warningProjection !== 'done') return 'pending';
9
+ || receipt.warningProjection !== 'done') return 'pending';
10
10
  if (receipt.lastError !== null || receipt.warnings.some((warning) => warning.status === 'open')) return 'unresolved';
11
11
  return 'complete';
12
12
  }