@fitlab-ai/agent-infra 0.11.3 → 0.11.5

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 (172) hide show
  1. package/README.md +17 -8
  2. package/README.zh-CN.md +17 -8
  3. package/dist/bin/internal-cli.js +1 -6
  4. package/dist/lib/decide.js +4 -41
  5. package/dist/lib/git/branch-worktree.js +45 -0
  6. package/dist/lib/internal/cli-route-inventory.js +1 -4
  7. package/dist/lib/internal/task-activity.js +7 -40
  8. package/dist/lib/internal/task-artifact.js +1 -1
  9. package/dist/lib/internal/task-event.js +4 -37
  10. package/dist/lib/internal/task-finalization.js +48 -1
  11. package/dist/lib/internal/task-ledger.js +14 -44
  12. package/dist/lib/internal/task-lifecycle.js +1 -1
  13. package/dist/lib/internal/task-operation-registry.js +2 -4
  14. package/dist/lib/internal/task-review.js +5 -37
  15. package/dist/lib/internal/task-verify.js +3 -47
  16. package/dist/lib/internal/task-warning.js +4 -40
  17. package/dist/lib/platform/completion-backfill.js +63 -17
  18. package/dist/lib/platform/pr-summary.js +4 -23
  19. package/dist/lib/platform/verification-sync.js +12 -49
  20. package/dist/lib/sandbox/control/client.js +4 -1
  21. package/dist/lib/sandbox/control/executor.js +12 -4
  22. package/dist/lib/sandbox/control/protocol.js +2 -1
  23. package/dist/lib/sandbox/control/server.js +26 -143
  24. package/dist/lib/sandbox/removal.js +15 -8
  25. package/dist/lib/sandbox/task-cleanup.js +13 -20
  26. package/dist/lib/task/activity-intent.js +1 -2
  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 +37 -28
  30. package/dist/lib/task/artifact-receipts.js +4 -1
  31. package/dist/lib/task/artifact-schema.js +8 -7
  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 +12 -168
  36. package/dist/lib/task/decision-intents.js +14 -4
  37. package/dist/lib/task/delivery.js +1 -43
  38. package/dist/lib/task/events.js +68 -73
  39. package/dist/lib/task/finalization-handoff.js +92 -0
  40. package/dist/lib/task/finalization.js +423 -154
  41. package/dist/lib/task/intermediate-cleanup.js +9 -148
  42. package/dist/lib/task/ledger-intents.js +159 -20
  43. package/dist/lib/task/lifecycle-path.js +75 -0
  44. package/dist/lib/task/lifecycle.js +60 -11
  45. package/dist/lib/task/local-artifact-finalization.js +13 -47
  46. package/dist/lib/task/orchestration.js +17 -7
  47. package/dist/lib/task/review-command.js +1 -2
  48. package/dist/lib/task/review-finalization.js +4 -18
  49. package/dist/lib/task/rework-intent.js +70 -4
  50. package/dist/lib/task/snapshot.js +6 -6
  51. package/dist/lib/task/verification-engine.js +25 -30
  52. package/dist/lib/task/verification.js +2 -13
  53. package/dist/lib/task/workflow-warning-intents.js +23 -10
  54. package/dist/lib/task/workspace-snapshot.js +7 -2
  55. package/dist/lib/task/write.js +1 -2
  56. package/dist/lifecycle-build-manifest.json +3 -3
  57. package/lib/decide.ts +5 -35
  58. package/lib/git/branch-worktree.ts +41 -0
  59. package/lib/internal/cli-route-inventory.ts +1 -3
  60. package/lib/internal/task-activity.ts +6 -34
  61. package/lib/internal/task-artifact.ts +1 -1
  62. package/lib/internal/task-event.ts +4 -37
  63. package/lib/internal/task-finalization.ts +47 -1
  64. package/lib/internal/task-ledger.ts +15 -41
  65. package/lib/internal/task-lifecycle.ts +2 -2
  66. package/lib/internal/task-operation-registry.ts +2 -4
  67. package/lib/internal/task-review.ts +5 -31
  68. package/lib/internal/task-verify.ts +3 -39
  69. package/lib/internal/task-warning.ts +3 -32
  70. package/lib/platform/completion-backfill.ts +78 -16
  71. package/lib/platform/pr-summary.ts +4 -25
  72. package/lib/platform/verification-sync.ts +13 -53
  73. package/lib/sandbox/control/client.ts +5 -1
  74. package/lib/sandbox/control/executor.ts +13 -4
  75. package/lib/sandbox/control/protocol.ts +3 -2
  76. package/lib/sandbox/control/server.ts +21 -146
  77. package/lib/sandbox/removal.ts +23 -7
  78. package/lib/sandbox/task-cleanup.ts +12 -20
  79. package/lib/task/activity-intent.ts +1 -2
  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 +59 -28
  83. package/lib/task/artifact-receipts.ts +4 -1
  84. package/lib/task/artifact-schema.ts +10 -7
  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 +17 -189
  89. package/lib/task/decision-intents.ts +11 -4
  90. package/lib/task/delivery.ts +1 -40
  91. package/lib/task/events.ts +73 -74
  92. package/lib/task/finalization-handoff.ts +104 -0
  93. package/lib/task/finalization.ts +388 -122
  94. package/lib/task/intermediate-cleanup.ts +9 -125
  95. package/lib/task/ledger-intents.ts +157 -20
  96. package/lib/task/lifecycle-path.ts +98 -0
  97. package/lib/task/lifecycle.ts +58 -13
  98. package/lib/task/local-artifact-finalization.ts +14 -56
  99. package/lib/task/orchestration.ts +26 -8
  100. package/lib/task/review-command.ts +2 -5
  101. package/lib/task/review-finalization.ts +4 -20
  102. package/lib/task/rework-intent.ts +75 -5
  103. package/lib/task/snapshot.ts +6 -6
  104. package/lib/task/verification-engine.ts +29 -30
  105. package/lib/task/verification.ts +3 -16
  106. package/lib/task/workflow-warning-intents.ts +20 -10
  107. package/lib/task/workspace-snapshot.ts +3 -2
  108. package/lib/task/write.ts +1 -4
  109. package/package.json +1 -1
  110. package/templates/.agents/rules/review-handshake.en.md +4 -4
  111. package/templates/.agents/rules/review-handshake.zh-CN.md +4 -4
  112. package/templates/.agents/skills/analyze-task/SKILL.en.md +21 -5
  113. package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +21 -5
  114. package/templates/.agents/skills/code-task/SKILL.en.md +14 -8
  115. package/templates/.agents/skills/code-task/SKILL.zh-CN.md +18 -13
  116. package/templates/.agents/skills/code-task/reference/code-rules.en.md +2 -2
  117. package/templates/.agents/skills/code-task/reference/code-rules.zh-CN.md +2 -2
  118. package/templates/.agents/skills/code-task/reference/dual-mode.en.md +4 -4
  119. package/templates/.agents/skills/code-task/reference/dual-mode.zh-CN.md +4 -4
  120. package/templates/.agents/skills/code-task/reference/report-template.en.md +7 -7
  121. package/templates/.agents/skills/code-task/reference/report-template.zh-CN.md +7 -7
  122. package/templates/.agents/skills/complete-manual-validation/config/verify.en.json +2 -2
  123. package/templates/.agents/skills/complete-manual-validation/config/verify.zh-CN.json +2 -2
  124. package/templates/.agents/skills/complete-manual-validation/reference/report-template.en.md +1 -3
  125. package/templates/.agents/skills/complete-manual-validation/reference/report-template.zh-CN.md +1 -3
  126. package/templates/.agents/skills/complete-task/SKILL.en.md +2 -2
  127. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +2 -2
  128. package/templates/.agents/skills/complete-task/config/verify.en.json +2 -2
  129. package/templates/.agents/skills/complete-task/config/verify.zh-CN.json +2 -2
  130. package/templates/.agents/skills/plan-task/SKILL.en.md +13 -10
  131. package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +13 -10
  132. package/templates/.agents/skills/review-analysis/SKILL.en.md +5 -1
  133. package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +5 -1
  134. package/templates/.agents/skills/review-analysis/reference/report-template.en.md +3 -3
  135. package/templates/.agents/skills/review-analysis/reference/report-template.zh-CN.md +3 -3
  136. package/templates/.agents/skills/review-code/SKILL.en.md +5 -1
  137. package/templates/.agents/skills/review-code/SKILL.zh-CN.md +5 -1
  138. package/templates/.agents/skills/review-code/reference/report-template.en.md +3 -3
  139. package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +3 -3
  140. package/templates/.agents/skills/review-plan/SKILL.en.md +5 -1
  141. package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +5 -1
  142. package/templates/.agents/skills/review-plan/reference/report-template.en.md +3 -3
  143. package/templates/.agents/skills/review-plan/reference/report-template.zh-CN.md +3 -3
  144. package/templates/.agents/skills/review-pr/config/verify.en.json +3 -1
  145. package/templates/.agents/skills/review-pr/config/verify.zh-CN.json +3 -1
  146. package/templates/.agents/skills/review-pr/reference/report-template.en.md +3 -3
  147. package/templates/.agents/skills/review-pr/reference/report-template.zh-CN.md +3 -3
  148. package/templates/.agents/skills/run-manual-validation/config/verify.en.json +2 -2
  149. package/templates/.agents/skills/run-manual-validation/config/verify.zh-CN.json +2 -2
  150. package/templates/.agents/skills/run-manual-validation/reference/report-template.en.md +2 -2
  151. package/templates/.agents/skills/run-manual-validation/reference/report-template.zh-CN.md +2 -2
  152. package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +18 -2
  153. package/templates/.agents/templates/task.en.md +2 -2
  154. package/templates/.agents/templates/task.zh-CN.md +2 -2
  155. package/templates/.agents/workflows/bug-fix.en.yaml +11 -6
  156. package/templates/.agents/workflows/bug-fix.zh-CN.yaml +11 -6
  157. package/templates/.agents/workflows/feature-development.en.yaml +11 -6
  158. package/templates/.agents/workflows/feature-development.zh-CN.yaml +11 -6
  159. package/templates/.agents/workflows/refactoring.en.yaml +11 -6
  160. package/templates/.agents/workflows/refactoring.zh-CN.yaml +11 -6
  161. package/dist/lib/internal/task-override.js +0 -138
  162. package/dist/lib/sandbox/control/finalization-response.js +0 -33
  163. package/dist/lib/task/artifact-recovery.js +0 -476
  164. package/dist/lib/task/artifact-repair-intent.js +0 -181
  165. package/dist/lib/task/guard-override.js +0 -31
  166. package/dist/lib/task/human-override.js +0 -1259
  167. package/lib/internal/task-override.ts +0 -144
  168. package/lib/sandbox/control/finalization-response.ts +0 -35
  169. package/lib/task/artifact-recovery.ts +0 -572
  170. package/lib/task/artifact-repair-intent.ts +0 -227
  171. package/lib/task/guard-override.ts +0 -50
  172. package/lib/task/human-override.ts +0 -1522
