@fitlab-ai/agent-infra 0.11.2 → 0.11.4

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 (162) hide show
  1. package/README.md +17 -8
  2. package/README.zh-CN.md +17 -8
  3. package/dist/bin/internal-cli.js +7 -2
  4. package/dist/lib/agent-clients/adapter.js +3 -0
  5. package/dist/lib/agent-clients/adapters/codex-lifecycle/recovery.js +234 -0
  6. package/dist/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.js +8 -35
  7. package/dist/lib/agent-clients/adapters/codex-lifecycle/store.js +1 -34
  8. package/dist/lib/agent-clients/adapters/codex.js +10 -0
  9. package/dist/lib/internal/cli-route-inventory.js +1 -1
  10. package/dist/lib/internal/manual-validation.js +22 -27
  11. package/dist/lib/internal/sandbox-control.js +1 -43
  12. package/dist/lib/internal/task-artifact.js +1 -1
  13. package/dist/lib/internal/task-finalization.js +41 -1
  14. package/dist/lib/internal/task-ledger.js +13 -5
  15. package/dist/lib/internal/task-lifecycle.js +1 -1
  16. package/dist/lib/internal/task-operation-registry.js +7 -2
  17. package/dist/lib/platform/completion-backfill.js +63 -17
  18. package/dist/lib/platform/issue-comments.js +2 -31
  19. package/dist/lib/platform/pr-summary.js +7 -2
  20. package/dist/lib/sandbox/control/client.js +3 -44
  21. package/dist/lib/sandbox/control/executor.js +14 -79
  22. package/dist/lib/sandbox/control/protocol.js +5 -64
  23. package/dist/lib/sandbox/control/server.js +49 -343
  24. package/dist/lib/sandbox/control/state.js +1 -7
  25. package/dist/lib/sandbox/control/task-view.js +1 -2
  26. package/dist/lib/sandbox/task-cleanup.js +13 -20
  27. package/dist/lib/task/artifact-command.js +11 -4
  28. package/dist/lib/task/artifact-lifecycle.js +217 -85
  29. package/dist/lib/task/artifact-operations.js +1 -26
  30. package/dist/lib/task/artifact-receipts.js +4 -1
  31. package/dist/lib/task/artifact-schema.js +1 -0
  32. package/dist/lib/task/artifact-selection.js +25 -0
  33. package/dist/lib/task/capabilities.js +158 -39
  34. package/dist/lib/task/commands/status.js +9 -2
  35. package/dist/lib/task/control-authority.js +37 -38
  36. package/dist/lib/task/control-recovery.js +3 -42
  37. package/dist/lib/task/decision-intents.js +12 -0
  38. package/dist/lib/task/delegation-receipts.js +2 -2
  39. package/dist/lib/task/delivery.js +1 -43
  40. package/dist/lib/task/events.js +80 -64
  41. package/dist/lib/task/finalization-artifacts.js +1 -3
  42. package/dist/lib/task/finalization-state.js +1 -1
  43. package/dist/lib/task/finalization.js +319 -234
  44. package/dist/lib/task/ledger-intents.js +157 -15
  45. package/dist/lib/task/lifecycle-path.js +75 -0
  46. package/dist/lib/task/lifecycle.js +11 -1
  47. package/dist/lib/task/local-artifact-finalization.js +1 -20
  48. package/dist/lib/task/manual-validation-receipt.js +8 -3
  49. package/dist/lib/task/orchestration.js +29 -13
  50. package/dist/lib/task/review-finalization.js +3 -16
  51. package/dist/lib/task/rework-intent.js +70 -4
  52. package/dist/lib/task/verification-engine.js +20 -30
  53. package/dist/lib/task/verification.js +1 -0
  54. package/dist/lib/task/workflow-warning-intents.js +21 -6
  55. package/dist/lib/task/workspace-snapshot.js +7 -2
  56. package/dist/lifecycle-build-manifest.json +3 -3
  57. package/lib/agent-clients/adapter.ts +46 -0
  58. package/lib/agent-clients/adapters/codex-lifecycle/recovery.ts +297 -0
  59. package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.ts +13 -33
  60. package/lib/agent-clients/adapters/codex-lifecycle/store.ts +1 -44
  61. package/lib/agent-clients/adapters/codex.ts +10 -0
  62. package/lib/internal/cli-route-inventory.ts +1 -1
  63. package/lib/internal/manual-validation.ts +22 -26
  64. package/lib/internal/sandbox-control.ts +0 -42
  65. package/lib/internal/task-artifact.ts +1 -1
  66. package/lib/internal/task-finalization.ts +40 -1
  67. package/lib/internal/task-ledger.ts +12 -5
  68. package/lib/internal/task-lifecycle.ts +2 -2
  69. package/lib/internal/task-operation-registry.ts +8 -4
  70. package/lib/platform/completion-backfill.ts +78 -16
  71. package/lib/platform/issue-comments.ts +1 -35
  72. package/lib/platform/pr-summary.ts +10 -2
  73. package/lib/sandbox/control/client.ts +5 -68
  74. package/lib/sandbox/control/executor.ts +14 -81
  75. package/lib/sandbox/control/protocol.ts +7 -83
  76. package/lib/sandbox/control/server.ts +47 -362
  77. package/lib/sandbox/control/state.ts +1 -9
  78. package/lib/sandbox/control/task-view.ts +1 -2
  79. package/lib/sandbox/task-cleanup.ts +12 -20
  80. package/lib/task/artifact-command.ts +14 -4
  81. package/lib/task/artifact-lifecycle.ts +236 -85
  82. package/lib/task/artifact-operations.ts +1 -26
  83. package/lib/task/artifact-receipts.ts +4 -1
  84. package/lib/task/artifact-schema.ts +1 -0
  85. package/lib/task/artifact-selection.ts +53 -0
  86. package/lib/task/capabilities.ts +154 -40
  87. package/lib/task/commands/status.ts +11 -2
  88. package/lib/task/control-authority.ts +41 -48
  89. package/lib/task/control-recovery.ts +3 -43
  90. package/lib/task/decision-intents.ts +9 -0
  91. package/lib/task/delegation-receipts.ts +2 -2
  92. package/lib/task/delivery.ts +1 -40
  93. package/lib/task/events.ts +82 -64
  94. package/lib/task/finalization-artifacts.ts +1 -3
  95. package/lib/task/finalization-state.ts +1 -1
  96. package/lib/task/finalization.ts +310 -216
  97. package/lib/task/ledger-intents.ts +155 -15
  98. package/lib/task/lifecycle-path.ts +98 -0
  99. package/lib/task/lifecycle.ts +15 -2
  100. package/lib/task/local-artifact-finalization.ts +2 -25
  101. package/lib/task/manual-validation-receipt.ts +8 -2
  102. package/lib/task/orchestration.ts +56 -27
  103. package/lib/task/review-finalization.ts +3 -16
  104. package/lib/task/rework-intent.ts +75 -5
  105. package/lib/task/verification-engine.ts +19 -30
  106. package/lib/task/verification.ts +2 -1
  107. package/lib/task/workflow-warning-intents.ts +18 -6
  108. package/lib/task/workspace-snapshot.ts +3 -2
  109. package/package.json +1 -1
  110. package/templates/.agents/rules/lifecycle-orchestration.en.md +19 -5
  111. package/templates/.agents/rules/lifecycle-orchestration.zh-CN.md +19 -5
  112. package/templates/.agents/rules/review-handshake.en.md +4 -4
  113. package/templates/.agents/rules/review-handshake.zh-CN.md +4 -4
  114. package/templates/.agents/skills/analyze-task/SKILL.en.md +11 -4
  115. package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +11 -4
  116. package/templates/.agents/skills/code-task/SKILL.en.md +14 -8
  117. package/templates/.agents/skills/code-task/SKILL.zh-CN.md +18 -13
  118. package/templates/.agents/skills/code-task/reference/code-rules.en.md +2 -2
  119. package/templates/.agents/skills/code-task/reference/code-rules.zh-CN.md +2 -2
  120. package/templates/.agents/skills/code-task/reference/dual-mode.en.md +4 -4
  121. package/templates/.agents/skills/code-task/reference/dual-mode.zh-CN.md +4 -4
  122. package/templates/.agents/skills/code-task/reference/report-template.en.md +1 -1
  123. package/templates/.agents/skills/code-task/reference/report-template.zh-CN.md +1 -1
  124. package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +2 -2
  125. package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +2 -2
  126. package/templates/.agents/skills/complete-task/SKILL.en.md +7 -8
  127. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +7 -8
  128. package/templates/.agents/skills/plan-task/SKILL.en.md +13 -10
  129. package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +13 -10
  130. package/templates/.agents/skills/review-analysis/SKILL.en.md +5 -1
  131. package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +5 -1
  132. package/templates/.agents/skills/review-code/SKILL.en.md +5 -1
  133. package/templates/.agents/skills/review-code/SKILL.zh-CN.md +5 -1
  134. package/templates/.agents/skills/review-plan/SKILL.en.md +5 -1
  135. package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +5 -1
  136. package/templates/.agents/skills/run-manual-validation/SKILL.en.md +2 -2
  137. package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +2 -2
  138. package/templates/.agents/skills/run-task/SKILL.en.md +9 -8
  139. package/templates/.agents/skills/run-task/SKILL.zh-CN.md +9 -8
  140. package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +18 -2
  141. package/templates/.agents/templates/task.en.md +2 -2
  142. package/templates/.agents/templates/task.zh-CN.md +2 -2
  143. package/templates/.agents/workflows/bug-fix.en.yaml +11 -6
  144. package/templates/.agents/workflows/bug-fix.zh-CN.yaml +11 -6
  145. package/templates/.agents/workflows/feature-development.en.yaml +11 -6
  146. package/templates/.agents/workflows/feature-development.zh-CN.yaml +11 -6
  147. package/templates/.agents/workflows/refactoring.en.yaml +11 -6
  148. package/templates/.agents/workflows/refactoring.zh-CN.yaml +11 -6
  149. package/dist/lib/sandbox/control/finalization-response.js +0 -35
  150. package/dist/lib/sandbox/control/task-workflow.js +0 -74
  151. package/dist/lib/sandbox/control/workflow-executor.js +0 -142
  152. package/dist/lib/task/lifecycle-recovery.js +0 -549
  153. package/dist/lib/task/recovery-warning.js +0 -19
  154. package/dist/lib/task/workflow-command.js +0 -21
  155. package/dist/lib/task/workflow-dispatch.js +0 -213
  156. package/lib/sandbox/control/finalization-response.ts +0 -37
  157. package/lib/sandbox/control/task-workflow.ts +0 -95
  158. package/lib/sandbox/control/workflow-executor.ts +0 -156
  159. package/lib/task/lifecycle-recovery.ts +0 -691
  160. package/lib/task/recovery-warning.ts +0 -21
  161. package/lib/task/workflow-command.ts +0 -22
  162. package/lib/task/workflow-dispatch.ts +0 -214
