关于 delegate
用于通过准备、派发和结算流程协调子 Agent 实现工作的技能,面向已有待执行功能任务的编排者。它强调任务上下文完整、使用独立 worktree,并在验收时验证结果。
主要功能
- 01准备并固定任务批次
- 02向子 Agent 派发任务
- 03在 git worktree 中隔离工作
- 04结算并验证任务结果
- 05按任务风险选择模型等级
适用场景
- 01并行处理独立实现任务
- 02派发功能工作流任务
- 03集成前验证委派成果
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ lvlup-sw/ exarchos --skill delegate安装后在 Claude Code 中输入/delegate确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/adaptive-orchestration.mdagent-teams-saga.mdfix-mode.mdfixer-prompt.mdfixer-prompt.md.test.shimplementer-prompt.mdimplementer-prompt.md.test.shparallel-strategy.mdpbt-patterns.mdrationalization-refutation.mdstate-management.mdtesting-patterns.mdtroubleshooting.mdworked-example.mdworkflow-steps.mdworktree-enforcement.md详细介绍
委派技能
将实现任务分派给子 Agent,并提供适当的上下文、工作树隔离和 TDD 要求。此技能遵循三步流程:准备、分派、结算。
prepare 会将工作流中尚未完成的任务编译为一个不可变的任务包——任务图、每项任务的结果契约与验证条款,以及用于评判工作的权威依据——并将其固定。子 Agent 批量运行任务,期间不进行任何治理调用。settle 会将它们的结果作为一个批次提交:根据固定的任务包裁定每项声明,自行运行每项已接受任务的验证,并将任务标记为完成。随后,编排器会合并工作树并完成状态转换。
该任务包针对 feature 工作流编译。工作流类型为 prepare 时会拒绝执行(WORKFLOW_TYPE_UNSUPPORTED),并改用本技能末尾附录中的原始流程。
触发条件
在以下情况下启用此技能:
- 用户运行
{{COMMAND_PREFIX}}delegate命令 - 实现计划已就绪,且其中包含可提取的任务
- 用户希望将工作并行分派给多个子 Agent
例外——oneshot 工作流完全跳过委派。 oneshot 操作手册会在主 Agent 的上下文中运行会话内 TDD 循环,不分派子 Agent,也不进行审核阶段。如果 workflowType === "oneshot",不要调用此技能——轻量级流程请参阅 @skills/oneshot/SKILL.md。
核心原则
每项任务使用全新上下文(强制要求)
每个子 Agent 都必须从干净、完整独立的上下文开始。正如 Anthropic 多 Agent 协作最佳实践所述:
- 不得假设共享状态。 每条子 Agent 提示词都必须包含完整的任务描述、文件路径、TDD 要求和验收标准。绝不要说“参阅计划”或“如前所述”。
- 不得交叉依赖其他 Agent。 除非计划中明确通过依赖边进行排序,否则子 Agent A 不得依赖子 Agent B 的输出。
- 隔离工作树。 每个子 Agent 都在自己的
git worktree中运行。在同一工作树中运行并行 Agent 会破坏分支状态。
违反此原则的合理化模式收录于 references/rationalization-refutation.md。
委派模式
默认的 subagent 模式使用运行时的生成原语 {{TASK_TOOL}} 来分派每项任务。
在 Claude Code(以及任何未来声明 team:agent-teams 的运行时)中,还可以使用 agent-team 模式——Task 调用会绑定到 team_name,以便进行交互式多窗格协作。
| 模式 | 机制 | 最适合 |
|---|---|---|
subagent(默认) | 运行时生成原语 | 1~3 项独立任务、CI、无头模式 |
agent-team | 使用 team_name 的 Task | 3 项以上相互依赖的任务、交互式会话 |
**自动检测:**存在 tmux 和 CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS 时使用 agent-team,否则使用 subagent。使用 {{COMMAND_PREFIX}}delegate --mode subagent|agent-team 可覆盖此设置。
模型选择——推理层级,而非版本
任务的层级是 capsule 的 settlementContract.taskVerification[taskId].riskTier——在批次编译时根据计划冻结(规划器的 **Risk Tier:** 标记优先;否则由文件和层级启发式规则决定)。通过 agents.tier-models 根据该值解析模型层级(默认值为 low → haiku、medium → sonnet、high → opus;操作员可在 .exarchos.yml 中覆盖,且会进行单调性验证,确保更高的风险层级绝不会解析为更弱的模型)。如果任务没有对应的项(例如在批次之外派发修复任务),则省略 model,继承会话默认值。
如果运行时仍会提供 recommendedModel,它表示的是推理层级(haiku | sonnet | opus),而非模型版本。
以下三条规则构成完整策略:
- 绝不在任何地方写入模型版本——无论是派发指令、Agent 规范、技能还是提示词。不要使用
claude-*-YYYYMMDD,不要使用-latest,也不要使用次版本号。内容中固定的版本字符串会成为第二个模型权威,悄无声息地凌驾于操作员的层级策略之上,并在下一次发布时过时。传递层级别名,让运行框架根据自身当前的目录解析它。 - 模型强度取决于风险,而非角色。 Agent 的选择决定角色(实现者/修复者/审查者/脚手架);层级阶梯决定强度。在 Agent 规范中固定模型 ID 是缺陷——因此所有随附的规范都声明
model: inherit。 - 如果需要版本,请查阅目录,不要凭记忆。 在派发时,从运行框架自身的模型列表中解析当前的层级→版本映射。根据训练数据回忆模型信息,本身就会过时;猜测标识符,最好的情况是安全失败,最坏的情况是悄然降级。
unless otherwise specified 指在 .exarchos.yml 中明确设置操作员覆盖项(agents.default-model、agents.models、agents.tier-models),而不是在派发时自行判断。
派发前的架构发现
派发前,先查询决策手册,对工作进行分类并选择合适的策略:
- 任务复杂度: 调用
exarchos_orchestrate({ action: "runbook", id: "task-classification" }),获取认知复杂度分类树。低复杂度任务可使用脚手架 Agent 规范,以加快执行速度。 - 派发策略: 调用
exarchos_orchestrate({ action: "runbook", id: "dispatch-decision" }),获取派发策略(并行还是顺序、团队规模、隔离模式)。
步骤 1:准备
步骤 1——编译批次
exarchos_orchestrate({ action: "prepare", featureId: "<featureId>", repoRoot: "<absolute path of the worktree that holds baseRef>" })
prepare 会读取工作流计划,并将其就绪前沿——所有尚未完成且其 blockedBy 任务均已完成的任务——编译到一个 capsule 中,在托管状态下按摘要固定,并在与其记录相同的提交中逐一发布每个任务的 task.assigned 事件——此前不会发生任何调用。保留回执:settle 以 capsuleVersion 为键,而每个子 Agent 的数据包都基于 capsule 构建。
repoRoot 为必填项,且必须是绝对路径。传入包含 baseRef 的工作树:即检出集成分支的工作树。prepare 从该检出中读取仓库、配置和不变量目录,而不是从服务端所在目录读取。它会在 capsule 中固定仓库和检出(settlementContract.scope),并为每个任务固定 baseRef 的提交(baseCommit)。
| 在封装中 | 你从中读取的内容 |
|---|---|
graph.tasks[]、graph.dependencies[]、graph.joins[] | 批次及其必须遵循的运行顺序——dependencies 边 { from, to } 表示 to 必须等待 from |
settlementContract.taskVerification[taskId] | 任务的 riskTier 和 boundaryTouching,它们根据计划冻结——模型层级以及结算将运行的关卡;任务的 baseRef,即集成分支,杀伤性探测会以此测量任务的差异;以及任务的 baseCommit,即 prepare 时 baseRef 的提交,任务的工作树从这里开始 |
settlementContract.scope | 仓库(repository,即其 git 公共目录)、prepare 读取的检出(checkout),以及该检出配置文件的摘要(configDigest)。结算会拒绝不在此范围内的已声明工作树,也会拒绝 prepare 后发生变化的配置 |
knowledge.patterns[] | 批次中每个层级的一条陈述,列出这些关卡——将其原样插入工作者的提示词 |
contracts.taskResults[taskId] | 任务必须报告的内容:worktreePath(必填)、branch,以及其来源信息(files、implements、tests、acceptanceTestRef) |
contracts.deviationEnvelope | 当封装假设被证明有误时,工作者可提出的内容(invalidated-assumption、missing-context)——而不是猜测——以及其中哪些类型属于重大偏差(materialDeviationKinds:invalidated-assumption),因此接受此类偏差会修订设计 |
authority.invariants[]、authority.escalationBoundaries[] | 评判工作的依据,以及必须上报而不能自行决定的事项——粘贴到每个任务包中 |
authority.delegatedDecisions[] | 封装留给工作者决定的事项,每项都有一个 id。声明可以用其中一个 ID 作为 rationale 记录选择。将它们粘贴到每个任务包中 |
knowledge.rationale[] | 设计的权威记录;对于基于修订后设计(design-v2 或更高版本)编译的封装,还包括此后的已接受变更:每项变更用一条陈述说明由谁决定、给出提案时所提的变更,以及偏差说明了什么;最多列出八项变更,之后用一条陈述统计其余变更 |
executionProfile.capabilities | 此运行时必须具备的条件才能运行到这里;prepare 已经拒绝了不符合要求的运行时 |
拒绝项,均未编译任何内容:
| 代码 | 含义 | 操作 |
|---|---|---|
WORKFLOW_TYPE_UNSUPPORTED | 不是功能工作流 | 采用基本流程(附录) |
PHASE_NOT_PREPARABLE | 不在 delegate 阶段 | 先进行阶段转换 |
NOTHING_TO_PREPARE | 所有计划任务均已完成 | 转换到审查阶段 |
NO_READY_TASKS | 任务仍待处理,但每项任务都在等待某个尚未完成的任务(消息中会列出这些任务) | 先解决或修复阻塞任务;互相等待的任务构成循环,需要在计划中打破 |
BASE_UNRESOLVED | 工作流未记录集成分支(或分支名称不是安全的引用名称),因此没有任务的基准 | exarchos_workflow({ action: "update", featureId: "<featureId>", updates: { "synthesis.integrationBranch": "<branch the tasks fork from>" } }),然后再次执行 prepare |
BASE_UNRESOLVED | 检出仓库中没有集成分支的任何提交,或批次任务的某个已完成阻塞任务落在基准不包含的提交上(消息中会指出两者) | 创建该分支,或传入包含它的 repoRoot。合入阻塞任务,然后再次执行 prepare。在 serialize_merge 之外通过压缩合并或变基合入的阻塞任务,需要有记录的 merge.executed 行(references/troubleshooting.md) |
SCOPE_UNRESOLVED | repoRoot 不在 git 工作树中,或位于与最新 capsule 不同的仓库;检出的配置无法加载;已注册目录无法解析;或配置未将 check_static_analysis 设为阻塞项(消息中会指出具体情况) | 修复消息中指出的问题,然后再次执行 prepare(references/troubleshooting.md) |
UNKNOWN_DEPENDENCY、INVALID_TASK_ID、INVALID_TASK_STAMP、CAPSULE_UNSOUND | 计划无法表示为批次 | 修复计划,然后再次执行 prepare |
RUNTIME_UNFIT | 此运行时缺少结算所需的能力,无法验证该批次(消息中会指出具体情况) | 停止;不要派发——该批次永远无法在此处结算 |
REVISION_UNREADABLE | capsule 将说明某个设计修订所接受的更改,但包含这些更改的结算包(消息中会指出)无法读取,或缺少该修订列出的偏差或已接受的决策 | 在运行包存储中恢复该包,然后再次执行 prepare。没有绕过办法:在包恢复前,工作流会停在此处 |
输入未变时,重试会返回已记录的 capsule;如果计划、集成分支或基准提交发生变化,使用了其他检出,编辑了配置文件或设计记录,或记录了新的设计修订,则会编译下一个版本,并标记编译时的设计版本(identity.designVersion、design-v<N>)。每一轮都要执行 prepare,不能每个工作流只执行一次:capsule 只包含就绪的前沿,因此第一轮结算后才会编译第二轮的 capsule。
Worktree
每项任务都在其独立的关联 worktree 中执行。该 worktree 由宿主在原生隔离环境中创建,或通过 setup_worktree 按规范路径 .worktrees/<taskId>-<taskName> 布置。任务绝不会在 prepare 读取的检出目录或仓库的主 worktree 中运行。settle 和 task_complete 都会拒绝这两种情况(the-checkout),因为已准备的工作流中的每项任务都必须在自己的关联 worktree 中运行。在原生隔离环境中,必须在任何 Agent 编辑文件之前,确认每个 Agent 的工作目录都位于 .worktrees/ 下(检查其首次报告的 pwd);如果 Agent 位于共享检出目录,则停止该 Agent,手动为其创建 worktree(git worktree add --detach .worktrees/<taskId>-<taskName> <baseCommit>,使用 capsule 中该任务的 baseCommit),之后才允许其编辑。手动创建的 worktree 处于分离状态,因此 Agent 的基础步骤会为任务创建分支。任务声明中携带的路径就是 worktree 路径,结算时也会对此路径进行验证。
工作者从 <baseCommit> 开始。在入口检查完成后、任何文件改动之前,实现者或脚手架构建者会运行 git switch -c <own branch> <baseCommit>(其 Agent 定义中的基础步骤),这样每次运行时,其 worktree 都会基于该提交。<own branch> 必须是当前尚未被任何分支占用的名称。它不能是 setup_worktree 或 git worktree add -b 创建的 worktree 分支,因为分支已存在时,git 会拒绝执行 git switch -c。修复者会继续使用该任务之前的分支(见下文“修复失败任务”)。如果 worktree 的 HEAD 未包含 <baseCommit>(base-not-held),或者 worktree 位于 baseRef 分支上(on-the-base-branch),结算都会拒绝。终止探针会根据 baseRef 测量任务的差异。每个 worktree 都必须保持干净:如果 git status --porcelain 的输出不为空(uncommitted-changes),结算会拒绝。链接的 node_modules 需要使用忽略规则 node_modules,末尾不能有斜杠。
提取任务
capsule 固定的是条款;计划包含的是正文。根据实现计划,为每个 capsule 任务提取:
- 完整任务说明(直接粘贴,不得引用外部文件)
- 要创建或修改的文件,使用相对于 worktree 根目录的路径(例如
src/foo.ts);不得使用仓库父目录中的绝对路径,也不得包含逃出 worktree 根目录的..序列。这两种路径形式都会解析到 Agent worktree 当前工作目录之外,并会悄悄将更改写入主 worktree。这是平台无关的防护措施,在每种运行环境中都必须遵守。 - 测试文件路径(相对于 worktree)和预期的测试名称
- 对其他任务的依赖项,取自
graph.dependencies,该字段是计划中blockedBy编译后的结果 - 基于属性的测试标志(
testingStrategy.propertyTests)
如需查看此流程的完整示例,请参阅 references/worked-example.md。
第 2 步:分派
使用 references/implementer-prompt.md 作为模板,构建子 Agent 提示词。每个提示词都必须包含完整的任务上下文——这是新鲜上下文原则的实际应用。
构建提示词
适用于支持原生 Agent 定义的运行环境:
实现者 Agent 定义已包含系统提示词、模型、隔离设置、技能、钩子和记忆。分派提示词应仅包含任务专属上下文:
- 完整任务说明(需求、验收标准)
- 工作目录(第 1 步中的 worktree 路径)
- 要创建或修改的文件路径,以及测试文件路径
- 质量提示(如有)
- 当
propertyTests: true时,包含 PBT 标志
完整提示词模板(默认):
对于每项任务:
- 在实现者提示词模板中填写任务专属详情
- 将
Working Directory设置为第 1 步中的 worktree 路径 - 在 Quality Signals 部分中加入质量提示(如有)
- 当
propertyTests: true时,加入references/pbt-patterns.md中的 PBT 部分 - 加入
references/testing-patterns.md中的测试模式
提示词中的 capsule 条款
每个数据包都会原样包含 capsule 中的内容:
- 任务的验证条款——其
riskTier和boundaryTouching,以及该层级对应的knowledge.patterns[]声明。这样,worker 就知道结算会对其工作树运行哪些门禁(低风险层级任务:静态分析;中风险层级任务:还包括测试充分性 kill probe;涉及边界的任务:还会运行契约漂移检查),并编写确实可能失败的测试,而不是走过场。这些条款还会给出基准:提示词会在集成分支尖端所在的位置写明<baseCommit>,即任务的baseCommit,worker 会从该提交开始创建自己的分支。 - 结果契约——
contracts.taskResults[taskId]字段。worker 的完成报告就是其声明:worktreePath、branch、files、implements、tests。无需填写证据字段:结算运行门禁时会记录证据。未完成任务的 worker 应报告状态和原因,不填写任何结果字段(Build the batch,Step 3)。 - 权限依据——
authority.invariants[]和authority.escalationBoundaries[],以及 偏差范围:如果 capsule 的某项假设被证明有误,worker 应报告偏差(从contracts.deviationEnvelope.allowedDeviationKinds中选择deviationKind,说明哪些内容不成立,并提出变更方案),而不是悄悄绕过。出现偏差会暂停批次,等待人工处理;悄悄绕过则会在结算时被拒绝,或者更糟,被接受。 - 委派的决策——
authority.delegatedDecisions[],其中每项均包含id及其声明。worker 可以自行做出这些选择,无需审批。它可以在报告中以理由说明某项选择,并注明对应的id。理由不能注明其他id:委派决策之外的选择应作为偏差报告。 - 已接受的设计变更——在根据修订后的设计编译的 capsule 中,
knowledge.rationale[]声明会遵循当前设计。worker 从干净的上下文开始,因此数据包会向它说明有哪些变更。
派发时使用该数据包,而不是静态 Agent 默认提示。 随附的 agents/implementer.md 固定写入了一条中风险层级说明(为预先绑定命名 Agent 的运行时提供的独立备用方案)。只有在不存在 capsule 条款时(例如派发修复任务),才原样使用该说明。层级是 capsule 中的纯数据;这里不涉及按工作流类型分支处理。
决策手册
对于派发策略决策,请查询决策手册:
exarchos_orchestrate({ action: "runbook", id: "dispatch-decision" })
此手册提供结构化标准,用于判断并行还是顺序派发、确定团队规模,以及处理失败升级。
并行派发
使用运行时原生的 spawn 原语,在单条消息中派发所有相互独立的任务,使其并行运行。
{{SPAWN_AGENT_CALL agent="implementer" description="Implement task-001: [title]" prompt="Task-specific context: requirements, file paths, acceptance criteria"}}
注意: 在派发载荷中加入
references/implementer-prompt.md中的完整 implementer 提示词模板,确保启动的 Agent 拥有独立完整的上下文——会预先将 implementer 提示词绑定到命名 Agent 的运行时会自动丢弃重复内容。
并行分组策略和模型选择请参阅 references/parallel-strategy.md。
Agent Teams 派发
使用 --mode agent-team 时,请遵循 references/agent-teams-saga.md 中的 6 步 saga。该 saga 要求先发出事件再执行副作用:每个步骤都必须先发出事件,再执行副作用。
Agent Teams 的事件发出契约:完整载荷格式和补偿协议请参阅 references/agent-teams-saga.md。
事件发出契约(必需)
委派阶段需要发出以下事件(由 check-event-emissions 检查):
| 事件 | 触发时机 | 发出方 |
|---|---|---|
team.spawned | 创建团队后、派发前 | Orchestrator |
team.task.planned | 计划中的每项任务(使用 batch_append) | Orchestrator |
team.teammate.dispatched | 每个子 Agent 启动后 | Orchestrator |
task.progressed | 每个 TDD 阶段(red/green/refactor)结束后 | 子 Agent |
team.disbanded | 所有子 Agent 完成后 | Orchestrator |
完整的事件架构和发出顺序请参阅 references/agent-teams-saga.md。
由运行时代表你发出,绝不可由你发出:task.assigned(prepare 时与其记录在同一提交中;在原语路径中,prepare_delegation 会在读取就绪状态之前发出)、task.completed 和 task.failed(由 task_complete 和 task_fail 直接发出,或作为叶节点结算流程的一部分发出)。
注意:
task.progressed事件由子 Agent 在 TDD 执行期间发出,而非由 Orchestrator 发出。Orchestrator 只发出团队生命周期事件。
验证责任契约(每项声明仅由一方负责)
每项验证声明都恰好只有一个责任方。重复验证你不负责的声明并非纵深防御,而是重复劳动——会增加此轮的成本,并且当两次验证结果不一致时,难以确定哪次才是权威结果。
| 声明 | 责任方 | 运行位置 | 其他所有人 |
|---|---|---|---|
| “此任务的行为已有测试覆盖,且测试能够失败” | 结算流程 | settle 会针对每项已接受的任务,在进程内按照任务完成手册执行,并在该声明所指的工作树中、按 capsule 锁定的级别运行 | 实现者在自己的工作树中运行测试并报告;它不会运行任何 Exarchos 门禁。负责人阅读验证结果,不重新运行门禁 |
| “此任务的差异干净(类型、lint、契约、模拟对象)” | 结算流程 | 同一组合阶段 | 同上 |
| “整轮任务没有发生级联问题” | 负责人 | 每轮边界仅一次——每轮合并完成后运行 check_integration_suite | 实现者绝不运行累积套件 |
| “此轮任务已完成” | 结算流程 + 转换守卫 | 已结算的批次会使所有已接受的任务都完成;all-tasks-complete 允许进行状态转换 | 无需单独检查完成状态 |
以下两点约束了各手册的执行方式:
task_complete是任务完成手册的最后一步,而结算流程会运行该手册:所有阻塞性门禁都通过后,任务才会被记录为完成,无论由谁推动完成。exarchos_orchestrate({ action: "runbook", id: "task-completion" })会列出结算流程组合执行的步骤;你无需运行这些步骤。check_integration_suite是轮次边界的兜底检查,而非逐任务门禁。根据其自身的操作说明,它在每轮合并后恰好运行一次。逐任务的级联风险由该任务自身范围内的门禁覆盖。
负责人的唯一独立验证是抽查——阅读结算流程的验证结果和已记录的证据,并且最多抽查一项自己有具体理由怀疑的声明。全面重新运行逐任务验证链属于违反契约。
第 3 步:收集并结算
监控子 Agent
使用运行时的结果收集原语获取后台任务结果(根据运行时不同,这可能是逐项轮询/等待,也可能是内联回复):
{{SUBAGENT_RESULT_API}}
构建批次
根据每个子 Agent 的完成报告,构建一个声明。字段必须来自结果契约,不得包含其他字段——未声明的字段会被视为问题:
const claims = reports.map((r) => ({
taskId: r.taskId,
fields: {
worktreePath: r.worktreePath, // required: where the work is
branch: r.branch,
files: r.files,
implements: r.implements,
tests: r.tests,
},
evidence: [], // settlement records the evidence; a claim cites none
}))
切勿相信实现者的自我评估。 声明说明工作所在的位置;是否已完成由结算流程判定。报告存在偏差的工作者应归入 deviations({ deviationKind, statement, affectedTasks?, proposedChange? }),不得将其塞入一份粉饰过的声明中。
未完成的任务会获得状态,而不是结果。 claim 可包含可选的 status。默认值为 completed。如果没有可供结算验证的任务结果,请发送其他三个值之一:
status | 发送时机 |
|---|---|
failed | 工作人员尝试了该任务,但没有可供验证的结果 |
blocked | 在任务之外的某项内容发生变化之前,任务无法继续 |
skipped | 本次运行未尝试该任务 |
const notCompleted = {
taskId: "<taskId>",
status: "blocked", // or "failed", or "skipped"
reason: "<why the task is not completed>",
}
使用这三个值之一的 claim 需要包含不超过 280 个字符的 reason。此类 claim 不包含 fields、evidence 或 rationale。已完成的 claim 不包含 reason。如果 claim 违反其中任一规则,结算会将其作为无效输入(INVALID_INPUT)拒绝,且不进行任何裁定。
对此类 claim,结算不会运行任何门禁,也不会据此记录任务完成。任务仍未解决,且不会因该 claim 而完成。结算仍会验证批次中已完成的 claim(见下文的 corrective-capsule)。状态仅存在于 claim 上:结算不会记录 task.failed 行,tasks[].status 也不会更改。如果流中已显示任务完成,则已记录的完成状态保持不变,且该 claim 不会产生任何判定。
委派权限范围内的选择应作为 rationale 写入 claim。 capsule 会列出留给工作人员决定的事项(authority.delegatedDecisions),每项都带有一个 id。prepare 编译的 capsule 会列出两项:implementation-approach 和 execution-order。已完成的 claim 可以使用其中一个 id 记录选择:
const withChoice = {
taskId: r.taskId,
fields: { worktreePath: r.worktreePath /* and the other fields of the result contract */ },
evidence: [],
rationale: [
{ delegatedDecision: "implementation-approach", statement: "<the choice, in the words of the worker>" },
],
}
每个 claim 最多记录四项选择,每条 statement 最多 280 个字符。rationale 不会改变任何门禁:无论是否附带 rationale,结算都会对任务执行相同的验证。它不是证据,也不会使带有判定结果的 claim 获得通过。capsule 未列出的 id 会被判定为 rationale-not-delegated,并导致批次被拒绝。委派决策范围之外的选择属于偏离,因此应写入 deviations。
偏离应说明发生了什么变化,以及涉及哪些任务。 proposedChange 由工作人员提供,按其报告填写;affectedTasks 则由编排器填写,因为只有编排器掌握计划。每个失败的假设报告一项偏离;同一假设的报告应合并为一项:每个批次最多包含十六项偏离,每项偏离最多列出三十二个任务。如果两项偏离的 statement 不同,应为它们填写不同的 statement——在响应预算内返回的回执会按 id、kind、statement 和受影响任务列出偏离,因此,如果两项偏离仅在 proposed change 上不同,负责决策的人看到的内容会完全相同。以摘要形式返回的回执(见下文的 Settle)仅按 id 和 kind 列出偏离;其 statement 和受影响任务见结算包,其中由 bundleRefs 指明。
设计修订将以 affectedTasks 中的任务为对象,并且修订只命名未完成的任务。结算会拒绝以下三类条目,随后直接拒绝批次,而不是将其暂存:
- 计划显示已完成的任务(
deviation-names-finished-task) - 本批次声明为已完成的任务(
deviation-names-claimed-task) - 计划中不存在的 id(
deviation-unknown-task)
已完成的工作不会重新打开——应将其返工作为新任务进行规划,并命名该新任务。
如果工作人员自己的任务依赖于错误的假设,应将该任务报告为 blocked。 该任务的 claim 应将状态设为 blocked 并提供 reason,且偏离应在 affectedTasks 中列出该任务。只有当 claim 将该任务报告为 failed、blocked 或 skipped 时,偏离才能列出同一批次中的任务。此类 claim 不会返回结果,因此偏离会改变仍待完成的工作。决策轮次会将该任务保留为未解决状态(见下文的 deviation-pending)。
结算前验证工作树状态:工作树中 git status 显示有未提交更改,意味着存在声明中未提及的工作。结算会拒绝此类工作树(uncommitted-changes)。提交任务的文件,移除每次门禁运行遗留的文件,不要提交这些文件。
结算
胶囊要求的每项任务(settlementContract.requiredResults)都需要一条声明,除非流中已显示该任务完成。提交的批次至少包含一条声明。提交批次一次,并使用你选择的 ID。该 ID 就是结算键:
exarchos_orchestrate({
action: "settle",
featureId: "<featureId>",
capsuleVersion: <capsuleVersion>, // from the prepare receipt
batchId: "<featureId>:wave-1",
claims,
deviations: [],
})
客户端错误或中断不会停止调用。 服务端不会读取客户端的取消请求,因此第一次调用仍可能运行至结束。然后:
- 在同一会话中使用相同的批次 ID 重试。服务端一次只会为每个批次 ID 运行一个调用。重试会等待第一次调用完成,然后获取第一次调用记录的判定结果。
- 从其他会话或 CLI 重试会在另一个服务端进程中运行。它不会等待。它可能会在第一次调用仍在运行时,在同一工作树上再次运行任务的门禁。
- 在判定结果返回之前,不要在任务工作树中编辑、提交或分派工作。每项任务的探测都会在其工作树中还原任务源文件,然后将其快照写回受跟踪的文件。此期间的更改会丢失,此期间的提交则会包含已还原的源文件。
结算首先会根据胶囊读取每条声明和每项偏差。如果胶囊固定了范围,结算还会在任何门禁运行之前,通过 git 检查每条已完成声明的工作树:工作树超出范围时,该任务会出现 worktree-out-of-scope 发现项(见下文)。任何会使批次过期、遭拒或暂缓的发现项都会停止批次,结算随后不会进行任何验证。如果没有发现项停止批次,结算会通过执行器运行每条无发现项的已完成声明的验证。报告状态的声明不会停止批次,其任务也不会接受验证。如果流中已显示某项任务完成,结算不会再次运行该任务。回执会在一次调用中列出所有原因:findings 列出每项故障,verification[] 列出每项任务的环节(operationId、outcome、failedLeaf)。
超出响应预算的回执会以摘要形式返回。摘要始终保留轮次、designRevision、每项待处理偏差(ID 和类型)以及每项决策(ID 和决策)。在 corrective-capsule 下,摘要还会保留未解决任务的数量(counts.unresolved)。摘要会保留第一页发现项(firstPage,不含消息),以及最多四个 verification[] 结果,失败项优先。每个失败项都包含其 operationId 和 failedLeaf。每项未解决任务都会以 taskId 和 cause 的形式保留。为符合预算,摘要会先删除发现项页面,再删除验证结果。然后从末尾开始删除未解决任务条目,counts.unresolvedOmitted 会说明省略的数量。bundleRefs 指向的结算包会保留完整的发现项和验证结果。
回执会列出一种结果,每种结果都有对应的下一次调用:
| 结果 | 结算验证内容 | 下一次调用 |
|---|---|---|
settled | 每项必需任务均已完成 | 无 |
accepted-with-rationale | 每项必需任务均已完成,且有一条声明记录了委派的选择 | 无 |
corrective-capsule | 已通过的任务均已完成,且至少有一项任务尚未解决 | 在同一胶囊上使用新的 batchId 结算一个批次,其中仅包含 unresolved 任务 |
rejected | 无 | 使用新的 batchId 结算修正内容 |
deviation-pending | 无 | 使用相同的 batchId 结算,且仅包含 decisions |
recompile-required | 无 | 准备后,在新胶囊下结算 |
Settlement 会在各个分段完成后、提交过程中再次读取流。如果两次读取之间行发生变化,结果可能会改变。在读取发生变化前已由某个分段验证通过的任务仍保持完成。因此,recompile-required 或 rejected 回执的 verification[] 中可能列出 Settlement 在读取发生变化前验证通过的任务。 |
在 settled 或 accepted-with-rationale 后,转到步骤 4。带有 designRevision 的回执会使下一次调用进入各个结果下的 recompile-required(见下文的 designRevision)。
无论结果如何,settle 都不会编译任何 capsule。只有 prepare 会编译。设计将 settled 称为“已接受”,将 deviation-pending 称为“需要决策”。它将 accepted-with-rationale 所记录的选择称为获准的偏离。保留这些记录名称,因为已存储的行、bundle 和重放的回执都会携带这些名称。
settled — 每项必需任务都记录为已完成(task.completed,来自该原语路径所使用的叶节点)。除非流中已显示任务完成,否则已认领的任务在此批次中通过了其门禁。未认领的必需任务此前已完成。转换守卫读取的状态文档处于稳定状态。转到步骤 4。
accepted-with-rationale — 此批次已结算,且获准的认领记录了一项委托选择(见上文“构建批次”)。与 settled 一样,每项必需任务都记录为已完成,且运行了相同的门禁。execution.settled 行通过任务及其决策 ID(rationales)列出每项选择。bundleRefs 指定的结算 bundle 会保留每条说明。回执中没有用于记录这些选择的字段,因为该结果本身表示认领记录了一项选择。转到步骤 4。
发现项会先于理由决定结果。因此,含有未解决任务的批次属于 corrective-capsule,其行仍会列出已通过任务的选择。该批次的修正结果为 settled,除非其自身的某项认领记录了选择。
corrective-capsule — 至少有一项任务未解决。Settlement 已验证此批次,且已通过的任务已完成(acceptedTasks)。这些任务会保持完成。该结果沿用设计中的名称,但 settle 不会编译任何内容。
查看 unresolved。其中每项待修正任务都有一个条目,包含 taskId 和 cause。cause 表示针对该任务的发现项类型:
cause | 含义 | 修正前的操作 |
|---|---|---|
verification-failed | 该任务的一项门禁已中止。条目还包含该分段的 failedLeaf 和 operationId | 将修复人员派遣到该任务的工作树(见下文“修复失败任务”) |
worktree-out-of-scope | 已认领的工作树违反 capsule 的某项范围规则,因此未运行任何门禁。发现消息会指出违反的规则(unobservable、not-a-worktree-root、other-repository、the-checkout、on-the-base-branch、other-branch、base-not-held、uncommitted-changes 或 shared-worktree)以及恢复方法 | 按照消息说明操作:从 baseRef 的 baseCommit 创建独立工作树,或提交该任务列出的文件,并移除每个门禁运行时留下的文件。references/troubleshooting.md 对每条规则都有说明 |
task-failed | 认领报告该任务为 failed | 派遣修复人员,或重新派遣该任务 |
task-blocked | 认领报告该任务为 blocked | 移除阻碍。如果某项偏离指定了该任务,回执可能会带有 designRevision(见下文) |
task-skipped | 认领报告该任务为 skipped | 派遣该任务 |
若在叶节点保留其契约的情况下于门控叶节点停止,则会得到 verification-failed。未能运行到判定结果的门控也会得到 verification-failed:准入被拒绝、没有处理程序,或处理程序运行失败。片段也可能在完成叶节点、运行手册未列出的叶节点处停止,或因叶节点违反其契约(INTENT_EMISSION_CONTRACT_VIOLATED)而停止(包括在门控叶节点处)。这类停止不属于门控判定结果。所有片段运行完毕后,结算会再次读取完成记录。若此时流记录显示已停止的任务已完成,则以完成状态为准:该任务已完成,且 verification[] 会将其列为 already-complete。否则,非门控判定结果的停止会导致调用失败,且不会产生判定结果(见下文“错误”)。 |
已停止片段的发现消息会指出叶节点及停止原因。已报告状态的发现消息会给出工作器的原因。在摘要中,每个条目仅包含 taskId 和 cause,且所有发现都没有消息。bundleRefs 指向的结算包会保存每条消息。
要读取已停止片段的回执,请发送结算针对该任务发出的调用。从该任务的 verification[] 条目或 unresolved 条目中获取 operationId。只能使用该 operationId,因为使用其他 ID 会在结算之外重新运行整个片段,包括 task_complete。达到上限的摘要不会在 unresolved 条目中保留 operationId,因此此时请从 bundleRefs 指向的结算包的 verification 列表中获取。请从胶囊中该任务的条款(settlementContract.taskVerification[taskId])获取 riskTier、boundaryTouching 和 baseRef。从批次中获取 capsuleVersion,并从声明中获取其他参数:
exarchos_orchestrate({
action: "execute_intent",
intent: "task-completion",
featureId: "<featureId>",
operationId: "<operationId>", // of the segment of the task
args: {
taskId: "<taskId>",
worktreePath: "<worktreePath>", // from the claim
branch: "<branch>", // from the claim, only when the claim has one
riskTier: "<riskTier>", // from the capsule terms of the task
boundaryTouching: <boundaryTouching>,
baseRef: "<baseRef>",
result: claimFields, // each field of the claim, in the key order of the claim
capsuleVersion: <capsuleVersion>, // the capsule of the batch
},
})
此调用不会重新运行任何内容,而是返回已记录的回执。对于已停止的片段,回执为代码 INTENT_SEGMENT_FAILED、叶节点的消息以及 error.intentReceipt。其他参数,或 result 中键的不同顺序,都会构成不同的请求。此时调用会因 INTENT_REPLAY_DIGEST_MISMATCH 失败,且不会运行任何内容。
然后,在同一胶囊上结算一个新的批次 ID,并且只声明未解决的任务:
exarchos_orchestrate({
action: "settle",
featureId: "<featureId>",
capsuleVersion: <capsuleVersion>, // the same capsule
batchId: "<featureId>:wave-1:correction-1",
claims: unresolvedClaims, // one claim for each `unresolved` entry
})
已记录的完成状态满足必需结果,因此已完成的任务无需声明。更正操作的回执会在 verification[] 中将每个此类任务列为 already-complete。
如果任务仍未完成,请再次以其状态和原因声明该任务。该批次仍是 corrective-capsule,且任务仍处于未解决状态。结算不会限制更正次数,因此请自行决定何时停止并咨询人工。
如果摘要省略了未解决条目(counts.unresolvedOmitted 大于 0),请再次声明所有必需任务。对于已完成的任务,结算不会再次运行门控,而会将该任务作为 already-complete 返回。
如果回执还包含 designRevision,请先调用 prepare(见下文“designRevision”)。然后在 prepare 返回的胶囊下进行结算。声明该胶囊要求且尚未完成的每项任务。被阻止且在其自身偏差中列出的任务也要按此流程处理。修订会列出该任务,因此在较早的胶囊上创建新批次会得到 recompile-required。
acceptedTasks 中的任务均已完成,因此 prepare 可以编译依赖其中某项任务的任务。工作树从其任务的 baseRef(即集成分支)分叉,而该分支只包含已合入的工作。在创建依赖任务的工作树之前,请先合入 acceptedTasks 中的每项任务(步骤 4,第 1 项)。
rejected — 请求或偏离存在错误。在读取时,结算未验证任何内容。在读取前已由某个阶段验证的任务仍保持完成状态,并列于 verification[] 中。请阅读 findings:
- 形状问题(
missing-field、undeclared-field、field-type-mismatch、unknown-task、undeclared-result-shape、duplicate-claim、missing-claim、inadmissible-evidence):修正声明。missing-claim表示某个必需任务尚未完成,且没有声明。带有状态和理由的内容即为声明。对于未完成的任务,请发送一条声明。 rationale-not-delegated:理由指定了 capsule 未列出的决策 ID。问题消息会列出 capsule 的 ID。请删除该条目,或将该选择报告为偏离。- 偏离问题(
deviation-outside-envelope、deviation-unknown-task、deviation-names-finished-task、deviation-names-claimed-task):批次未被暂缓。请修正偏离。超出 envelope 范围的类型属于升级处理。其余三种情况分别是偏离无法指定的affectedTasks条目(见上文“构建批次”)。在决策轮次中,deviation-names-finished-task源自一个已暂缓的批次(见下文deviation-pending)。 deviation-rejected:决策拒绝了该偏离(见下文deviation-pending)。
然后,在同一个 capsule 下,使用新的批次 ID 结算修正后的请求。已有判定结果的批次 ID 已被占用,因此修正时必须使用新的批次:
exarchos_orchestrate({
action: "settle",
featureId: "<featureId>",
capsuleVersion: <capsuleVersion>,
batchId: "<featureId>:wave-1:retry-1",
claims: correctedClaims,
})
较早的一次构建也会因声明已被取代或验证失败而返回 rejected。重放时会按该构建存储的内容返回此类回执,其 acceptedTasks 可能处于完成状态。
deviation-pending — 偏离正在等待决策。工作者提出了 envelope 范围内的偏离,而 envelope 要求审批。在读取时,结算未验证任何内容,且批次未完成任何任务。回执中的 pendingDeviations 会通过每条偏离的 deviationId 列出这些偏离,流中也会为每条记录保留一行 deviation.proposed。人工检查点:展示每条偏离及其提出的变更(回执不包含该变更)。由人员决定每条偏离。你不能决定,提出偏离的工作者也不能决定。如果运行中没有人员,则在暂缓的批次处停止:报告暂缓的回执及其待决偏离,不发送任何决策。人员作出决定后,使用这些决策且不带声明,再次结算同一个批次。结算会从记录中读取暂缓的批次:
exarchos_orchestrate({
action: "settle",
featureId: "<featureId>",
capsuleVersion: <capsuleVersion>,
batchId: heldBatchId, // the batch the held receipt names, not a new one
decisions: pendingDeviations.map(({ deviationId }) => ({
deviationId,
decision: "accepted", // or "rejected"
actor: "human:<who decided>",
rationale: "<why>",
})),
})
在一次调用中决定所有待决偏离。如果有任何一条未作决定,结算会拒绝该决策(DECISION_INCOMPLETE)。
结算会记录一项 accepted 决策(deviation.decided)。随后会验证暂缓的工作,并给出结果:settled、accepted-with-rationale 或 corrective-capsule。暂缓的声明会保留各自的状态、理由和 rationale。如果之后的设计修订指定了暂缓批次中的某项任务,结果将为 recompile-required,且结算在读取时不会验证任何内容。如果已接受的偏离指定了本轮读取时已完成的任务,结果将为 rejected,并附带 deviation-names-finished-task。该偏离不会记录任何修订,其他每项已接受的实质性偏离仍会记录其修订。请将已完成任务的返工作为新任务规划,并指定新任务。接受决策后的修正无需再次陈述该偏离。该偏离已作出决策,再次陈述它会成为一项新的提议,并暂缓新的批次。
偏离指定的被阻塞任务。 暂缓的批次将该任务报告为 blocked,且偏离指定了该任务。作出 accepted 决策后,结算会验证该批次中已完成的声明。被阻塞的任务尚未解决,因此结果为 corrective-capsule,原因是 task-blocked。
如果偏差类型属于实质性,回执还会携带 designRevision,且该修订会指明受阻任务。先调用 prepare。后续操作会在新的设计版本下编译任务,其 capsule 会说明已接受的变更。在创建依赖某个任务的任务的 worktree 之前,先完成 acceptedTasks(第 4 步,第 1 项)中的每个任务。从新数据包分派任务,并在新的 capsule 下完成结算。在较早 capsule 上执行的批次会标记为 recompile-required,无论它是否认领该任务、再次报告该任务,还是未提及该任务。
如果偏差类型不属于实质性,回执不会携带 designRevision。移除阻塞,然后在同一个 capsule 上通过新批次完成任务结算。
结算也会记录 rejected 决策。结果为 rejected,并附带 deviation-rejected 发现;结算在读取时不会验证任何内容。基于该偏差开展的工作不予接受。修正该工作,并在不包含该偏差的新批次 ID 下完成结算。如果计划中有用于处理被拒绝偏差的返工任务,则移除该任务。然后再次调用 prepare,并在其返回的 capsule 下完成结算。
结算只会记录一次决策。再次发送相同的决策会返回其裁决。对于已作出决策的批次,结算会拒绝不同的决策。
回执中的 designRevision — 该轮至少接受了一个实质性偏差,并为其记录了一项设计修订(design.revised,设计版本从 N 到 N+1),与该轮的裁决无关。回执会列出两个版本和偏差(priorDesignVersion、nextDesignVersion、deviationIds)。收到携带设计修订的回执后,如果已接受的变更需要调整计划,则先调整计划,然后再次调用 prepare — 修订指明的任务只能在修订后编译的 capsule 下完成结算。recompile-required 回执也需要同样调用 prepare。
修订后第一次调用 prepare 就是后续编译:与第 1 步相同的调用,始终会在新的设计版本下编译就绪的任务前沿,并在同一次提交中记录重新编译(capsule.recompiled)。返回的回执会携带 recompile — 其所基于的 capsule 版本和涵盖的设计版本(priorCapsuleVersion、priorDesignVersion、nextDesignVersion)、修订指明的任务(declaredTasks),以及修订使其失效的任务(invalidatedTasks):每个被指明的未完成任务,以及每个直接或通过其他未完成任务依赖这些任务的未完成任务。已完成的工作保持不变,仅依赖已完成工作的任务也保持不变。其 capsule 会携带已接受的变更(knowledge.rationale[])。尚未就绪的失效任务会列在该列表中,但不会包含在该 capsule 中:阻塞任务完成后,后续调用 prepare 会在相同设计版本下编译它。重放的回执会重复记录的重新编译;这不是新的重新编译,也不会追加任何内容。
recompile-required — 批次到达时,capsule 已过时。数据流在该 capsule 之后记录了一项设计修订。该修订指明了批次认领的某个任务,或尚未完成的必需任务。发现为 claim-superseded-by-revision。结算期间提交的修订也会导致此结果。在读取时,结算未验证任何内容,批次中的未完成任务仍未完成。修订提交前已由某个片段验证的任务仍保持完成状态,并会列在 verification[] 中。包含修订所指明的未完成必需任务的 capsule 不会结算任何新批次。决策轮也可能返回此结果,且其决策仍会被记录。
再次调用 prepare。然后在 prepare 返回的胶囊下继续。认领此胶囊要求完成且尚未完成的每项任务。修订版中提到的任务,其工作使用了早先的术语。新胶囊会说明已接受的更改(knowledge.rationale[]),因此再次认领工作前,请先阅读该更改。
说明:提到某项任务的修订版会在该任务的工作段运行期间提交。随后,作为该工作段最后一个叶节点的 task_complete 会以 TASK_SUPERSEDED_BY_REVISION 拒绝该任务。工作段结束后的读取会返回 recompile-required。如果直接调用 task_complete,则修订版在同一胶囊中提到该任务后,该调用也会以相同方式拒绝该任务。然后再次调用 prepare,并在新胶囊下完成该任务。
错误(未进行任何裁定)。以下每个代码都表示调用失败,且未进行任何裁定:
CAPSULE_INVALID:提交的胶囊不符合胶囊契约。CAPSULE_NOT_PREPARED:此处没有记录对此胶囊版本的prepare调用。CAPSULE_DIGEST_MISMATCH:提交的胶囊不是已记录的胶囊。CAPSULE_UNRESOLVED:结算无法应用已记录的胶囊。如果任务没有验证条款,请再次运行prepare。需要分段的批次还会因另外两种原因而遇到此问题。胶囊未固定任何范围,或者批次中的某个任务没有baseCommit:请再次运行prepare,然后在新批次中认领任务。prepare之后检出内容的配置文件发生了变化:请使用相同的repoRoot再次运行prepare,然后在新批次中认领任务。在决策轮次中,新的prepare无济于事,因为被挂起的批次仍使用其胶囊版本,因此此消息会指出其他处理途径(references/troubleshooting.md)。OPERATION_DIGEST_MISMATCH:此批次 ID 已有针对不同请求的裁决,或者已有不同的决策生效。请使用新的批次 ID,或发送当前生效的决策。没有裁决但其分段已由早期构建运行过的批次也会出现此错误,因为此构建会将capsuleVersion添加到每个分段的请求中。请使用新的批次 ID 结算此类批次的认领。CONCURRENCY_CONFLICT:决定裁决结果的读取内容在三次提交前都发生了变化,因此此次调用未记录任何裁决。请再次结算相同的批次 ID。重试会重新播放已验证任务的分段,但不会再次运行其门禁。BATCH_NOT_HELD:决策指向的批次没有裁决,或者该批次并未处于挂起状态。没有偏差在等待决策。DECISION_INCOMPLETE:决策回答的偏差少于该批次正在等待的偏差。STATE_SYNC_FAILED:状态文档写入失败,但流中的事实仍然有效。请运行exarchos_workflow reconcile,然后再次结算相同的批次。如果仍然被拒绝,请停止并报告给相关人员。存储文档中的版本间隔会导致每次重试都失败。重试已有裁决的批次时,error.settlementReceipt会保留该裁决。超过回执预算时,它会保留经过截断的摘要。INTENT_SEGMENT_FAILED或INTENT_EMISSION_CONTRACT_VIOLATED:某个任务的分段已中止,且此次中止不是门禁裁决。要么叶节点不是门禁,要么叶节点违反了契约。如果task_complete拒绝该任务,完成叶节点就会中止;连续发生 8 次序列冲突后也会中止。只有当批次在分段之后读取时仍处于验证状态,此次中止才会导致调用失败。消息会指出任务和叶节点。分段记录会保留,因此重试相同的批次 ID 会得到相同的中止结果,除非流随后记录了任务已完成。请修正原因,然后使用新的批次 ID 结算这些认领一次。如果再次出现相同的中止,请停止并报告给相关人员。静态分析门禁跳过运行(无工具链)不会中止分段。随后task_complete会以GATE_NOT_PASSED拒绝任务。这也属于此类中止,使用新的批次 ID 仍会再次遇到。分段运行期间,如果在工作树中提交更改或切换分支,也会导致task_complete拒绝(other-tree、tree-out-of-scope):在裁决返回前,请勿改动各任务的工作树。INVALID_INPUT:结算无法根据认领构建分段(通常是因为缺少worktreePath),提交的批次没有任何认领,或者决策携带了认领或未指明任何待处理偏差。违反其status、reason或rationale规则的认领也会产生此错误(见上文“构建批次”)。请修正请求并使用相同的批次 ID。
Agent Teams 监控
- 队友会显示在 tmux 分屏窗格中
{{SUBAGENT_COMPLETION_HOOK}}会自动运行质量门禁,并发出完成/失败事件- 编排器通过
exarchos_view delegation_timeline监控,以检测瓶颈 - 有关解散和协调,请参阅
references/agent-teams-saga.md
失败恢复
当任务失败时——子 Agent 报告失败,或结算后任务仍未解决(corrective-capsule):
- 从运行时的结果收集原语(
{{SUBAGENT_RESULT_API}})中读取失败输出,以及结算发现中的消息 - 诊断根本原因——不要相信实现者的自我评估(参见 R3 对抗性立场)
- 使用下方的修复流程修复任务
- 在同一 capsule 上结算新的批次 ID,并认领已修复的任务(步骤 3)。除非流中已显示任务完成,否则结算会验证该任务。此类任务会返回
already-complete,且不会再次运行门禁。已完成的任务无需认领。若要返工已完成的任务,请规划新任务(见下方回滚流程)
在批次结算前报告失败的工作者不会中止该批次。正确做法有两种。先修复任务,并在同一批次中将其认领为已完成。或者,将任务认领为状态 failed 并说明原因,然后立即结算批次。第二种做法会一次性完成其他任务,但修正时会多调用一次 settle。对于尚在等待的任务或没有工作者运行的任务,同样使用状态 blocked 或 skipped。
完整的恢复流程及具体示例,请参见 references/worked-example.md。
修复失败的任务
使用运行时原生的生成原语,派发一个新的修复 Agent,并提供完整的失败上下文和原始任务描述:
{{SPAWN_AGENT_CALL agent="fixer" description="Fix failed task-001" prompt="Your implementation failed. [failure context from test output and the settlement finding]. Apply adversarial verification: do NOT trust your previous self-assessment, re-read actual test output, identify root cause not symptoms. [Original task context, including the capsule's terms]."}}
优化(可选): 在支持原生会话恢复的运行时中(例如工作流状态中包含 agentId 的 Claude Code),你可以恢复原始 Agent,让它保留实现者上下文。上方的新派发方式仍然正确,也是规范的默认做法。
修复会沿着任务之前的分支继续进行,而不是开启新分支。在不支持原生隔离时,将修复 Agent 派发到任务之前的工作树。在支持原生隔离时,在派发前提交任务列出的文件,移除每个被门禁运行留下的文件,并删除之前的工作树,以便修复 Agent 将自己的工作树切换到之前的分支。references/fix-mode.md 介绍了各运行时的操作流程,以及在新分支上进行修复的恢复方法。
修复完成后,在同一 capsule 上的新批次中认领已修复的任务,并使用修复 Agent 的工作树和与之前认领相同的 branch。不要手动运行门禁链:新的 settle 调用会运行它。
第 4 步:合并并转换阶段
- 合并每个工作树。 完成结果包含工作树的已结算任务会使工作流进入
merge-pending;通过serialize_merge合并这些工作树(见下方“包含工作树的任务”),并按依赖顺序一次处理一个工作树。 - 波次后备检查——在波次的所有合并完成后,运行一次针对集成分支最新提交的累积测试套件:
exarchos_orchestrate({
action: "check_integration_suite",
featureId: "<featureId>",
repoRoot: "<integration worktree>",
})
- 委派完成门禁(D4,建议性)——在转换到审核前,对整个分支差异进行运行韧性检查:
exarchos_orchestrate({
action: "check_operational_resilience",
featureId: "<featureId>",
repoRoot: ".",
baseBranch: "main"
})
此项为建议性检查——检查结果会记录在收敛视图中,但不会阻止从委派阶段转换到审核阶段。请在委派摘要中附上检查结果,以便审核阶段关注。
-
架构同步——如果有任何任务修改了 API 文件(
*Endpoints.cs、Models/*.cs),请运行npm run sync:schemas -
转换阶段——参见末尾的“转换阶段”部分:运行
exarchos_workflow transition转到review。任务已全部完成;无需手动打补丁。
修复模式(--fixes)
处理审查失败,而非初始实现。使用 references/fixer-prompt.md 模板,并采用对抗式验证立场;按问题分派修复任务,然后重新调用审查以重新整合修复内容。
参数: --fixes <state-file-path> — 状态 JSON,其中包含 .reviews.<taskId>.specReview 或 .reviews.<taskId>.qualityReview 中的审查结果。
有关修复模式的详细流程,请参阅 references/fix-mode.md。
已弃用:
--pr-fixes已由/exarchos:shepherd取代。PR 反馈工作流请使用 shepherd 技能。
上下文压缩恢复
如果委派期间发生上下文压缩:
- 查询工作流状态:使用
fields: ["tasks"]执行exarchos_workflow get - 读取每个批次的结算记录:
exarchos_event({ action: "query", stream: "<featureId>", filter: { type: "execution.settled" } })。每一行都会列出其batchId、capsuleVersion、outcome和acceptedTasks,以及corrective-capsule下的unresolvedTasks - 检查活跃工作树:执行
ls .worktrees/并验证分支状态 - 对账:
exarchos_workflow reconcile会重放事件流并修补过时的任务状态 - 在确认丢失之前,切勿重新创建分支或重新分派 Agent
工作树状态架构
工作树条目在工作流状态中存储为 worktrees["<wt-id>"]。每个条目都需要包含:
| 字段 | 类型 | 必填 | 备注 |
|---|---|---|---|
branch | string | 是 | Git 分支名称 |
taskId | string | 有条件必填 | 单个任务 ID(用于单任务工作树) |
tasks | string[] | 有条件必填 | 多个任务 ID(用于多任务工作树) |
status | "active" | "merged" | "removed" | 是 | 工作树生命周期状态 |
必须提供 taskId 或 tasks(非空数组)中的至少一项。
单任务示例:
{ "branch": "feat/task-001", "taskId": "task-001", "status": "active" }
多任务示例:
{ "branch": "feat/integration", "tasks": ["task-001", "task-002"], "status": "active" }
阶段转换与守卫
完整的转换表请参阅 @skills/checkpoint/references/phase-transitions.md。
快速参考: delegate → review 转换要求满足守卫 all-tasks-complete——工作流状态中所有 tasks[].status 都必须为 "complete"。
结算后的批次会将它们标记为完成:结算完成叶节点会在事件流和守卫读取的文档中,将每个已接受任务标记为完成。如果转换被拒绝,说明有任务尚未完成:其所属批次有未解决任务,或者该批次被拒绝、搁置或已过时。请读取结算记录,而非任务列表。调用
exarchos_event({ action: "query", stream: "<featureId>", filter: { type: "execution.settled" } })会为每一轮结算返回一行,其中包含其outcome,以及corrective-capsule下的unresolvedTasks。不要手动修改tasks[].status来绕过守卫。
携带工作树的任务:自动绕行至 merge-pending
当 task.completed 事件携带工作树关联信息(data.worktree 或 data.worktreePath)时,HSM 会自动经过 feature/merge-pending 转换,然后进入 review。next_actions 投影会显示合并操作(幂等键为 ${streamId}:merge_orchestrate:${taskId}),因此,使用 next_actions 的运行时会自动分派合并。
通过 serialize_merge 落地。 集成分支由多个工作树共享——同一批次中的兄弟工作树合并(或并发操作员)可能会争抢该分支。serialize_merge 是集成合并的唯一途径:它会持有按 integrationRef 划分的乐观单写者租约,然后原样组合 merge_orchestrate,在本地执行 git merge,并记录恢复点 SHA——参见 @skills/merge-orchestrator/SKILL.md。不要分派原始 merge_orchestrate 来合并到集成分支——如果存在有效的外部租约,它会以失败关闭方式退出(MERGE_LEASE_HELD,并指出 serialize_merge)。原始 merge_orchestrate 适用于非集成合并(不会被兄弟工作树触碰的私有/临时分支),或重新提交原始租约、从崩溃中恢复的调用方。
合并终止(completed/rolled-back/aborted)后,HSM 会从 merge-pending 返回 delegate。此时,如果还有下一个携带工作树的任务,delegate 会再次进入 merge-pending;如果所有委派任务均已完成,则会转至 review。
对委派技能本身而言,这次绕行不可见——所有任务均已完成的守卫仍会控制 delegate → review 转换。merge-pending 子状态只是在任务完成与下一次分派决策之间。
任务状态值
| 状态 | 使用时机 |
|---|---|
pending | 任务尚未开始 |
in_progress | 任务正在处理中 |
complete | 任务已成功完成 |
failed | 任务遇到错误(需要修复周期) |
这些是工作流状态中 tasks[].status 的取值。认领项(步骤 3)的 status 是另一个字段。认领项报告 failed 并不会将任务设为 failed。
架构发现
使用 exarchos_workflow({ action: "describe", actions: ["update", "init"] }) 查询参数架构,并使用 exarchos_workflow({ action: "describe", playbook: "feature" }) 查询阶段转换、守卫和剧本指南。使用 exarchos_orchestrate({ action: "describe", actions: ["prepare", "settle"] }) 查询控制面的自身架构——包括 prepare 的返回内容以及认领项可携带的内容。
集成分支在批次中途前进时
当子 Agent 工作树的分支偏离集成分支时,用于恢复的运行手册。触发条件是集成合并的祖先关系预检(由 serialize_merge 内部组合的 merge_orchestrate 执行)失败:失败消息会原样链接到这里,并包含手动 git rebase 命令。目前尚未接入自动 rebase——操作员必须手动处理恢复。
症状
merge-orchestrator 报告以下形式的祖先关系失败:
source branch <feature-branch> is not a descendant of <integration-branch>.
Rebase manually with: git rebase <integration-branch> (run from the <feature-branch> worktree).
Runbook: content/delivery/skills/delegate/SKILL.md#when-integration-advances-mid-wave
这表示集成分支已前进(通常是因为较早的工作树合并已落地),而发生失败的工作树仍在处理中。快进合并已不再安全——必须先让工作分支追上集成分支。
出现此问题的原因
每个子 Agent 工作树都从其任务的 baseCommit 开始,也就是 prepare 编译该批任务时集成分支所处的提交(Agent 的 base 步骤)。当编排器依次合并各个同级工作树时,每次合并都会推进集成分支。某个工作树若基于较旧的集成提交创建,轮到它时就会在祖先关系预检中失败。
这是当前单写入者合并约定下的预期行为——预检会有意只在失败时拦截,让操作人员掌控流程。
前提条件 — 固定工作树基点。 原生
isolation: worktree会从仓库的默认分支(origin/HEAD→main)而非集成分支最新提交创建子 Agent 工作树分支,除非在.claude/settings.json中设置worktree.baseRef: "head"。请保留此固定设置, 让子 Agent 工作树从接近其基点的位置开始。 当请求nativeIsolation但缺少此固定设置时,prepare_delegation会明确报错,并给出以下修复方式:{ "worktree": { "baseRef": "head" } }工作树是干净的检出,因此只有已提交的 HEAD 状态会传递过去—— 请在派发前提交设计、计划和研究成果。
固定设置并不能保证基点。Agent 的 base 步骤会在每个运行环境中指定基点:对于实现者 或搭建者,执行
git switch -c <own branch> <baseCommit>;对于修复者,则使用任务之前的分支。 结算的base-not-held规则会拒绝 HEAD 未包含<baseCommit>的工作树。因此,操作人员无需在派发步骤前手动添加git reset --hard <integration-tip>作为 STEP 0。
恢复流程
每个步骤开始前,请确认你位于主工作树(而非失败的子 Agent
工作树),并且 git status 显示工作区干净。
-
在执行任何破坏性操作前,记录回滚 SHA:
git rev-parse <feature-branch> > /tmp/rollback.sha保留此 SHA,直到确认合并成功。如果出现问题,在功能分支上执行
git reset --hard "$(cat /tmp/rollback.sha)",即可恢复变基前的状态。文件名特意不包含 分支名称,这样像feature/dr-6这样的斜杠分隔分支就不会因名称中嵌入/字符而破坏路径。 -
将功能分支变基到当前集成分支的最新提交:
cd <feature-worktree-path> git fetch origin git rebase <integration-branch>解决出现的任何冲突。这些冲突是真实存在的——它们反映了两个分支之间的实际偏移,并非预检噪声。不要 盲目传入
--strategy-option=theirs,否则会丢弃子 Agent 的工作。 -
从主工作树重新运行集成合并——通过
serialize_merge,在单写入者租约下重新执行merge_orchestrate的祖先关系预检:exarchos_orchestrate({ action: "serialize_merge", featureId: "<featureId>", integrationRef: "<integration-branch>", sourceBranch: "<feature-branch>", strategy: "squash", // squash | rebase | merge taskId: "<taskId>", dryRun: false, // REQUIRED to execute — the action DEFAULTS to dry-run })serialize_merge默认采用试运行(仅预检,不获取租约):省略dryRun时,它会报告合并是否可以执行,但不会修改任何内容。传入dryRun: false才会实际获取单写入者租约并执行合并。此操作声明为 共享变更型,因此只读调用方(不具备写入能力的会话)即使在应用变更时也会被拒绝;这种情况下,请从主工作树使用本地 Git 执行合并(执行git merge --squash <feature-branch>,然后提交),并自行记录等效的合并状态和事件,因为该合并不受序列化租约约束。此时预检应该会通过。继续执行编排器的常规合并流程。(只有在非集成合并时,或作为崩溃恢复后的调用方重新提交原始
leaseOperationId时,才直接重新运行原始的merge_orchestrate。)
回滚流程
如果变基产生了无法安全解决的冲突,或者变基后合并仍然失败:
-
将功能分支重置到捕获的回滚 SHA:
cd <feature-worktree-path> git rebase --abort # if mid-rebase git reset --hard "$(cat /tmp/rollback.sha)" -
将返工规划为新任务。 该任务已在工作流中完成:它的完成行启动了此次合并。再次认领会返回
already-complete,并且不会再次运行任何门禁。已完成的工作不会重新打开。将新任务添加到计划中,运行prepare,并为新任务分派修复者(参见上文的“故障恢复”部分)。不要删除工作树——修复者需要原始分支状态来诊断冲突。 -
记录此次事件:发送一个
merge.aborted事件,其中包含reason: "ancestry-rebase-conflict"和失败分支在变基前的 SHA,以便收敛视图记录此次回滚。
为什么暂不自动变基
自动变基尚未接入。目前,编排器会有意在祖先关系预检处停止:在分歧的工作树之间进行不完善的自动变基,可能会在不知情的情况下丢弃子 Agent 的工作;而且,上述恢复流程足够简短,因此由操作人员驱动变基比使用巧妙但脆弱的自动化更可取。
转换
批次处理完成且工作树落地后,立即自动继续(无需用户确认):
- 在工作流状态中确认所有
tasks[].status === "complete"——批次处理完成后,它们应保持此状态 - 转换:
exarchos_workflow({ action: "transition", featureId: "<featureId>", target: "review" }) - 调用:
{{CHAIN next="review" args="<plan-path>"}}
这不是人工检查点——工作流会自主继续。
附录:原始路径(非功能工作流类型)
prepare 会编译功能工作流。当它因 WORKFLOW_TYPE_UNSUPPORTED 而拒绝处理——例如调试或检修委派——时,需按以下顺序手动执行各任务的治理调用:
- 就绪检查 —
exarchos_orchestrate({ action: "prepare_delegation", featureId: "<featureId>", planPath: "docs/specs/<the-decomposition-spec>.md", repoRoot: "<absolute path of the integration worktree>", tasks: [...] })。它会先自行公布计划中的任务(task.assigned),再读取就绪状态,因此不会有追加内容先于此步骤。传入planPath,以提取每个任务的**Risk Tier:**/**Boundary Touching:**标记;若ready: false,则停止此轮。将repoRoot设为绝对路径:分支读取、受保护分支检查、祖先关系预检、stash 探测以及相对路径的planPath都会以该路径对应的工作树为准。若不传入repoRoot,它会读取服务所在目录并返回警告。它会返回implementerPromptTemplate、以"<riskTier>|<boundaryTouching>"为键的verificationNotes映射,以及taskClassifications[i].verificationNoteKey——派发前,将任务对应的说明插入模板。它还会返回baseBranch,即任务分出的集成分支:请记录下来,供第 3 步使用。 - 派发并收集 — 按步骤 2 和步骤 3 执行,并以说明替换胶囊中的术语。
- 每个已完成的任务,运行任务完成操作手册:
exarchos_orchestrate({ action: "runbook", id: "task-completion" }),并按顺序执行返回的步骤。门禁失败时停止。将<baseRef>模板变量绑定到第 1 步中记录的baseBranch:没有此值时,终止探测会因base-missing而阻止操作。将<taskId>和<worktreePath>绑定到对应任务:check_static_analysis会在该任务的工作树中逐个运行,并使用taskId和repoRoot;task_complete仅接受同一任务、在result.worktreePath指定的工作树上、当前提交中通过的运行结果。若运行时传入taskId但未传入repoRoot,则不会扫描任何内容(repo-root-missing)。如果操作手册不可用,请使用describe获取门禁架构:exarchos_orchestrate({ action: "describe", actions: ["check_test_adequacy", "check_static_analysis", "task_complete"] })。其终端步骤会记录完成状态及报告来源:
exarchos_orchestrate({
action: "task_complete",
taskId: "<taskId>",
streamId: "<featureId>",
result: {
summary: "<task summary>",
worktreePath: "<absolute worktree path of the task>",
implements: ["DR-1", "DR-3"],
tests: [{ name: "testName", file: "path/to/test.ts" }],
files: ["path/to/impl.ts", "path/to/test.ts"]
}
})
- 门禁失败时,派发修复者(见上文“修复失败任务”),然后运行
task-fix操作手册:exarchos_orchestrate({ action: "runbook", id: "task-fix" })。 - 合并并转换 —
task_complete已在工作流状态中将每个通过的任务标记为完成,因此不要手动写入tasks[].status。按步骤 4 执行合并、后备处理和转换。
参考资料
| 文档 | 用途 |
|---|---|
references/implementer-prompt.md | 实现任务的完整提示词模板 |
references/fixer-prompt.md | 采用对抗式验证立场的修复 Agent 提示词 |
references/worked-example.md | 包含恢复路径(R1)的完整委派追踪 |
references/rationalization-refutation.md | 常见的自我合理化说辞及反驳(R2) |
| references/agent-teams-saga.md | 包含事件负载的 6 步 Agent 团队历程 |
| references/parallel-strategy.md | 并行分组和模型选择 |
| references/testing-patterns.md | Arrange/Act/Assert、命名和模拟约定 |
| references/pbt-patterns.md | 基于属性的测试模式 |
| references/fix-mode.md | 修复模式的详细流程 |
| references/state-management.md | 状态模式和基准标注 |
| references/troubleshooting.md | 常见故障模式及解决方法 |
| references/adaptive-orchestration.md | 自适应团队组成 |
| references/workflow-steps.md | 跨平台逐步委派参考 |
| references/worktree-enforcement.md | 工作树隔离规则 |