package/README.md CHANGED
@@ -9,7 +9,7 @@
9
9
  </p>
10
10
 
11
11
  <p align="center">
12
- <strong>From issue to merged PR in 11 commands.</strong> Define a requirement, let AI handle analysis, planning, coding, and three-stage review — you only step in when it matters.
12
+ <strong>From issue to merged PR through a risk-selected lifecycle.</strong> Every task starts with analysis, and every implementation receives an independent code review.
13
13
  </p>
14
14
 
15
15
  <p align="center">
@@ -51,14 +51,22 @@ Once initialized, open the project in your AI TUI and install the latest skills:
51
51
 
52
52
  > AI reads `.agents/.airc.json`, auto-locates the installed template root, and syncs the latest skill manifests, managed files, and registry deterministically via `sync-templates.js`.
53
53
 
54
- **Scenario**: Issue #42 reports *"Login API returns 500 when email contains a plus sign"*. Here is the full fix lifecycle — AI does the heavy lifting, you stay in control:
54
+ **Scenario**: Issue #42 reports *"Login API returns 500 when email contains a plus sign"*. Every task starts with analysis, which records one canonical path from observable facts:
55
+
56
+ | Path | Stages | Use when |
57
+ |------|--------|----------|
58
+ | Streamlined | `analyze → code → review-code` | Scope and acceptance are clear, with no separate design or document-audit decision |
59
+ | Standard | `analyze → plan → code → review-code` | The implementation needs an explicit cross-module contract, data flow, or test strategy |
60
+ | Full | `analyze → review-analysis → plan → review-plan → code → review-code` | Concrete acceptance disputes, costly interface/schema/migration choices, or real external/security boundaries require independent review |
61
+
62
+ File count, module count, or a merely possible risk does not select the full path. A later finding can return work to analysis, planning, or code when new evidence justifies it; independent `review-code` remains part of every path.
63
+
64
+ Suppose the analysis selects the **standard path** because the application-layer boundary needs an explicit plan:
55
65
 
56
66
  ```bash
57
67
  /import-issue 42 # AI reads the issue, creates a task, extracts requirements
58
- /analyze-task --task <task-id> # AI scans the codebase, finds the root cause, writes analysis.md
59
- /review-analysis --task <task-id> # An isolated reviewer checks the analysis
68
+ /analyze-task --task <task-id> # AI scans the codebase, records the selected path, writes analysis.md
60
69
  /plan-task --task <task-id> # AI proposes a fix plan
61
- /review-plan --task <task-id> # A fresh isolated reviewer checks the plan
62
70
  ```
