关于 merge-orchestrator
面向 Exarchos 工作流的本地 Git 分支合并技能。它通过预检、记录恢复点和失败恢复处理合并;共享集成分支须经串行租约路径合并。
主要功能
- 01使用指定策略合并本地分支
- 02预检工作树与分支状态
- 03合并前记录恢复点
- 04失败时恢复且不执行硬重置
- 05支持结构化事件与阶段续跑
适用场景
- 01本地合入子 Agent 工作树分支
- 02串行合并到共享集成分支
- 03失败后恢复目标分支
MCP 相关
通过 Exarchos MCP 编排操作执行并跟踪本地合并;共享集成分支使用 serialize_merge 获取单写者租约。
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ lvlup-sw/ exarchos --skill merge-orchestrator安装后在 Claude Code 中输入/merge-orchestrator确认已加载。
附带文件references/local-git-semantics.mdreferences/recovery-runbook.md
详细介绍
合并编排器技能
本地 Git,而非远程 VCS
此技能会在本地执行 git merge,将源(子 Agent)工作树分支合并到目标(集成)分支,并记录一个恢复点 SHA,以便在发生任何故障时,按照 INV-14 恢复阶梯(git merge --abort → git reset --keep,绝不使用 --hard)回退合并。它不会调用 VCS 提供商(GitHub / GitLab / Azure DevOps),也不需要 PR id。有关远程 PR 合并,请参阅封装 merge_pr 操作的配套技能。
关于这两项关注点为何需要分别处理的思路模型和原因,记录在 references/local-git-semantics.md 中。
单写入者入口点:serialize_merge
对于共享集成分支——在 delegate → merge-pending → delegate 循环期间,将子 Agent 工作树分支合入集成 ref,而此时同级工作树的合并可能发生竞争——serialize_merge 是集成合并的唯一入口。它会获取按 integrationRef 隔离的乐观租约(每个集成 ref 至多允许一个正在进行的合并),然后在该租约下原样组合执行此操作。集成分支合并必须通过 serialize_merge 路由;不要为此直接调用原始 merge_orchestrate。
只有在目标上不会发生并发合并竞争时,才可直接调用 merge_orchestrate:
- 非集成合并——目标是私有 / 临时 / 独立分支,不会被其他 Agent 操作。
- 崩溃后恢复的调用方——新的 pid 完成其已获取租约的合并,并重新提供原始请求中的
leaseOperationId。 - 组合执行器调用——
serialize_merge自行发起的调用(它会传递自己的租约operationId,使其调用通过检查)。
DR-2 租约防护。如果针对集成 ref 的原始 merge_orchestrate 调用遇到一个有效的他方租约(持有者的 operationId ≠ 所提供的 leaseOperationId,且持有者状态 ∈ {alive, unknown}),则会安全失败,返回结构化的 MERGE_LEASE_HELD 错误,且不会产生任何 git 副作用;错误消息会指出应使用 serialize_merge。如果没有租约,或可确认持有者已死亡,则会照常继续(向后兼容;已死亡的持有者不会构成阻碍)。因此,这些路由要求由处理器的关键入口强制执行,而不只是约定俗成——请使用 serialize_merge。
概述
每当操作员(或自动化的 next_actions 分发器)针对 Exarchos MCP 调用 merge_orchestrate 操作时,此技能就会启用。源分支在工作树中准备完毕后,此技能会:
- 执行工作树可用性预检(目标分支不得在同级工作树中处于检出状态)。
- 组合执行其他预检防护(祖先关系、当前分支保护、主工作树断言、工作树漂移)。
- 将目标分支合并前的
HEAD记录为恢复点(写前日志标记,在任何 ref 修改前持久化)。 - 在本地将源分支
git merge到目标分支。 - 发生任何故障时,执行 INV-14 恢复阶梯(
git merge --abort→git reset --keep <recoveryPointSha>,绝不使用--hard),并给出分类后的失败原因。 - 发出专用事件类型,使合并时间线仅凭事件日志即可重建。
支持恢复续行:在重新进入时,终结阶段(completed / rolled-back / aborted)会短路退出,不会重复发出事件。支持幂等:使用相同 taskId 重新分发时,会通过 next_actions 幂等键合并为同一次操作。
触发条件
在以下情况下激活此技能:
- 操作者运行
exarchos merge-orchestrate ...(CLI)。 - 直接调用
exarchos:exarchos_orchestrate({ action: "merge_orchestrate", ... })。 - 自动化
next_actions信封为merge-pending分流显示了合并动词(幂等键<streamId>:merge_orchestrate:<taskId>)。
通过
serialize_merge路由共享集成合并。 如果触发条件是共享集成分支(常见情况是merge-pending分流),请按照上文的单写入者入口,通过serialize_merge执行合并——serialize_merge会在租约下组合此操作。直接原始分发merge_orchestrate仅适用于非集成合并或已持有租约的合并;若针对已租赁的集成引用执行,则会安全失败(MERGE_LEASE_HELD)。
不要在以下情况下激活此技能:
- 合并远程 PR——应使用
merge_pr。这两种操作互不相关;请参阅下文的消歧说明。 - 编排器记录的
mergeOrchestrator.phase已处于终态——恢复短路机制会运行,但无需进行新的分发。
流程
架构: 使用
exarchos:exarchos_orchestrate({ action: "describe", actions: ["merge_orchestrate"] })获取此操作的参数架构。策略为必填项(架构层面没有默认值)——请审慎选择squash/merge/rebase。
步骤 1:选择合并策略
| 策略 | 本地 git 操作 | 选择时机 |
|---|---|---|
merge | git merge --no-ff --no-edit <source> — 显式合并提交 | 保留子 Agent 的提交历史,并显示清晰的合并边界。 |
squash | git merge --squash <source> 然后执行 git commit — 在目标分支上生成单个压缩提交 | 子 Agent 的提交历史过于冗杂;一项逻辑变更应作为一个提交合入。 |
rebase | 将源分支的临时副本变基到目标分支上,然后对目标分支执行快进合并 — 线性历史 | 不生成合并提交;集成分支保持线性。原始源引用会保留(变基在临时分支上执行,之后会删除该分支),因此执行器恢复时只需将目标分支回退到恢复点。 |
策略在架构层面为必填项(用于冲突检查和用户可见的一致性)。没有隐式默认值——操作者的意图始终会明确记录在事件日志中。
步骤 2:调用
集成合并? 下方的直接调用是原始执行器形式——仅用于非集成合并或已持有租约的合并。对于共享集成分支,请改为调用
serialize_merge(它会在单写入者租约下组合执行完全相同的调用);请参阅上文的单写入者入口。
通过 MCP 调用(示例——规范参数名称以 describe 返回的结果为准):
exarchos:exarchos_orchestrate({
action: "merge_orchestrate",
// workflow-correlation identifier — name per the action's schema
sourceBranch: "<subagent-branch>",
targetBranch: "<integration-branch>",
taskId: "<task-id>", // present when auto-dispatched from next_actions
strategy: "squash", // required
dryRun: false, // optional — preflight only, no executor invocation
resume: false, // optional — short-circuit on terminal phases
})
通过 CLI 调用:
exarchos merge-orchestrate \
--source-branch <subagent-branch> \
--target-branch <integration-branch> \
--task-id <task-id> \
--strategy squash
# plus the workflow-correlation id flag — see `--help`
# add --dry-run for preflight-only, --resume for terminal-phase short-circuit
CLI 退出代码:0 = 成功,1 = 输入无效,2 = 合并失败(预检阻止或已执行恢复),3 = 未捕获的异常。
第 3 步:解读结果
处理程序会返回一个 ToolResult,其 data.phase 用于区分结果:
phase | 含义 | 操作者操作 |
|---|---|---|
completed | 本地合并已完成;mergeSha 是目标分支的新 HEAD。 | 无——工作流按编排器的操作手册继续执行。 |
aborted | 预检失败;未尝试合并。data.preflight 包含结构化的防护子结果(如果由正文预检生成),或者 data.reason 用于区分提前中止的原因。 | 请参阅下方的中止原因表。解决根本问题后重新分派。 |
rolled-back | 已尝试合并,但失败(reason: 'merge-failed' / 'verification-failed' / 'timeout'),并执行了 INV-14 恢复流程(git merge --abort → git reset --keep <recoveryPointSha>)。目标分支已回退到恢复点。(阶段值仍为 rolled-back——这是一个关键状态令牌,不会因恢复框架的调整而改变。) | 检查 data.reason。如果还存在 data.recoveryError / data.recoveryErrorDetail,则重置本身也失败了——工作树处于受困状态,需要操作者介入。 |
中止原因的载荷格式
当 phase === 'aborted' 时,数据载荷会区分具体原因:
data.reason | 载荷字段 | 原因 | 操作者补救措施 |
|---|---|---|---|
target-checked-out-elsewhere | siblingWorktreePath: string(绝对路径) | 目标分支已在同一仓库的另一个工作树中检出。此情况会在工作树可用性预检期间检测,早于任何事件发出、执行器调用或状态持久化。 | 解决该工作树问题:移除它(git worktree remove <path>)、将其检出切换到其他分支,或针对其他目标重新运行合并。然后重新分派(共享集成引用时通过 serialize_merge 执行)。 |
| (正文预检失败) | data.preflight: { ancestry, worktree, currentBranchProtection, drift } | 正文预检防护失败(祖先不匹配、工作树漂移、当前分支受保护等)。 | 检查 preflight.* 子结果,确定失败的防护项。解决根本问题(例如,提交或暂存状态漂移、切换离开受保护分支),然后重新分派。 |
target-checked-out-elsewhere 中止路径比较特殊:它会同时抑制 merge.requested 和 merge.preflight 事件,并完全跳过状态持久化。这可确保事件日志不会记录无法捕获正确恢复点的尝试(执行器会从错误的工作树读取 HEAD)。
有关各结果对应的完整恢复流程,请参阅 references/recovery-runbook.md。
步骤 4:确认事件发射
事件会直接发射到编排器的事件流(流 ID 是传入的 streamId 值)——不会包装在 gate.executed 中:
| 事件类型 | 时机 | 携带内容 |
|---|---|---|
merge.preflight | 始终发射(预检运行后、任何合并尝试前)——但提前中止的 target-checked-out-elsewhere 路径除外,该路径不会发射任何事件 | 完整的结构化防护子结果,以及 passed: false 时的 failureReasons |
merge.requested | 预检通过后、执行器运行前(双事件拆分中的 A 阶段意图记录)——提前中止的 target-checked-out-elsewhere 路径会抑制此事件 | sourceBranch、targetBranch、strategy、taskId |
merge.executed | 本地合并成功时 | mergeSha、rollbackSha、taskId、源分支/目标分支 |
merge.recovered | 合并后发生故障并随后恢复时——这是规范的恢复事件,自 DR-2 起也是唯一会发射的恢复事件 | recoveryPointSha、reason、recoveryError?、recoveryErrorDetail?、taskId、源分支/目标分支 |
merge.rollback | 已弃用(DR-2)——旧版线格式,兼容读取但不会发射:现在已没有任何代码会写入此事件;它只保留在 DR-2 之前的事件日志中,并与 merge.recovered 一样折叠处理 | rollbackSha、reason、taskId、源分支/目标分支 |
恢复术语。 规范的框架基于数据库事务,而非 Saga:恢复点(记录的 HEAD SHA,在合并前作为预写日志标记持久化)和恢复事件(
merge.recovered)。merge.recovered是merge.rollback的后继事件,自 DR-2 起也是执行器发射的唯一恢复事件;旧版merge.rollback写入路径已弃用(兼容读取但不会发射,仅为确保 DR-2 之前的日志可被同样地重放而保留)。merge.recovered携带已确定的recoveryPointSha/recoveryErrorDetail字段名,以及未变更的 INV-14recoveryError判别字段;保留的merge.executed事件仍携带其rollbackSha线字段。阶段值'rolled-back'有意保留。
这些事件由处理程序自动发射——正常操作时,不要通过 exarchos:exarchos_event 手动追加这些事件。只有在 recovery-runbook.md 中记录的手动恢复流程里,才允许手动发射:当合并已在外部完成(例如,冲突解决),且事件日志必须恢复同步时,请遵循该运行手册中以事件优先的顺序。
通过
exarchos:exarchos_event({ action: "describe", eventTypes: ["merge.preflight", "merge.requested", "merge.executed", "merge.rollback", "merge.recovered"] })发现事件负载架构。(merge.rollback是旧版、兼容读取的事件——为确保 DR-2 之前的日志可被重放而保留,现已不再发射。)
消除歧义:merge_orchestrate 与 merge_pr
两个相关操作,各自关注点不同:
| 方面 | merge_orchestrate(此技能) | merge_pr(配套技能) |
|---|---|---|
| 层级 | 本地 SDLC 交接 | 远程 PR 基元 |
| 合并内容 | 子 Agent 工作树分支合并到目标分支 | 通过 VCS 提供商 API 合并面向用户的 PR |
| 所需标识符 | sourceBranch + targetBranch | prId |
| 底层操作 | git merge(本地) | provider.mergePr()(远程 API) |
| 恢复 | INV-14 恢复阶梯:git merge --abort → git reset --keep <recoveryPointSha>(实际执行,回退合并;绝不使用 --hard) | 无——VCS 提供商拥有合并状态 |
| 事件 | merge.preflight / merge.requested / merge.executed / merge.recovered(旧版 merge.rollback 已弃用——兼容读取,不会发射) | pr.merged |
如果你想到“我想合并一个 PR”而去使用 merge_orchestrate,那么你需要的是 merge_pr。
恢复语义
使用 resume: true 调用时,处理程序会读取现有的 mergeOrchestrator 状态。终止阶段(completed / rolled-back / aborted,即 EXCLUDED_MERGE_PHASES 的成员)会提前结束,并返回记录的结果,不产生新事件,也不会调用执行器。非终止阶段(pending / executing)会继续执行全新的预检和执行器运行;这是安全的,因为底层 Git 操作对于已合并的分支具有幂等性。
不使用 resume 调用时,会有意忽略先前状态,即采用全新分发语义。
注意:target-checked-out-elsewhere 提前中止路径在读取恢复状态之前运行,因此无论 resume 为何值,都会提前结束,且不会持久化状态。
试运行
dryRun: true 会运行正文预检,发出 merge.preflight,并在执行器运行及任何状态持久化之前提前结束。返回 { dryRun: true, preflight, phase: 'pending' | 'aborted' }。适用于在合并窗口开启前检查合并就绪情况的 CI 集成。试运行仍会检查工作树是否可用——即使设置了 dryRun,同级工作树冲突也会以 reason: 'target-checked-out-elsewhere' 中止。
反模式
| 不要 | 应改为 |
|---|---|
将原始 merge_orchestrate 分发到共享集成分支 | 通过 serialize_merge 路由(单写入者租约);原始 merge_orchestrate 用于非集成合并或持有租约的合并——存在有效的其他租约时会失败关闭(MERGE_LEASE_HELD) |
| 使用此技能合并远程 PR | 使用 merge_pr 技能 |
在正常流程中手动发出 merge.preflight / merge.requested / merge.executed / merge.recovered | 让处理程序自动发出;手动发出会导致重复(一个例外:recovery-runbook.md 中记载的手动恢复流程) |
将合并事件包装在 gate.executed 下 | 直接追加到流中,并使用专用事件类型——这些是状态转换,而不是门控执行 |
在 rolled-back 结果后不检查原因就重新分发 | 读取 data.reason 和 data.recoveryErrorDetail(以及 data.recoveryError 判别字段);先解决根本原因 |
在 reason: 'target-checked-out-elsewhere' 后不先释放同级工作树就重新分发 | 移除或重新检出 data.siblingWorktreePath 引用的同级工作树,然后重新分发 |
省略 --strategy / strategy: 字段并期待使用默认值 | 必须指定策略;明确提供 squash / merge / rebase |
| 从子 Agent 工作树调用 | 预检会拒绝执行(主工作树断言);请从主工作树调用 |
诊断
在调用 merge_orchestrate 前,在环境中设置 EXARCHOS_PREFLIGHT_DEBUG=1,即可在 merge.preflight 事件中附加结构化调试载荷。该载荷受两个条件限制,且必须同时满足:
- 编排器进程环境中存在
EXARCHOS_PREFLIGHT_DEBUG=1。 - 预检的祖先关系守卫未通过(即
ancestry.passed === false)。
即使设置了环境变量,通过的预检也不会携带调试载荷——仅在失败时启用是有意为之(考虑到事件存储的增长)。未来的 EXARCHOS_PREFLIGHT_DEBUG=2 通道可能会添加详细或通过预检的诊断信息;这不属于第 1 阶段的范围。
调试块包含九个字段:
| 字段 | 来源 | 用途 |
|---|---|---|
gitVersion | git --version | 区分不同 Git 版本中的行为。 |
repoRoot | git rev-parse --show-toplevel | 区分符号链接路径、规范化路径与原始仓库路径。 |
worktreeList | git worktree list --porcelain | 展示可能影响引用解析的同级工作树拓扑结构。 |
refsHeadsSource | git for-each-ref refs/heads/<source> | 源分支引用的 SHA 和打包状态。 |
refsHeadsTarget | git for-each-ref refs/heads/<target> | 目标分支引用的 SHA 和打包状态。 |
mergeBaseCommand | 构造所得 | 辅助程序重新运行的确切 argv,包括 'git' 前缀,可供操作员直接复制粘贴。 |
mergeBaseExitCode | 重新运行 git merge-base --is-ancestor <target> <source> | 导致祖先关系检查失败的退出代码。 |
mergeBaseStdout | 同一调用 | 捕获的标准输出。 |
mergeBaseStderr | 同一调用 | 捕获的标准错误(在默认 Git 适配器下会并入标准输出)。 |
失败时关闭:调试辅助程序中的任何单次 Git 调用失败时,该字段会使用空字符串或默认值,不会抛出异常。调试信息的附加操作绝不能掩盖操作员正试图排查的底层预检失败。
报告工作流。 捕获到包含调试信息的事件后,将完整的 data.debug 块附加到新的 GitHub issue,并添加相关范围标签(例如 windows、merge-orchestrator、preflight)。第 2 阶段的根因分析至少需要一个包含此载荷的真实主机事件。
架构发现
如需了解参数架构,请调用 exarchos:exarchos_orchestrate({ action: "describe", actions: ["merge_orchestrate"] })。事件载荷的形状可通过 exarchos:exarchos_event({ action: "describe", eventTypes: ["merge.preflight", "merge.requested", "merge.executed", "merge.rollback", "merge.recovered"] }) 获取(merge.rollback 是旧版、读取时兼容的事件——为支持重放 DR-2 之前的日志而保留,不再发出)。
工作流状态中的 mergeOrchestrator.* 字段由此技能写入,mergeOrchestrator.phase 由门控读取;底层的 phase 工作流字段不可变,必须通过 transition 而非 update 修改。完整的不可变键列表和类型化的 RESERVED_FIELD 错误信封,请参阅 checkpoint 技能中的保留字段部分。
完成标准
- 预检结果为
passed: true(或操作员已决定尽管存在有记录的预检缺口仍继续) - 编排器状态中的
mergeOrchestrator.phase === 'completed' - 流中存在
merge.executed事件,其中记录了mergeSha和rollbackSha(恢复点对应的旧版线缆字段,在 v2.11.x 弃用期内保留) - 目标分支的 HEAD 与记录的
mergeSha相符
如果任何一项标准未满足,请先查阅 references/recovery-runbook.md,再重新派发。