@@ -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,14 +23,10 @@ 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,
29
+ commitPreparedTaskFinalization,
33
30
  type TaskFinalizationRequest,
34
31
  type TaskFinalizationResult
35
32
  } from './finalization.ts';
@@ -39,12 +36,10 @@ import {
39
36
  type TaskLifecycleRequest,
40
37
  type TaskLifecycleResult
41
38
  } 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';
39
+ import type {
40
+ AgentClientLifecycleRecoveryRequest as LifecycleRecoveryRequest,
41
+ AgentClientLifecycleRecoveryResult as LifecycleRecoveryResult
42
+ } from '../agent-clients/adapter.ts';
48
43
  import { locateActivityLog } from './activity-log.ts';
49
44
  import { resolveTaskRef, TASK_ID_RE } from './resolve-ref.ts';
50
45
  import { TaskExecutionLockError, withTaskExecutionLock } from './task-execution-lock.ts';
@@ -542,7 +537,6 @@ export type TaskControlExecutionContext =
542
537
  generation: string;
543
538
  manifestPath: string;
544
539
  requestId: string;
545
- diagnosticLog?: OrchestrationDiagnosticLogger;
546
540
  controllerBinding?: TaskControlControllerBinding | null;
547
541
  lifecycleRecoveryAttestation?: LifecycleRecoveryAttestationV1 | null;