63
71
 
64
72
  > **You review the plan and reply in natural language:**
@@ -79,7 +87,7 @@ Just fix it at the application layer in LoginService.
79
87
  /complete-task --task <task-id> # task archived
80
88
  ```
81
89
 
82
- **11 commands. 1 natural-language correction. From issue to merged PR.** That is the entire SOP — programming can have a standard operating procedure too.
90
+ **One analysis-selected path, one natural-language correction, and an independent code review.** The lifecycle keeps the evidence and review depth proportional to the task.
83
91
 
84
92
  Every command above works the same way in Claude Code, Codex, Antigravity CLI, and OpenCode. Switch tools mid-task — the workflow state follows. For what each skill does under the hood, see [Built-in AI Skills](./docs/en/skills.md).
85
93
 
@@ -193,8 +201,9 @@ The most-used lifecycle commands, in delivery order. The command prefix varies b
193
201
  |---------|---------|
194
202
  | `create-task` / `import-issue` | Start a task from a description or a GitHub Issue |
195
203
  | `run-task` | Resume the lifecycle with fresh isolated executors/reviewers when the selected client exposes verified actual model/effort evidence; Codex now has an experimental Hooks + App Server evidence channel, but orchestration remains disabled until that channel is connected to delegation receipts |
196
- | `analyze-task` → `review-analysis` | Capture scope and risks, then review the analysis |
197
- | `plan-task` → `review-plan` | Design the approach, then review the plan |
204
+ | `analyze-task` | Capture scope and risks, then select the streamlined, standard, or full path |
205
+ | `review-analysis` | Review the analysis when the selected full path requires an independent requirement checkpoint |
206
+ | `plan-task` / `review-plan` | Design the approach on standard/full paths; independently review it only on the full path |
198
207
  | `code-task` → `review-code` | Implement and test; `code-task` creates a local checkpoint, then a structured review checks that checkpoint |
199
208
  | `create-pr` → `complete-task` | Publish the approved checkpoint to the task-bound target branch, then archive after merge and final gates |
200
209
 
package/README.zh-CN.md CHANGED
@@ -9,7 +9,7 @@
9
9
  </p>
10
10
 
11
11
  <p align="center">
12
- <strong>从 Issue 到合并 PR,只需 11 条命令。</strong> 定义需求,让 AI 完成分析、方案设计、编码与三阶段审查 —— 你只需在关键节点介入。
12
+ <strong>从 Issue 到合并 PR,由风险选择相称的生命周期。</strong> 每个任务都先分析,每份实现都接受独立代码审查。
13
13
  </p>
14
14
 
15
15
  <p align="center">
@@ -50,14 +50,22 @@ agent-infra 的目标就是把这层共享基础设施标准化。它为所有
50
50
 
51
51
  > AI 读取 `.agents/.airc.json`,自动定位已安装的模板根目录,并通过 `sync-templates.js` 确定性地同步最新的 skill 清单、managed 文件和注册表。
52
52
 
53
- **场景**:Issue #42 报告 *"登录接口在邮箱包含加号时返回 500"*。以下是完整的修复流程 —— AI 执行主要工作,你掌控方向:
53
+ **场景**:Issue #42 报告 *"登录接口在邮箱包含加号时返回 500"*。每个任务都从分析开始,并由分析根据可观察事实记录一条规范路径:
54
+
55
+ | 路径 | 阶段 | 适用条件 |
56
+ |------|------|----------|
57
+ | 精简路径 | `analyze → code → review-code` | 范围和验收明确,不需要独立设计或文档审计决策 |
58
+ | 标准路径 | `analyze → plan → code → review-code` | 实现需要明确跨模块契约、数据流或测试策略 |
59
+ | 完整路径 | `analyze → review-analysis → plan → review-plan → code → review-code` | 存在具体的验收争议、高代价接口/schema/迁移选择,或真实外部/安全边界,需要独立审查 |
60
+
61
+ 文件数量、模块数量或可能存在的风险不能单独触发完整路径。后续 finding 只有在新证据成立时才会把工作返回分析、方案或实现阶段;每条路径都保留独立 `review-code`。
62
+
63
+ 假设分析认为应用层边界需要明确方案,因此选择**标准路径**:
54
64
 
55
65
  ```bash
56
66
  /import-issue 42 # AI 读取 Issue,创建任务,提取需求
57
- /analyze-task --task <task-id> # AI 扫描代码库,定位根因,输出 analysis.md
58
- /review-analysis --task <task-id> # 隔离 reviewer 审查分析产物
67
+ /analyze-task --task <task-id> # AI 扫描代码库、记录所选路径,输出 analysis.md
59
68
  /plan-task --task <task-id> # AI 提出修复方案
60
- /review-plan --task <task-id> # fresh 隔离 reviewer 审查方案
61
69
  ```
62
70
 
63
71
  > **你审查方案后用自然语言回复:**
@@ -78,7 +86,7 @@ agent-infra 的目标就是把这层共享基础设施标准化。它为所有
78
86
  /complete-task --task <task-id> # 任务归档
79
87
  ```
80
88
 
81
- **11 条命令,1 次自然语言纠正,从 Issue 到合并 PR。** 这就是完整的 SOP —— 编程也可以有标准作业流程。
89
+ **一条由分析选择的路径、一次自然语言纠正和一次独立代码审查。** 生命周期会让证据与审查深度和任务风险相称。
82
90
 
83
91
  以上每条命令在 Claude Code、Codex、Antigravity CLI、OpenCode 中完全通用。任务进行到一半切换工具,工作流状态照常延续。每个 skill 背后做了什么,见 [内置 AI Skills](./docs/zh-CN/skills.md)。
84
92
 
@@ -192,8 +200,9 @@ ai agent-client configure
192
200
  |------|------|
193
201
  | `create-task` / `import-issue` | 从描述或 GitHub Issue 创建任务 |
194
202
  | `run-task` | 当所选客户端能提供已验证的 actual model/effort 证据时,用 fresh 隔离 executor/reviewer 续跑生命周期;Codex 现已具备实验性的 Hooks + App Server 证据通道,但在该通道接入 delegation receipt 前仍不会启用 orchestration |
195
- | `analyze-task` → `review-analysis` | 明确范围与风险,再审查分析 |
196
- | `plan-task` → `review-plan` | 设计实现路径,再审查方案 |
203
+ | `analyze-task` | 明确范围与风险,再选择精简、标准或完整路径 |
204
+ | `review-analysis` | 仅在完整路径需要独立需求检查点时审查分析 |
205
+ | `plan-task` / `review-plan` | 标准和完整路径需要设计方案;只有完整路径独立审查方案 |
197
206
  | `code-task` → `review-code` | 实现并测试;`code-task` 创建本地 checkpoint,再由结构化审查检视该 checkpoint |
