关于 dispatch-agent
dispatch-agent 是面向 Claude arm 的技能,用于启动隔离的 dispatch worker,并指导其受控提交、导入、验证和清理流程。它适合运行 Doctrine dispatch 工作流的协调者。
主要功能
- 01在指定基线上创建 worker 分支
- 02执行生成前与 worker 安全检查
- 03指导 worker 通过 worker_commit 自行提交
- 04导入、验证、结束阶段并回收分支
- 05说明实时 worktree 备用流程
适用场景
- 01为文件互不重叠的任务启动并行 worker
- 02将 worker 已提交的更改合入协调树
- 03主路径不可用时恢复 worker 未提交的更改
MCP 相关
该技能要求 worker 通过 worker_commit MCP 工具提交,并指导协调者使用 dispatch_import、dispatch_conclude_phase 和 dispatch_reap MCP 工具。
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ davidlee/ doctrine --skill dispatch-agent安装后在 Claude Code 中输入/dispatch-agent确认已加载。
详细介绍
Dispatch — claude arm
通过 Agent 工具生成工作者。与 harness 完全相同的漏斗和驱动循环位于 /dispatch router 中——此技能是生成模板,以及此分支的落地机制。
生成前 — 启用基础版本,切换到生成目录
工作者的工作树由 doctrine 的 WorktreeCreate hook
(doctrine worktree create-fork)创建,而不是由 harness 原生创建。该 hook
通过位置判断是否为 dispatch 工作者:仅当 Agent 载荷的 cwd 是启用目录
<coord>/.doctrine/state/dispatch/spawn/ 时,生成的才是 dispatch 工作者。它分出的基础版本是显式指定的——该目录中的 base 文件——绝不会使用 cwd HEAD。cwd 是判断条件,不是基础版本来源。
启用有双重作用:该 hook 还会为工作者配置其
DispatchRecord——注册信息 worker_commit 会据此解析工作者的不透明 agent id。未启用的生成会作为正常工作树直接通过,不会创建记录,因此其 worker_commit 会拒绝 unknown-agent,也就完全无法落地分支提交。
基础版本清洁检查(在 arm-spawn 之前)。 在启用/生成之前,以及在分出分支前于 main 上,确认基础版本已通过证明检查——doctrine check prove(非变更型 fmt-check + lint)。基础版本出现 RED 属于 BASE 缺陷(由操作员格式化并提交/准备工作树),绝不能并入工作者的变更,绝不能自动修复。这与漏斗中记录的生成前检查相同;每批运行一次即可。
每次生成(或并行批次)之前:
doctrine dispatch arm-spawn --base <B> --slice <N> --phase PHASE-NN——写入<coord>/.doctrine/state/dispatch/spawn/base = <B>以及两个分支绑定槽位,并打印生成目录的绝对路径。支持幂等操作:在 B′ 重新启用会重写全部三个值。两个绑定部分都必须提供——只提供一半不会绑定任何内容,且会在启用时被拒绝(half-arm:);两部分都不提供则会被接受,但会发出警告,并导致分支的worker_commit在交接时以unprovable-fork拒绝——整个工作者轮次都将白费。cd进入该生成目录。这是启用信号——只有载荷 cwd 位于生成目录时,hook 才会在 B 处分支。- 发出 Agent 生成请求(见下文)。文件互不重叠的并行批次:启用一次,然后从生成目录发出 N 个生成请求——它们读取相同的 B;每个 hook 根据各自的
name推导自己的branch/dir。 - 生成请求发出后,
cd返回协调根目录——按位置解除启用。从协调根目录发出的正常isolation:worktree生成请求会直接通过(会配置,但不会分支为工作者);只有从生成目录内部发出的生成请求才是 dispatch 工作者。遗留的base文件不会产生影响——触发条件是 cwd 的位置,而非文件是否存在。
**串行驱动:**每个阶段都重新启用——在 B 处启用,切换进入目录,生成,再切换返回;漏斗提交会推进协调 HEAD;下一阶段在 B′ 处启用。基础版本是显式指定的,因此启用与创建之间协调 HEAD 的漂移无关。默认 cwd 是协调根目录;只有发出工作者生成请求时才切换到生成目录。
**位置前提(ISS-031):**协调工作树(因此也包括其启用目录)必须位于项目根目录内——约定使用 .dispatch/SL-<n>(dispatch setup --dir .dispatch/SL-<n>)。在 cwd 受限的隔离环境中(以 /workspace/<repo> 为根的 bubblewrap),切换到外部同级目录后,下一次 Bash 调用会静默地返回项目根目录。若 --dir 解析到根目录之外,dispatch setup 会在 claude arm 上拒绝执行。请在单独的 Bash 调用中执行不带其他命令的
cd <spawn-dir> ; pwd,确认位置无误后再继续。
Spawn
基础防护 — 必须先运行,然后才能进行任何读取、编辑或提交。如果任何检查失败,立即停止且不写入任何内容:
- git status --porcelain → 必须为空(工作树干净)
- git rev-parse --git-dir vs --git-common-dir → 必须不同(隔离的链接工作树,而非主工作树)
- git merge-base --is-ancestor HEAD → 必须退出 0(HEAD 是从基线 派生的)
- grep prerequisite seams: → 必须存在 任何检查失败时:立即停止,不得创作或提交任何内容,并报告 "base-guard-failed: "。
检查 #2 是编排器的 not-isolated 防护带在工作器内的镜像 —
如果工作器的 git-dir 等于 git-common-dir,就不是隔离的工作树。
subagent_type: dispatch-worker
isolation: worktree
prompt: <pre-distilled worker prompt, including the base-guard block above>
提示词 MUST instruct the self-commit:最后调用
worker_commit MCP 工具,并提供工作器自己的工作树名称(绝不能是路径)
以及提交消息,然后在结构化交接中报告返回的 oid(fork_tip)。在隔离环境中,原始 git commit 会失败(.git 只读) —
worker_commit 是工作器唯一的提交途径。遇到 Refused 结果时,工作器必须原样报告原因并停止;绝不能绕过 forbidden-zone/commit-gate-red 拒绝后重试。
Spawn 后的流程 — 工作器自行提交,编排器落地
主要路径(B):工作器的增量会作为一个已提交的非合并提交 C
(C^ == B)出现在它自己的 dispatch/<agent> 分支上,由受门控的
worker_commit 工具在服务器端落地(防护带是安全边界:非空增量 →
forbidden-zone 范围检查 → HEAD == B → check commit 门控)。因此,该增量是
分支持久的 — 它会在工作树之外保留,恢复/修复时会从已提交的最新提交继续。
排序由 oracle 负责,不是你。 doctrine dispatch next --slice <N>
会指定当前应执行的一个操作;路由器会说明循环流程。以下内容是此路径上各项指令的
具体操作方式,不是一套固定执行顺序。
到达(在流程能够看到增量之前)。
- **页脚。**读取 Agent 返回页脚中的
worktreePath:。 没有页脚/没有worktreePath:⇒ 未创建隔离树(钩子中止或 回退到主工作树)⇒ 中止,不要进入流程。重新分派,或者如果钩子持续失败, 切换到子进程路径。 - **身份。**从
worktreePath推导(规范数据,已实时验证):name = basename(worktreePath),branch = dispatch/<name>。不要读取 页脚的worktreeBranch字段 — 对于钩子创建的树,它是undefined。 - 验证工作器。
doctrine worktree verify-worker --base <B> --dir <worktreePath> --branch <branch>任何拒绝均须中止:no-worker-head / not-isolated / unstamped / wrong-base / branch-mismatch。它接受提交后的 HEAD(测试merge-base --is-ancestor B HEAD,即后代 — 而非HEAD == B);--branch将目录与分支绑定,使两条防护带验证同一个工作器状态。
import — dispatch_import{slice, name: <branch>}(MCP;next 会生成
此调用)。按切片在服务器端解析 coord 树,运行 classify_import
范围防护带,作为硬性合成前门控(拒绝 .doctrine//.claude/,
拒绝未声明范围 — 遇到拒绝时不会落地任何内容),通过 merge-tree 合成 coord-tip ⊕
worker-tip(仅操作对象数据库,不涉及工作树),并落地一个非合并提交,
保留工作器的作者信息。导入和提交合并为一步 —
没有单独的编排器提交步骤。任何 Refused{reason} ⇒ 报告并停止
(merge-conflict、head-moved、undeclared-scope 等绝不会自动解决)。
该增量已通过提交门控(worker_commit 已运行门控),因此此路径无需单独运行导入后的证明。
verify — doctrine dispatch verify --slice <N> --phase PHASE-NN(CLI)。
在协调工作树中运行阶段套件,并落地通过/失败证据;
失败证据会使下一项指令变为 triage-verify-failure。
conclude — dispatch_conclude_phase{slice, phase, code_start, code_end}
(MCP;next 会根据漏斗行将其完整参数化)。一次调用,两个层级:将被 git 忽略的阶段表切换为 completed,并写入已提交的 (code_start, code_end) 边界行(按阶段执行 UPSERT,不依赖工作树)。此切换会同时作用于 coord 表(下一阶段就绪状态的权威来源)和 PRIMARY 表(树的 prepare-review 完整性门槛从中读取已完成阶段集合)——无需手动再执行 slice phase --status completed -p <primary>。coord→primary 镜像由单一写入器 set_phase_status(ISS-212)实现,因此并非 conclude 专属:在 dispatch/<slice> 工作树仍存在时,coord 树中任何 completed 切换都会向下镜像,无论是通过此工具还是原始的 slice phase --status 完成。重试时具有幂等性;唯一的故障结果是表已切换但边界尚未提交,重试即可重新组合。
reap — dispatch_reap{slice, name: <branch>}(MCP)。运行 patch-id 已落地判定器(git cherry):若分叉的补丁不在 coord 历史记录中,则拒绝操作;否则移除已落地分叉的工作树和分支。只能通过此判定器执行 reap——绝不要对尚未导入的树直接运行 git worktree remove --force(这会删除未落地增量的唯一副本;工具的拒绝机制正是原始 remove 所缺少的保护)。
回退方案(A)——导入活动工作树
主路径无法运行时的应急方案:MCP 服务已关闭,或 Worker 从未完成配置(worker_commit → unknown-agent),因此不存在分叉提交。Worker 尚未提交的增量仍位于工作树中,并会在 Agent 返回后继续保留(没有 WorktreeRemove 钩子——运行框架不会自动执行 reap)。这不是绕过流程:若因 forbidden-zone / commit-gate-red / undeclared-scope 被拒绝,说明 Worker 增量存在缺陷——应修正或停止,绝不可绕过检查直接导入。
- 按上述方式执行
verify-worker(此模式下 HEAD == B——没有提交)。 doctrine worktree import --base <B> --from-worktree <worktreePath> --slice <N>— 收集当前已跟踪和未跟踪的增量,运行相同的classify_import防护流程,将其应用到B但不提交,然后在进程内运行拒绝并停止的证明门槛(未格式化或 lint 未通过的增量会以暂存状态停止,绝不会自动修复)。- 自行在协调分支上创建一个提交,然后记录边界:
doctrine dispatch record-boundary --slice <N> --phase PHASE-NN --code-start <B> --code-end <B+1>。 - 只有在增量已落地后才执行 reap——在此路径下,协调提交落地之前,工作树是唯一副本;移除操作必须用
&&串接在提交之后。
这也是 pi/子进程分支唯一可用的模式——受 bwrap 限制的 Worker 完全无法提交(/dispatch-subprocess)。绝不要要求或期待 claude 分支自行提交。
Worker 隔离(SL-182)
claude worker 受两个已安装的 PreToolUse 钩子约束(插件
hooks.json → doctrine worktree pretooluse):Bash 会被不透明地重写为嵌套的 bwrap 沙箱(工作树可读写,其他所有内容只读);工作树外的 Edit|Write
会被拒绝。编排器(没有 agent_id)不受限制。
隔离由构造保证——不依赖 worker 配合设置标志。worker_commit 是刻意设置的、用途单一的绕过通道:不受隔离的服务
代表 worker 提交,而该工具的防护机制——而非隔离墙——构成了此处的安全边界。
- 不会热重载。 插件
hooks/的更改仅在会话开始时加载 (docs/claude/plugins-reference.md:394)。运行doctrine install(或编辑任何钩子)后,使用/reload-plugins(更轻量)或重启会话来加载更改——否则生效的仍是旧钩子(或没有钩子)。 - 安装时默认拒绝。 安装的命令是已解析的绝对
可执行文件路径,并带有
|| exit 2保护,因此二进制文件缺失或过时会拒绝执行,而不是不受隔离地运行工具(PreToolUse 在其他情况下会默认放行——只有退出码 2 才会阻止)。 - 逃生通道。 有问题的 Bash 包装器可能会让你无法使用 Bash,但 Edit/Write 不受 Bash 限制:使用 Edit 禁用有问题的钩子,然后运行
/reload-plugins。
危险信号
绝不可: 未先运行 arm-spawn 并 cd 进入 spawn 目录就启动(从 coord 根目录启动只会无害地透传,绝不会成为派发 worker——且其 worker_commit 会拒绝 unknown-agent);读取页脚的
worktreeBranch(对于钩子创建的树,该值未定义——应改为推导
branch = dispatch/basename(worktreePath));将未返回 worktreePath: 页脚的 worker 导入漏斗;存在 fork 提交时导入活动树(差异就是该提交——通过 dispatch_import 落地);使用回退方案 (A) 绕过 worker_commit/dispatch_import 的拒绝(拒绝意味着存在缺陷或应停止,绝不是绕行);对尚未落地的树直接运行 git worktree remove --force(通过 dispatch_reap 的 patch-id 判定器回收);声称在 pi arm 上自行提交;将 dispatch setup --dir 指向根目录外的同级目录(ISS-031);使用 subagent_type 不是 dispatch-worker 的方式启动;在这里运行 fork 或 bwrap(应使用 /dispatch-subprocess);声称可以并行落地(v1 每个 base 只落地一个);凭记忆手动串联落地命令,或手动从 git 状态推断漏斗状态(应询问 doctrine dispatch next --slice <N>)。
务必: 运行 arm-spawn --base B --slice N --phase PHASE-NN(两个绑定部分都要提供,不能只提供一个),然后在启动前 cd 进入 spawn 目录,启动后 cd 回 coord 根目录;将 subagent_type 固定为 dispatch-worker;在提炼后的 worker 提示词中加入 base-guard 区块和 worker_commit 自行提交指令;从 worktreePath 推导 branch;落地前运行 verify-worker;只做 dispatch next 指示的一件事,然后再次询问;返回路由器遵循驱动节奏。