548
542
  }>;
@@ -631,7 +625,6 @@ export function createSandboxExecutorExecutionContext(params: Readonly<{
631
625
  generation: string;
632
626
  manifestPath: string;
633
627
  requestId: string;
634
- diagnosticLog?: OrchestrationDiagnosticLogger;
635
628
  controllerBinding?: TaskControlControllerBinding | null;
636
629
  lifecycleRecoveryAttestation?: LifecycleRecoveryAttestationV1 | null;
637
630
  }>): TaskControlExecutionContext {
@@ -652,7 +645,6 @@ export function createSandboxExecutorExecutionContext(params: Readonly<{
652
645
  generation: params.generation,
653
646
  manifestPath: absolute('manifestPath', params.manifestPath),
654
647
  requestId: params.requestId,
655
- ...(params.diagnosticLog === undefined ? {} : { diagnosticLog: params.diagnosticLog }),
656
648
  ...(params.controllerBinding === undefined ? {} : { controllerBinding: binding(params.controllerBinding) }),
657
649
  ...(params.lifecycleRecoveryAttestation === undefined ? {} : {
658
650
  lifecycleRecoveryAttestation: params.lifecycleRecoveryAttestation === null
@@ -697,11 +689,7 @@ function domainOptions(
697
689
  ): OrchestrationOptions {
698
690
  return {
699
691
  ...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 } : {})
692
+ repoRoot: context.repoRoot
705
693
  };
706
694
  }
707
695
 
@@ -725,7 +713,17 @@ async function applyLifecycleWithAuthority(
725
713
  }
726
714
  const execute = async (): Promise<(TaskLifecycleResult | LifecycleRecoveryResult) & { humanOverride?: unknown }> => {
727
715
  if (request.intent === 'recover-started') {
728
- return recoverStartedLifecycleUnderLock(request as LifecycleRecoveryRequest, { repoRoot: context.repoRoot });
716
+ const adapter = isAgentClientId(request.agent)
717
+ ? getAgentClientAdapter(request.agent).orchestrationAdapter
718
+ : undefined;
719
+ if (!adapter?.recoverStarted) {
720
+ return {
721
+ status: 'no-op', changed: false, targetState: 'active',
722
+ requestRef: request.taskRef, intent: 'recover-started', taskId: null,
723
+ receiptId: null, childId: null, error: null
724
+ };
725
+ }
726
+ return adapter.recoverStarted(request as LifecycleRecoveryRequest, { repoRoot: context.repoRoot });
729
727
  }
730
728
  const lifecycleResult = applyTaskLifecycle(request, { repoRoot: context.repoRoot });
731
729
  if (lifecycleResult.status !== 'failed' || !request.overrideTicket) return lifecycleResult;
@@ -761,7 +759,12 @@ async function applyLifecycleWithAuthority(
761
759
  } catch (error) {
762
760
  if (!(error instanceof TaskExecutionLockError)) throw error;
763
761
  if (request.intent === 'recover-started') {
764
- return recoveryFailure(request as LifecycleRecoveryRequest, 'owner-unknown', error.code, error.message);
762
+ return {
763
+ status: 'owner-unknown', changed: false, targetState: 'active',
764
+ requestRef: request.taskRef, intent: 'recover-started', taskId,
765
+ receiptId: null, childId: null,
766
+ error: { code: error.code, message: error.message }
767
+ };
765
768
  }
766
769
  return taskLifecycleFailure(request, { code: error.code, message: error.message }, taskId);
767
770
  }
@@ -790,8 +793,10 @@ function orchestration(
790
793
  requestedModel: input.requestedModel as string | undefined,
791
794
  requestedReasoningEffort: input.requestedReasoningEffort as string | undefined,
792
795
  };
793
- if (prepareInput.client === 'codex') {
794
- return prepareCodexOrchestrationDelegation(operation.taskRef, prepareInput, {
796
+ const prepareDelegation = getAgentClientAdapter(prepareInput.client)
797
+ .orchestrationAdapter?.prepareDelegation;
798
+ if (prepareDelegation) {
799
+ return prepareDelegation(operation.taskRef, prepareInput, {
795
800
  repoRoot: context.repoRoot,
796
801
  orchestrationOptions: options,
797
802
  });
@@ -856,7 +861,8 @@ export function dispatchTaskControlOperation(
856
861
  }
857
862
  if (operation.family === 'task-finalization') {
858
863
  operationTaskId(context, operation.request.taskRef);
859
- return applyTaskFinalization(operation.request, {
864
+ const finalize = context.source === 'sandbox-executor' ? commitPreparedTaskFinalization : applyTaskFinalization;
865
+ return finalize(operation.request, {
860
866
  repoRoot: context.repoRoot,
861
867
  ...(context.source === 'sandbox-executor' ? {
862
868
  controlBinding: { generation: context.generation, requestId: context.requestId }
@@ -898,7 +904,7 @@ function operationInvalid(message: string): never {
898
904
 
899
905
  const LIFECYCLE_FLAGS = new Set([
900
906
  '--agent', '--reason', '--unblock-condition', '--note', '--alert-number', '--staging-dir', '--issue-number',
901
- '--stage', '--round', '--artifact', '--override-ticket', '--override-target', '--override-scope', '--dry-run'
907
+ '--auto', '--override-ticket', '--override-target', '--override-scope', '--dry-run'
902
908
  ]);
903
909
 
904
910
  const FINALIZATION_FLAGS = new Set(['--agent']);
@@ -909,7 +915,7 @@ const ORCHESTRATION_FLAGS = new Set([
909
915
  '--parent-id', '--before-fingerprint', '--stage', '--round', '--artifact', '--role',
910
916
  '--native-agent', '--child-id', '--spawn-mode', '--actual-model', '--actual-reasoning-effort',
911
917
  '--model-fallback-reason', '--reasoning-effort-fallback-reason', '--exit-code', '--after-fingerprint',
912
- '--changed-paths', '--code', '--message', '--recoverable', '--git-worktree-root'
918
+ '--changed-paths', '--code', '--message', '--recoverable'
913
919
  ]);
914
920
 
915
921
  function parseValues(
@@ -920,7 +926,7 @@ function parseValues(
920
926
  const values: Record<string, string | boolean> = {};
921
927
  for (let index = start; index < args.length; index += 1) {
922
928
  const flag = args[index]!;
923
- if (flag === '--dry-run') {
929
+ if (flag === '--dry-run' || flag === '--auto') {
924
930
  if (!flags.has(flag)) operationInvalid(`unknown option '${flag}'`);
925
931
  if (values[flag] !== undefined) operationInvalid(`duplicate option '${flag}'`);
926
932
  values[flag] = true;
@@ -978,28 +984,18 @@ export function parseTaskControlOperation(
978
984
  ...(value(values, '--override-scope') ? { overrideScope: value(values, '--override-scope') } : {}),
979
985
  ...(value(values, '--alert-number') ? { alertNumber: Number(value(values, '--alert-number')) } : {}),
980
986
  ...(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') } : {}),
987
+ ...(values['--auto'] === true ? { auto: true } : {}),
984
988
  ...(values['--dry-run'] === true ? { dryRun: true } : {})
985
989
  };
986
990
  if (intent === 'recover-started') {
987
- const recoveryFlags = new Set(['--agent', '--stage', '--round', '--artifact', '--reason']);
991
+ const recoveryFlags = new Set(['--agent', '--auto']);
988
992
  for (const flag of Object.keys(values)) {
989
993
  if (!recoveryFlags.has(flag)) operationInvalid(`option '${flag}' is not supported for recover-started`);
990
994
  }
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');
995
+ if (values['--auto'] !== true) operationInvalid('--auto is required for recover-started');
996
+ return { family, request: input as unknown as TaskLifecycleControlRequest };
997
+ } else if (values['--auto'] !== undefined) {
998
+ operationInvalid('--auto is only supported for recover-started');
1003
999
  }
1004
1000
  return { family, request: input as unknown as TaskLifecycleControlRequest };
1005
1001
  }
@@ -1092,8 +1088,5 @@ export function parseTaskControlOperation(
1092
1088
  if (recoverable !== 'true' && recoverable !== 'false') operationInvalid('--recoverable must be true or false');
1093
1089
  input.code = value(values, '--code'); input.message = value(values, '--message'); input.recoverable = recoverable === 'true';
1094
1090
  }
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 } : {}) };
1091
+ return { family, taskRef, intent: parsedIntent, input };
1099
1092
  }
@@ -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
 
@@ -17,6 +17,7 @@ import { extractSection, extractSubSection, findSectionHeading } from './section
17
17
  import { captureTaskWriteMetadata, writeTask } from './write.ts';
18
18
  import type { TaskMutation, TaskOperationSummary, TaskWriteOptions } from './write.ts';
19
19
  import { allowsManualOverride } from './guard-override.ts';
20
+ import { consumeHumanDecisionReworkIntents, parseReworkIntentDocument, reworkIntentMutation } from './rework-intent.ts';
20
21
 
21
22
  type HumanDecisionRequest = {
22
23
  taskRef?: string;
@@ -177,6 +178,14 @@ function applyHumanDecision(request: HumanDecisionRequest, options: TaskWriteOpt
177
178
  kind: 'section', aliases: ACTIVITY_ALIASES, heading: activity.heading,
178
179
  body: appendActivityEntry(activity, { time: metadata.timestamp, step: 'Human Decision', agent: 'human', note: `${row.id} decided → ${recordId}` })
179
180
  });
181
+ try {
182
+ const parsedRework = parseReworkIntentDocument(content);
183
+ if (!parsedRework.ok) return failed('DECISION_DOCUMENT_INVALID', parsedRework.message, resolved.taskId, row.id);
184
+ const consumed = consumeHumanDecisionReworkIntents(parsedRework.intents, row.id, metadata.timestamp);
185
+ if (consumed.changed) mutations.push(reworkIntentMutation(content, consumed.intents));
186
+ } catch (error) {
187
+ return failed('DECISION_DOCUMENT_INVALID', error instanceof Error ? error.message : String(error), resolved.taskId, row.id);
188
+ }
180
189
  const writeResult = writeTask({ taskRef: resolved.taskId, expectedState: stateOverride ? resolved.state : 'active', mutations, dryRun: request.dryRun }, {
181
190
  ...options,
182
191
  taskLocation: { repoRoot: resolved.repoRoot, taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, state: resolved.state },
@@ -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');
@@ -4,8 +4,7 @@ import path from 'node:path';
4
4
 
5
5
  import { parseTaskFrontmatter } from './frontmatter.ts';
6
6
  import { resolveTaskRef } from './resolve-ref.ts';
7
- import { resolveDeliveryTarget, resolveDiffBase, resolveTargetHead, validateBaseRef, validateRemote } from './delivery-target.ts';
8
- import { extractReviewBaseline, extractReviewDiffBase, extractReviewTargetHead, extractReviewedHead, findAuthoritativeReviewCodeArtifact } from './review-fingerprint.ts';
7
+ import { resolveDeliveryTarget, validateBaseRef, validateRemote } from './delivery-target.ts';
9
8
  import { captureTaskWriteMetadata, writeTask } from './write.ts';
10
9
  import { withRepositoryMutationLock, withTaskExecutionLock } from './task-execution-lock.ts';
11
10
 
@@ -47,40 +46,6 @@ function validateBranch(cwd: string, branch: string): boolean {
47
46
  return git(cwd, ['check-ref-format', `refs/heads/${branch}`]).status === 0;
48
47
  }
49
48
 
50
- function validateReviewTargetCompatibility(
51
- repoRoot: string,
52
- taskDir: string,
53
- target: { remote: string; baseRef: string },
54
- reviewedHead: string
55
- ): { ok: true } | { ok: false; code: string; message: string } {
56
- const artifact = findAuthoritativeReviewCodeArtifact(taskDir);
57
- if (!artifact.ok || !artifact.path) return { ok: false, code: 'DELIVERY_REVIEW_REQUIRED', message: 'A current review-code artifact is required before delivery' };
58
- let content: string;
59
- try { content = fs.readFileSync(artifact.path, 'utf8'); }
60
- catch (error) { return { ok: false, code: 'DELIVERY_REVIEW_REQUIRED', message: error instanceof Error ? error.message : String(error) }; }
61
- const savedReviewedHead = extractReviewedHead(content) || extractReviewBaseline(content);
62
- const savedTargetHead = extractReviewTargetHead(content);
63
- const savedDiffBase = extractReviewDiffBase(content);
64
- if (!savedReviewedHead || !savedTargetHead || !savedDiffBase) {
65
- return { ok: false, code: 'DELIVERY_REVIEW_TARGET_INVALID', message: 'review-code must save reviewed head, target head, and diff base before delivery' };
66
- }
67
- if (savedReviewedHead !== reviewedHead) {
68
- return { ok: false, code: 'DELIVERY_REVIEW_REQUIRED', message: 'review-code reviewed head does not match last_reviewed_commit' };
69
- }
70
- const saved = resolveDiffBase(repoRoot, reviewedHead, savedTargetHead);
71
- if (!saved.ok || saved.diffBase !== savedDiffBase) {
72
- return { ok: false, code: 'DELIVERY_REVIEW_TARGET_INVALID', message: saved.ok ? 'saved review diff base does not match saved target head' : saved.message };
73
- }
74
- const currentTarget = resolveTargetHead(repoRoot, target);
75
- if (!currentTarget.ok) return { ok: false, code: currentTarget.code, message: currentTarget.message };
76
- const current = resolveDiffBase(repoRoot, reviewedHead, currentTarget.head!);
77
- if (!current.ok) return { ok: false, code: current.code, message: current.message };
78
- if (current.diffBase !== savedDiffBase) {
79
- return { ok: false, code: 'DELIVERY_REVIEW_TARGET_CHANGED', message: 'current delivery target changed the reviewed diff base; re-run review-code' };
80
- }
81
- return { ok: true };
82
- }
83
-
84
49
  function failure(taskId: string | null, code: string, message: string, retryable = false, status: 'failed' | 'blocked' = retryable ? 'blocked' : 'failed'): TaskBranchDeliveryResult {
85
50
  return { status, changed: false, taskId, remoteHead: null, localHead: null, state: null, error: { code, message, retryable } };
86
51
  }
@@ -115,10 +80,6 @@ function deliverUnlocked(
115
80
  const status = git(repoRoot, ['status', '--porcelain=v1']);
116
81
  if (status.status !== 0) return failure(taskId, 'DELIVERY_GIT_INSPECT_FAILED', status.stderr.trim() || 'Unable to inspect Git state');
117
82
  if (status.stdout.trim()) return failure(taskId, 'DELIVERY_WORKTREE_DIRTY', 'Working tree must be clean before branch delivery');
118
- const reviewed = frontmatter.last_reviewed_commit ?? '';
119
- if (reviewed !== localHead) return failure(taskId, 'DELIVERY_REVIEW_REQUIRED', 'Local HEAD must equal last_reviewed_commit before delivery');
120
- const reviewTarget = validateReviewTargetCompatibility(repoRoot, taskDir, target.value, reviewed);
121
- if (!reviewTarget.ok) return failure(taskId, reviewTarget.code, reviewTarget.message, reviewTarget.code.includes('UNAVAILABLE'), reviewTarget.code.includes('UNAVAILABLE') ? 'blocked' : 'failed');
122
83
  const ref = `refs/heads/${branch}`;
123
84
  const remoteOutput = git(repoRoot, ['ls-remote', '--refs', target.value.remote, ref]);
124
85
  if (remoteOutput.status !== 0) return failure(taskId, 'DELIVERY_REMOTE_UNAVAILABLE', remoteOutput.stderr.trim() || 'Unable to inspect the delivery remote', true);