198
207
  | `create-pr` → `complete-task` | 将已通过审查的 checkpoint 发布到任务绑定目标分支,并在合并和最终门禁通过后归档 |
199
208
 
@@ -50,7 +50,7 @@ function taskControlTransportFailure(message, code = 'TASK_CONTROL_TRANSPORT_INV
50
50
  let controlRouted = false;
51
51
  if (!taskViewGuardFailed && !markerlessHelp && (taskControlCommand || taskWorkflowCommand || manualValidationWorkflowCommand)) {
52
52
  const transport = resolveSandboxControlTransport(process.env, {
53
- localWorkflow: command === 'task-orchestration' || taskWorkflowCommand || manualValidationWorkflowCommand
53
+ localWorkflow: command === 'task-orchestration' || command === 'task-finalization' || taskWorkflowCommand || manualValidationWorkflowCommand
54
54
  });
55
55
  switch (transport.kind) {
56
56
  case 'fail-closed': {
@@ -240,11 +240,6 @@ if (!controlRouted && !taskViewGuardFailed && internalRouteRegistered)
240
240
  await taskFinalization(process.argv.slice(3));
241
241
  break;
242
242
  }
243
- case 'task-override': {
244
- const { taskOverride } = await import('../lib/internal/task-override.js');
245
- await taskOverride(process.argv.slice(3));
246
- break;
247
- }
248
243
  case 'task-short-id': {
249
244
  const { taskShortId } = await import('../lib/internal/task-short-id.js');
250
245
  taskShortId(process.argv.slice(3));
@@ -2,15 +2,11 @@ import { VERSION } from './version.js';
2
2
  import { parseTaskScope } from './task/command-options.js';
3
3
  import { applyHumanDecision } from './task/decision-intents.js';
4
4
  import { canonicalTimestamp } from './task/write.js';
5
- import { consumeHumanOverride, failureId } from './task/human-override.js';
6
5
  import { resolveTaskContext } from './task/resolve-ref.js';
7
6
  import { TaskExecutionLockError, withTaskExecutionLock } from './task/task-execution-lock.js';
8
7
  function parseDecisionParts(parts) {
9
8
  const decision = [];
10
9
  let needsImplementation;
11
- let overrideTicket;
12
- let overrideTarget;
13
- let overrideScope;
14
10
  for (let index = 0; index < parts.length; index += 1) {
15
11
  const part = parts[index];
16
12
  if (part === '--needs-implementation') {
@@ -22,16 +18,8 @@ function parseDecisionParts(parts) {
22
18
  }
23
19
  needsImplementation = value === 'true';
24
20
  }
25
- else if (part === '--override-ticket' || part === '--override-target' || part === '--override-scope') {
26
- const value = parts[++index];
27
- if (!value)
28
- throw new Error(`${part} requires a value`);
29
- if (part === '--override-ticket')
30
- overrideTicket = value;
31
- else if (part === '--override-target')
32
- overrideTarget = value;
33
- else
34
- overrideScope = value;
21
+ else if (part?.startsWith('--')) {
22
+ throw new Error(`unknown option '${part}'`);
35
23
  }
36
24
  else {
37
25
  decision.push(parts[index]);
@@ -39,7 +27,7 @@ function parseDecisionParts(parts) {
39
27
  }
40
28
  if (decision.length === 0)
41
29
  throw new Error('decision content is required');
42
- return { decision: decision.join(' '), needsImplementation, overrideTicket, overrideTarget, overrideScope };
30
+ return { decision: decision.join(' '), needsImplementation };
43
31
  }
44
32
  export async function decide(args, options = {}) {
45
33
  let scope;
@@ -94,32 +82,7 @@ export async function decide(args, options = {}) {
94
82
  metadataProvider: () => ({ timestamp: now, agentInfraVersion: options.version ?? VERSION })
95
83
  };
96
84
  let result;
97
- const execute = async () => {
98
- let current = applyHumanDecision(request, writeOptions);
99
- if (current.status !== 'failed' || !parsedDecision.overrideTicket)
100
- return current;
101
- if (!parsedDecision.overrideTarget || !parsedDecision.overrideScope)
102
- throw new Error('override ticket requires --override-target and --override-scope');
103
- const consumed = await consumeHumanOverride({
104
- taskRef: resolved.taskId,
105
- ticketId: parsedDecision.overrideTicket,
106
- failureId: failureId('decision-intent', current.error?.code ?? 'TASK_STATE_MISMATCH'),
107
- target: parsedDecision.overrideTarget,
108
- scope: parsedDecision.overrideScope
109
- }, {
110
- ...writeOptions,
111
- effectExecutor: (capability) => {
112
- const retried = applyHumanDecision(request, { ...writeOptions, manualOverride: capability });
113
- current = retried;
114
- return retried.status === 'failed' || retried.status === 'planned'
115
- ? { code: 'OVERRIDE_EFFECT_FAILED', message: retried.status === 'planned' ? 'producer returned planned; no decision effect was committed' : `${retried.error?.code ?? 'DECISION_FAILED'}: ${retried.error?.message ?? 'manual decision effect failed'}` }
116
- : null;
117
- }
118
- });
119
- if (consumed.status === 'failed')
120
- throw new Error(consumed.error.message);
121
- return current;
122
- };
85
+ const execute = async () => applyHumanDecision(request, writeOptions);
123
86
  result = await withTaskExecutionLock(resolved.repoRoot, resolved.taskId, 'task-decision', execute);
124
87
  if (result.error)
125
88
  throw new Error(result.error.message);
@@ -0,0 +1,45 @@
1
+ import { execFileSync } from 'node:child_process';
2
+ function gitText(repositoryRoot, args) {
3
+ try {
4
+ return execFileSync('git', ['-C', repositoryRoot, ...args], {
5
+ encoding: 'utf8',
6
+ stdio: ['ignore', 'pipe', 'pipe']
7
+ }).trim();
8
+ }
9
+ catch {
10
+ return null;
11
+ }
12
+ }
13
+ function verifiedWorktreeRoot(candidate, expectedBranch, expectedCommonDirectory) {
14
+ const commonDirectory = gitText(candidate, ['rev-parse', '--path-format=absolute', '--git-common-dir']);
15
+ if (commonDirectory !== expectedCommonDirectory)
16
+ return null;
17
+ const root = gitText(candidate, ['rev-parse', '--show-toplevel']);
18
+ if (!root)
19
+ return null;
20
+ const branch = gitText(root, ['symbolic-ref', '--short', 'HEAD']);
21
+ return branch === expectedBranch ? root : null;
22
+ }
23
+ export function resolveBranchWorktree(repositoryRoot, expectedBranch) {
24
+ const commonDirectory = gitText(repositoryRoot, ['rev-parse', '--path-format=absolute', '--git-common-dir']);
25
+ if (!commonDirectory)
26
+ return null;
27
+ const currentRoot = verifiedWorktreeRoot(repositoryRoot, expectedBranch, commonDirectory);
28
+ if (currentRoot)
29
+ return currentRoot;
30
+ const records = gitText(repositoryRoot, ['worktree', 'list', '--porcelain']);
31
+ if (!records)
32
+ return null;
33
+ const expectedRef = `refs/heads/${expectedBranch}`;
34
+ for (const record of records.split(/\r?\n\r?\n/)) {
35
+ const candidate = /^worktree (.+)$/m.exec(record)?.[1];
36
+ const branch = /^branch (.+)$/m.exec(record)?.[1];
37
+ if (candidate && branch === expectedRef) {
38
+ const verifiedRoot = verifiedWorktreeRoot(candidate, expectedBranch, commonDirectory);
39
+ if (verifiedRoot)
40
+ return verifiedRoot;
41
+ }
42
+ }
43
+ return null;
44
+ }
45
+ //# sourceMappingURL=branch-worktree.js.map
@@ -50,7 +50,7 @@ export const INTERNAL_HANDLER_ROUTE_SELECTORS = Object.freeze({
50
50
  'pr-review-grade': ['decide', 'resolve-host', 'verify-artifact'],
51
51
  'platform-checks': ['inspect', 'watch', 'resolve-run', 'logs'],
52
52
  'task-context': ['resolve'],
53
- 'task-ledger': ['decision-next-id', 'stage-status', 'finding-upsert', 'finding-respond', 'finding-review', 'decision-upsert', 'rework-intent-upsert'],
53
+ 'task-ledger': ['decision-next-id', 'stage-status', 'finding-upsert', 'finding-respond', 'finding-review', 'decision-upsert', 'rework-intent-upsert', 'rework-intent-rebuild'],
54
54
  'task-warning': ['list', 'add', 'set-status'],
55
55
  'task-activity': ['pr-review-inspect', 'pr-review-start', 'pr-review-complete', 'pr-review-terminate'],
56
56
  'task-artifact': ['inspect', 'init', 'preflight', 'finalize-local'],
@@ -58,7 +58,6 @@ export const INTERNAL_HANDLER_ROUTE_SELECTORS = Object.freeze({
58
58
  'task-review': ['preflight', 'finalize-summary'],
59
59
  'task-event': ['event'],
60
60
  'task-invalidation': ['reconcile'],
61
- 'task-override': ['diagnose', 'issue', 'consume'],
62
61
  'task-lifecycle': ['intent'],
63
62
  'task-finalization': ['complete'],
64
63
  'task-short-id': ['list', 'list-verify', 'alloc', 'release', 'resolve'],
@@ -105,8 +104,6 @@ export function internalRouteSelector(command, args) {
105
104
  return args[1] ?? '';
106
105
  if (command === 'task-invalidation')
107
106
  return args[1] ?? '';
108
- if (command === 'task-override')
109
- return args[1] ?? '';
110
107
  if (command === 'task-artifact')
111
108
  return args[1] ?? '';
112
109
  if (command === 'platform-pr-review' && first(args) === 'publish') {
@@ -1,5 +1,4 @@
1
1
  import { applyPrReviewActivityIntent, inspectPrReviewActivity } from '../task/activity-intent.js';
2
- import { consumeHumanOverride, failureId, overrideDryRunConflict } from '../task/human-override.js';
3
2
  import { resolveTaskRef } from '../task/resolve-ref.js';
4
3
  import { TaskExecutionLockError, withTaskExecutionLock } from '../task/task-execution-lock.js';
5
4
  import { ensureInternalHandlerRoute, internalHandlerRoute } from './cli-route-inventory.js';
@@ -18,15 +17,14 @@ const FLAGS = {
18
17
  '--major': 'major',
19
18
  '--minor': 'minor',
20
19
  '--outcome': 'outcome',
21
- '--reason': 'reason',
22
- '--override-ticket': 'overrideTicket', '--override-target': 'overrideTarget', '--override-scope': 'overrideScope'
20
+ '--reason': 'reason'
23
21
  };
24
22
  const COMMON = ['agent', 'artifact', 'head'];
25
23
  const ALLOWED = {
26
24
  'pr-review-inspect': new Set(['kind', 'taskRef']),
27
- 'pr-review-start': new Set(['kind', 'taskRef', 'agent', 'artifact', 'head', 'dryRun', 'overrideTicket', 'overrideTarget', 'overrideScope']),
28
- 'pr-review-complete': new Set(['kind', 'taskRef', 'agent', 'artifact', 'head', 'verdict', 'blockers', 'major', 'minor', 'dryRun', 'overrideTicket', 'overrideTarget', 'overrideScope']),
29
- 'pr-review-terminate': new Set(['kind', 'taskRef', 'agent', 'artifact', 'head', 'outcome', 'reason', 'dryRun', 'overrideTicket', 'overrideTarget', 'overrideScope'])
25
+ 'pr-review-start': new Set(['kind', 'taskRef', 'agent', 'artifact', 'head', 'dryRun']),
26
+ 'pr-review-complete': new Set(['kind', 'taskRef', 'agent', 'artifact', 'head', 'verdict', 'blockers', 'major', 'minor', 'dryRun']),
27
+ 'pr-review-terminate': new Set(['kind', 'taskRef', 'agent', 'artifact', 'head', 'outcome', 'reason', 'dryRun'])
30
28
  };
31
29
  function usageFailure(message) {
32
30
  process.stdout.write(`${JSON.stringify({ status: 'failed', changed: false, error: { code: 'ACTIVITY_PAYLOAD_INVALID', message } })}\n`);
@@ -109,43 +107,14 @@ async function taskActivity(args = []) {
109
107
  return;
110
108
  }
111
109
  }
112
- const dryRunConflict = overrideDryRunConflict(values);
113
- if (dryRunConflict) {
114
- usageFailure(dryRunConflict.message);
115
- return;
116
- }
117
110
  const resolved = resolveTaskRef(taskRef);
118
111
  if (!resolved.ok) {
119
112
  usageFailure(resolved.message);
120
113
  return;
121
114
  }
122
115
  let result;
123
- let humanOverride = null;
124
116
  try {
125
- result = await withTaskExecutionLock(resolved.repoRoot, resolved.taskId, `task-activity.${kind}`, async () => {
126
- let current = applyPrReviewActivityIntent(values, { lockAlreadyHeld: true });
127
- if (current.status !== 'failed' || !values.overrideTicket)
128
- return current;
129
- if (!values.overrideTarget || !values.overrideScope) {
130
- usageFailure('override ticket requires target and scope');
131
- return current;
132
- }
133
- const consumed = await consumeHumanOverride({
134
- taskRef, ticketId: String(values.overrideTicket),
135
- failureId: failureId('activity-intent', current.error?.code ?? 'TASK_STATE_MISMATCH'),
136
- target: String(values.overrideTarget), scope: String(values.overrideScope)
137
- }, {
138
- effectExecutor: (capability) => {
139
- const retried = applyPrReviewActivityIntent(values, { lockAlreadyHeld: true, manualOverride: capability });
140
- current = retried;
141
- return retried.status === 'failed' || retried.status === 'planned'
142
- ? { code: 'OVERRIDE_EFFECT_FAILED', message: retried.status === 'planned' ? 'producer returned planned; no activity effect was committed' : `${retried.error?.code ?? 'ACTIVITY_FAILED'}: ${retried.error?.message ?? 'manual activity effect failed'}` }
143
- : null;
144
- }
145
- });
146
- humanOverride = consumed;
147
- return current;
148
- });
117
+ result = await withTaskExecutionLock(resolved.repoRoot, resolved.taskId, `task-activity.${kind}`, async () => applyPrReviewActivityIntent(values, { lockAlreadyHeld: true }));
149
118
  }
150
119
  catch (error) {
151
120
  if (!(error instanceof TaskExecutionLockError))
@@ -154,10 +123,8 @@ async function taskActivity(args = []) {
154
123
  process.exitCode = 1;
155
124
  return;
156
125
  }
157
- process.stdout.write(`${JSON.stringify(humanOverride ? { ...result, humanOverride } : result)}\n`);
158
- const overrideFailed = Boolean(humanOverride && typeof humanOverride === 'object' &&
159
- humanOverride.status === 'failed');
160
- if (result.status === 'failed' || overrideFailed)
126
+ process.stdout.write(`${JSON.stringify(result)}\n`);
127
+ if (result.status === 'failed')
161
128
  process.exitCode = 1;
162
129
  }
163
130
  export { taskActivity };
@@ -1,6 +1,6 @@
1
1
  import { parseArtifactCommand, executeArtifactCommand } from '../task/artifact-command.js';
2
2
  import { ensureInternalHandlerRoute } from './cli-route-inventory.js';
3
- const USAGE = `Usage: agent-infra-internal task-artifact <N | TASK-id> inspect --family <family>\n agent-infra-internal task-artifact <N | TASK-id> init --family <family> --artifact <artifact> [--locale <zh-CN|en>]\n agent-infra-internal task-artifact <N | TASK-id> preflight|finalize-local --family <analysis|plan|code> --artifact <artifact>\n\nInspect, initialize, preflight, or finalize a workflow artifact without changing task state.\n`;
3
+ const USAGE = `Usage: agent-infra-internal task-artifact <N | TASK-id> inspect --family <family> [--source-finding <id> --source-artifact <name> --source-sha256 <digest>]\n agent-infra-internal task-artifact <N | TASK-id> init --family <family> --artifact <artifact> [--locale <zh-CN|en>]\n agent-infra-internal task-artifact <N | TASK-id> preflight|finalize-local --family <analysis|plan|code> --artifact <artifact>\n\nInspect, initialize, preflight, or finalize workflow artifact facts.\n`;
4
4
  function taskArtifact(args = []) {
5
5
  if (!ensureInternalHandlerRoute('task-artifact', args))
6
6
  return;
@@ -1,6 +1,5 @@
1
1
  import { normalizeAgentToken, AGENT_USAGE_HINT } from '../agent-clients/tokens.js';
2
2
  import { applyTaskEvent, eventCatalog } from '../task/events.js';
3
- import { consumeHumanOverride, failureId, overrideDryRunConflict } from '../task/human-override.js';
4
3
  import { resolveTaskRef } from '../task/resolve-ref.js';
5
4
  import { TaskExecutionLockError, withTaskExecutionLock } from '../task/task-execution-lock.js';
6
5
  import { ensureInternalHandlerRoute, internalHandlerRoute } from './cli-route-inventory.js';
@@ -18,8 +17,7 @@ const FLAGS = {
18
17
  '--verdict': 'verdict', '--blockers': 'blockers', '--major': 'major',
19
18
  '--minor': 'minor', '--manual-validation': 'manualValidation', '--files-modified': 'filesModified',
20
19
  '--tests-passed': 'testsPassed', '--summary-result': 'summaryResult',
21
- '--transaction-id': 'transactionId', '--receipt-digest': 'receiptDigest', '--pr-head-sha': 'prHeadSha',
22
- '--override-ticket': 'overrideTicket', '--override-target': 'overrideTarget', '--override-scope': 'overrideScope'
20
+ '--transaction-id': 'transactionId', '--receipt-digest': 'receiptDigest', '--pr-head-sha': 'prHeadSha'
23
21
  };
24
22
  const NUMERIC = new Set(['round', 'question', 'blockers', 'major', 'minor', 'manualValidation', 'filesModified', 'testsPassed']);
25
23
  function usageFailure(message) {
@@ -68,9 +66,6 @@ export function parseTaskEventRequest(args) {
68
66
  if (!agent)
69
67
  throw new Error(`invalid --agent '${request.agent}': ${AGENT_USAGE_HINT}`);
70
68
  request.agent = agent;
71
- const dryRunConflict = overrideDryRunConflict(request);
72
- if (dryRunConflict)
73
- throw new Error(dryRunConflict.message);
74
69
  return request;
75
70
  }
76
71
  async function taskEvent(args = []) {
@@ -99,34 +94,8 @@ async function taskEvent(args = []) {
99
94
  return;
100
95
  }
101
96
  let result;
102
- let humanOverride = null;
103
97
  try {
104
- result = await withTaskExecutionLock(resolved.repoRoot, resolved.taskId, `task-event.${request.event}`, async () => {
105
- let current = applyTaskEvent(request, { lockAlreadyHeld: true });
106
- const values = request;
107
- if (current.status !== 'failed' || !values.overrideTicket)
108
- return current;
109
- if (!values.overrideTarget || !values.overrideScope) {
110
- return { ...current, humanOverride: { status: 'failed', error: { code: 'OVERRIDE_PAYLOAD_INVALID', message: 'override ticket requires target and scope' } } };
111
- }
112
- const consumed = await consumeHumanOverride({
113
- taskRef: request.taskRef,
114
- ticketId: String(values.overrideTicket),
115
- failureId: failureId('task-event', current.error?.code ?? 'EVENT_TRANSITION_INVALID'),
116
- target: String(values.overrideTarget),
117
- scope: String(values.overrideScope)
118
- }, {
119
- effectExecutor: (capability) => {
120
- const retried = applyTaskEvent(request, { lockAlreadyHeld: true, manualOverride: capability });
121
- current = retried;
122
- return retried.status === 'failed' || retried.status === 'planned'
123
- ? { code: 'OVERRIDE_EFFECT_FAILED', message: retried.status === 'planned' ? 'producer returned planned; no task event was committed' : `${retried.error?.code ?? 'EVENT_FAILED'}: ${retried.error?.message ?? 'manual task event effect failed'}` }
124
- : null;
125
- }
126
- });
127
- humanOverride = consumed;
128
- return current;
129
- });
98
+ result = await withTaskExecutionLock(resolved.repoRoot, resolved.taskId, `task-event.${request.event}`, async () => applyTaskEvent(request, { lockAlreadyHeld: true }));
130
99
  }
131
100
  catch (error) {
132
101
  if (!(error instanceof TaskExecutionLockError))
@@ -135,10 +104,8 @@ async function taskEvent(args = []) {
135
104
  process.exitCode = 1;
136
105
  return;
137
106
  }
138
- process.stdout.write(`${JSON.stringify(humanOverride ? { ...result, humanOverride } : result)}\n`);
139
- const overrideFailed = Boolean(humanOverride && typeof humanOverride === 'object' &&
140
- humanOverride.status === 'failed');
141
- if (result.status === 'failed' || overrideFailed)
107
+ process.stdout.write(`${JSON.stringify(result)}\n`);
108
+ if (result.status === 'failed')
142
109
  process.exitCode = 1;
143
110
  }
144
111
  export { taskEvent };
@@ -1,6 +1,10 @@
1
1
  import { createDirectHostExecutionContext, dispatchTaskControlOperation, parseTaskControlOperation } from '../task/control-authority.js';
2
- import { applyTaskFinalization } from '../task/finalization.js';
2
+ import { applyTaskFinalization, bindTaskFinalizationReceipt, prepareTaskFinalization } from '../task/finalization.js';
3
+ import { publishTaskFinalizationHandoff } from '../task/finalization-handoff.js';
3
4
  import { detectRepoRoot, resolveTaskRef } from '../task/resolve-ref.js';
5
+ import { verifyTaskEvent } from '../task/verification.js';
6
+ import { resolveSandboxControlTransport } from './task-operation-registry.js';
7
+ import { requestSandboxTaskFinalization, SandboxControlClientError } from '../sandbox/control/client.js';
4
8
  import { ensureInternalHandlerRoute, internalHandlerRoute } from './cli-route-inventory.js';
5
9
  const USAGE = 'Usage: agent-infra-internal task-finalization <N | TASK-id> complete --agent <agent>\n';
6
10
  function envelope(status, changed, accepted, result, error) {
@@ -59,7 +63,50 @@ async function taskFinalization(args = []) {
59
63
  ...operation,
60
64
  request: { ...operation.request, taskRef: resolved.taskId }
61
65
  };
66
+ const transport = resolveSandboxControlTransport(process.env, { localWorkflow: true });
67
+ if (transport.kind === 'sandbox-local') {
68
+ const prepared = await prepareTaskFinalization(boundOperation.request, {
69
+ repoRoot,
70
+ preflight: (request, options) => verifyTaskEvent({ ...request, event: 'complete-task.hard-preflight' }, options)
71
+ });
72
+ if (prepared.status !== 'prepared') {
73
+ process.stdout.write(envelope(prepared.status, prepared.changed, true, prepared, prepared.error));
74
+ process.exitCode = exitCode(prepared.status);
75
+ return;
76
+ }
77
+ try {
78
+ const response = requestSandboxTaskFinalization({
79
+ agent: boundOperation.request.agent,
80
+ prepareHandoff: (binding) => {
81
+ const receipt = bindTaskFinalizationReceipt(repoRoot, boundOperation.request.taskRef, binding);
82
+ return publishTaskFinalizationHandoff('/share/branch', receipt, binding);
83
+ }
84
+ });
85
+ process.stdout.write(response.stdout);
86
+ process.stderr.write(response.stderr);
87
+ process.exitCode = response.exitCode ?? 1;
88
+ return;
89
+ }
90
+ catch (error) {
91
+ const detail = error instanceof SandboxControlClientError
92
+ ? error.detail
93
+ : { code: 'SANDBOX_CONTROL_CLIENT_FAILED', message: error instanceof Error ? error.message : String(error), retryable: false };
94
+ process.stdout.write(envelope(detail.retryable ? 'blocked' : 'failed', false, false, null, detail));
95
+ process.exitCode = detail.retryable ? 2 : 1;
96
+ return;
97
+ }
98
+ }
62
99
  const result = await dispatchTaskControlOperation(createDirectHostExecutionContext({ repoRoot }), boundOperation);
100
+ if (result.status === 'prepared') {
101
+ const detail = {
102
+ code: 'TASK_FINALIZATION_DIRECT_COMMIT_REQUIRED',
103
+ message: 'direct finalization prepared external work without committing the lifecycle transition',
104
+ retryable: false
105
+ };
106
+ process.stdout.write(envelope('failed', result.changed, true, result, detail));
107
+ process.exitCode = 1;
108
+ return;
109
+ }
63
110
  process.stdout.write(envelope(result.status, result.changed, true, result, result.error));
64
111
  process.exitCode = exitCode(result.status);
65
112
  }
@@ -1,21 +1,20 @@
1
1
  import fs from 'node:fs';
2
2
  import { applyLedgerIntent } from '../task/ledger-intents.js';
3
- import { consumeHumanOverride, failureId, overrideDryRunConflict } from '../task/human-override.js';
4
3
  import { LEDGER_SECTION_MISSING_CODE, LEDGER_SECTION_MISSING_MESSAGE, isReviewStage, parseLedgerDocument, summarizeLedgerStage, validateLedgerRows } from '../task/ledger.js';
5
4
  import { resolveTaskRef } from '../task/resolve-ref.js';
6
5
  import { TaskExecutionLockError, withTaskExecutionLock } from '../task/task-execution-lock.js';
7
6
  import { ensureInternalHandlerRoute, internalHandlerRoute } from './cli-route-inventory.js';
8
- const USAGE = `Usage: agent-infra-internal task-ledger <task-ref> <intent> [intent flags] [--dry-run]\n\nIntents: finding-upsert, finding-respond, finding-review, decision-next-id, decision-upsert, rework-intent-upsert, stage-status\n`;
7
+ const USAGE = `Usage: agent-infra-internal task-ledger <task-ref> <intent> [intent flags] [--dry-run]\n\nIntents: finding-upsert, finding-respond, finding-review, decision-next-id, decision-upsert, rework-intent-upsert, rework-intent-rebuild, stage-status\n`;
9
8
  const FLAGS = {
10
9
  '--stage': 'stage', '--review-artifact': 'reviewArtifact', '--ordinal': 'ordinal',
11
10
  '--severity': 'severity', '--evidence': 'evidence', '--id': 'id', '--round': 'round',
12
11
  '--status': 'status', '--artifact': 'artifact', '--needs-implementation': 'needsImplementation',
13
12
  '--intent-id': 'intentId', '--finding-id': 'findingId', '--source-artifact': 'sourceArtifact',
14
- '--source-sha256': 'sourceSha256', '--target': 'target',
15
- '--override-ticket': 'overrideTicket', '--override-target': 'overrideTarget', '--override-scope': 'overrideScope'
13
+ '--source-sha256': 'sourceSha256', '--classification': 'classification'
16
14
  };
17
15
  const NUMERIC = new Set(['ordinal', 'round']);
18
16
  const BOOLEAN = new Set(['needsImplementation']);
17
+ const REWORK_CLASSIFICATIONS = new Set(['scope-or-requirement', 'design', 'implementation', 'human-decision', 'insufficient-evidence']);
19
18
  function usageFailure(message) {
20
19
  process.stdout.write(`${JSON.stringify({ status: 'failed', changed: false, error: { code: 'LEDGER_PAYLOAD_INVALID', message } })}\n`);
21
20
  process.stderr.write(USAGE);
@@ -40,6 +39,7 @@ async function taskLedger(args = []) {
40
39
  internalHandlerRoute('task-ledger', 'decision-next-id', kind),
41
40
  internalHandlerRoute('task-ledger', 'decision-upsert', kind),
42
41
  internalHandlerRoute('task-ledger', 'rework-intent-upsert', kind),
42
+ internalHandlerRoute('task-ledger', 'rework-intent-rebuild', kind),
43
43
  internalHandlerRoute('task-ledger', 'stage-status', kind)
44
44
  ].some(Boolean)) {
45
45
  usageFailure(`unknown ledger intent '${kind}'`);
@@ -85,17 +85,16 @@ async function taskLedger(args = []) {
85
85
  'finding-review': ['id', 'status', 'evidence'],
86
86
  'decision-next-id': [],
87
87
  'decision-upsert': ['id', 'stage', 'artifact'],
88
- 'rework-intent-upsert': ['intentId', 'findingId', 'sourceArtifact', 'sourceSha256', 'target'],
88
+ 'rework-intent-upsert': ['intentId', 'findingId', 'sourceArtifact', 'sourceSha256', 'classification'],
89
+ 'rework-intent-rebuild': [],
89
90
  'stage-status': ['stage']
90
91
  };
91
92
  const optional = {
92
93
  'finding-review': ['needsImplementation'],
93
- 'decision-upsert': ['needsImplementation']
94
+ 'decision-upsert': ['needsImplementation'],
95
+ 'rework-intent-rebuild': ['findingId', 'sourceArtifact', 'sourceSha256', 'classification']
94
96
  };
95
- const overrideFields = kind === 'stage-status' || kind === 'decision-next-id' || kind === 'rework-intent-upsert'
96
- ? []
97
- : ['overrideTicket', 'overrideTarget', 'overrideScope'];
98
- const allowed = new Set(['kind', 'taskRef', 'dryRun', ...required[kind], ...(optional[kind] ?? []), ...overrideFields]);
97
+ const allowed = new Set(['kind', 'taskRef', 'dryRun', ...required[kind], ...(optional[kind] ?? [])]);
99
98
  const unexpected = Object.keys(values).find((key) => !allowed.has(key));
100
99
  const missing = required[kind].find((key) => values[key] === undefined);
101
100
  if (unexpected) {
@@ -106,9 +105,8 @@ async function taskLedger(args = []) {
106
105
  usageFailure(`${kind} requires '${missing}'`);
107
106
  return;
108
107
  }
109
- const dryRunConflict = overrideDryRunConflict(values);
110
- if (dryRunConflict) {
111
- usageFailure(dryRunConflict.message);
108
+ if (values.classification !== undefined && !REWORK_CLASSIFICATIONS.has(String(values.classification))) {
109
+ usageFailure(`invalid rework classification '${values.classification}'`);
112
110
  return;
113
111
  }
114
112
  if (kind === 'stage-status') {
@@ -151,34 +149,8 @@ async function taskLedger(args = []) {
151
149
  return;
152
150
  }
153
151
  let result;
154
- let humanOverride = null;
155
152
  try {
156
- result = await withTaskExecutionLock(lockResolved.repoRoot, lockResolved.taskId, `task-ledger.${kind}`, async () => {
157
- let current = applyLedgerIntent(values);
158
- if (current.status !== 'failed' || !values.overrideTicket)
159
- return current;
160
- if (!values.overrideTarget || !values.overrideScope) {
161
- usageFailure('override ticket requires target and scope');
162
- return current;
163
- }
164
- const consumed = await consumeHumanOverride({
165
- taskRef,
166
- ticketId: String(values.overrideTicket),
167
- failureId: failureId('ledger-intent', current.error?.code ?? 'TASK_STATE_MISMATCH'),
168
- target: String(values.overrideTarget),
169
- scope: String(values.overrideScope)
170
- }, {
171
- effectExecutor: (capability) => {
172
- const retried = applyLedgerIntent(values, { manualOverride: capability });
173
- current = retried;
174
- return retried.status === 'failed'
175
- ? { code: 'OVERRIDE_EFFECT_FAILED', message: `${retried.error?.code ?? 'LEDGER_FAILED'}: ${retried.error?.message ?? 'manual ledger effect failed'}` }
176
- : null;
177
- }
178
- });
179
- humanOverride = consumed;
180
- return current;
181
- });
153
+ result = await withTaskExecutionLock(lockResolved.repoRoot, lockResolved.taskId, `task-ledger.${kind}`, async () => applyLedgerIntent(values));
182
154
  }
183
155
  catch (error) {
184
156
  if (!(error instanceof TaskExecutionLockError))
@@ -187,10 +159,8 @@ async function taskLedger(args = []) {
187
159
  process.exitCode = 1;
188
160
  return;
189
161
  }
190
- process.stdout.write(`${JSON.stringify(humanOverride ? { ...result, humanOverride } : result)}\n`);
191
- const overrideFailed = Boolean(humanOverride && typeof humanOverride === 'object' &&
192
- humanOverride.status === 'failed');
193
- if (result.status === 'failed' || overrideFailed)
162
+ process.stdout.write(`${JSON.stringify(result)}\n`);
163
+ if (result.status === 'failed')
194
164
  process.exitCode = 1;
195
165
  }
196
166
  export { taskLedger };