---
name: zcode-executor
description: 把一件已经想清楚的开发任务交给本机 ZCode（GLM）执行，在隔离的执行副本里改代码，用 git diff 和测试验收。当用户说「让 zcode 去做」「派给 zcode」「派给执行端」，或者当前会话负责规划、实际改代码要交给 zcode 时使用。也用于闲时投递（用户选择时）、查看 zcode 会话状态、给正在跑的回合插话、应答挂起的审批请求或提问。
---

# 把任务派给 zcode 执行

你是规划中枢，zcode 是执行端。你把要做什么写成任务单，zcode 在隔离的执行副本（git worktree）里改，
你用客观证据验收。命令是 `zcode-executor`，下面的命令块替换占位符后可直接复制运行。
终端里先 `command -v zcode-executor` 确认：在 PATH 里就照抄命令块。找不到时按宿主取值：
- Claude Code 插件装好时已进 PATH；散装用法里它是本 skill 目录上两级的 `bin/zcode-executor`（`<插件根>/bin/zcode-executor`），用绝对路径调；
- 本机源码用法：仓库 `bin/` 的绝对路径（示例：`/Volumes/MacData/projects/zcode-executor/bin/zcode-executor doctor`）；
- 也可以 `npm install -g zcode-executor` 或直接 `npx zcode-executor …`。

## 工作模式：主 agent 编排，原会话闭环

- 主 agent 负责任务单、调度和最终证据验收；实现与返工交给执行会话，代码评审与复审交给独立 review 会话。记录任务对应的执行 id、review id 和执行副本路径；review 会话只读。
- **返工 → 原执行会话；返工后复审 → 原 review 会话。** 将评审问题、复现证据和通过标准写入返工单，等原执行回合结束后带 `--task` 重投；将最新 diff、测试结果和返工单交回原 review 会话，逐项确认问题关闭。主 agent 的证据验收不代替 review，也不亲自接管修补。原会话确实不可恢复时才新建，并移交上述上下文。
- **投递后先推进独立工作，确实无事可做再等待。** 默认 `send` 不带 `--wait`，后台起 `follow` 并记录 job id；期间规划下一步、准备验收清单或梳理后续任务，不碰执行端正在改的文件，不提前使用尚未完成的产物。收到完成或挂起通知再处理，交付前收齐结果；不要用反复 `status` 或 sleep 循环代替后台等待。

## 铁律

1. **任务单是契约，消息只是门铃。** 任务内容全部写进任务单文件，投递正文一句「执行 tasks/T-xxx.md」就够——
   zcode 是有自己判断的完整 agent，一段话过去它会自己重新理解，任务单才是事后对账的依据。
   所以 `send` 必带 `--task`：不给任务单，模型审批看不到你授权了什么，越界判断没有依据，几乎全部转人工。
   审批认的是**投递那一刻**的任务单内容（入队时留了快照）：之后再改文件，审批看不到，带着 `--task` 再投一次才算数。
2. **验收看 `git diff` 和测试，不看它怎么说。** 回合结束带回来的回答只是执行端自述。判断做没做对，
   一律回主仓看 diff、在执行副本里跑测试、读产物文件。
3. **反复投同一条会话三四轮还没过，回去改任务单。** 通常问题出在任务描述不清，不是多投几次能解决的。
   改法：写 `tasks/T-xxx-fix.md` 说明差在哪，再投同一条会话（它有上下文，带着上下文返工）。
4. **挂起先分流，不替用户批越界。** 先核对任务单、用户已有授权和具体操作：能确认且规则允许主 agent 处理的，按「退出码 5」应答；越界、无法确认或上层指令要求人工确认的，带判断依据转用户。挂起本身不是危险判据，也不是放行依据；本 skill 不覆盖宿主的审批限制。
5. **Jev 只由配置启用前筛。** 非空白 `review.jev.apiKey` 启用提前通过层；pass 结束审批，flag/error/skip 回到
   原 ZCode `fast` + `review.thought`（默认 `low`）快筛，未通过或无法解析才慢判；原快筛调用失败仍转人工。
   正文省略、缺有效意图或必要输入截断时本地 skip，不发 HTTP；复杂命令本身不是跳过理由。无 key 保留原链，
   无环境变量 fallback 或 mode/shadow；`review.enabled:false` 关闭全部模型审批。

## 一个任务的完整走法

```bash
# 1. 先写任务单（模板 templates/task.md）
#    验收标准要能被机器检查：跑什么命令、看什么文件、期望什么输出
#    合理拆分任务：一单只做一件能独立验收的事；估计要改很多文件或跑很久测试的活儿，
#    拆成多单串行投（前一单过了再投下一单），别塞成一单
#    回合默认超时 1 小时（--timeout 3600）：任务还要更久的，派单手动给更长的 --timeout，
#    超时会把当前回合取消、重投浪费额度

# 2. 给仓库建执行副本（每个仓库一个，按任务切分支）
WT=~/.zcode-executor/worktrees/<仓库名>
git -C <主仓绝对路径> worktree add "$WT" -b task/T-xxx    # 首次：在 $WT 里装一次依赖

# 3. 建会话：只做零 token 登记，返回本地 id（x_ 开头）；zcode 会话第一次投递时才建
zcode-executor new --cwd "$WT" --title T-xxx --tier fast --json

# 每个对话第一次派单前：zcode-executor quota（零额度），按「闲时投递」一节的话术告诉用户，然后照常普通投递
# 4. 非阻塞投递（--task 必带），随后用后台任务起 follow <id>；按「工作模式」推进独立工作
#    消息里的 tasks/T-xxx.md 是让 zcode 自己在执行副本里读的相对路径，所以任务单必须放在 $WT 里；
#    --task 给的是模型审批看的授权依据，用绝对路径
zcode-executor send <id> "执行 tasks/T-xxx.md" --task "$WT/tasks/T-xxx.md"

# 5. 收到完成通知后，交独立 review 会话评审；主 agent 核对客观证据。
#    zcode 不提交，改动停在执行副本工作区，先把未跟踪文件收进来再看 diff
git -C "$WT" add -A && git -C "$WT" diff --cached --stat && git -C "$WT" diff --cached
git -C "$WT" <跑测试的命令>
#    还想看审批经过：$ZCODE_EXECUTOR_HOME/runs/<id>/events.jsonl 里 type 为 executor.gate 的行

# 6. 过了：在 $WT 里提交（git -C "$WT" commit -m "T-xxx: …"），再回主仓合并（见下一节）。
#    没过：git -C "$WT" reset 撤掉暂存，写 tasks/T-xxx-fix.md，投原执行会话；完成后交原 review 会话复审
```

默认投递立刻返回、后台 runner 继续工作；`follow` 用宿主的后台任务工具启动（`run_in_background: true`），记下返回的 job id。
仅当前下一步确实依赖结果且无独立工作时才等待该后台任务；`send --wait` 是可选阻塞用法，挂起时以退出码 5 返回。

## git 一律 `-C <绝对路径>`，别靠 cd

主仓和执行副本是两个目录，一条 Bash 里 `cd` 过一次之后，后面的 `git merge`、`git checkout -b`
就可能跑在你以为之外的那个仓库里。真踩过的坑：在执行副本里跑完测试接着 merge，merge 落在执行副本
（HEAD 就是那个分支，自合并什么都没变），主仓 main 没动，下一单从 main 切的分支缺上一单的代码。

```bash
git -C <主仓绝对路径> merge --ff-only task/T-xxx      # 合并进 main：在主仓
git -C <主仓绝对路径> log --oneline -1                # 切新分支前核对 main 已含上一单
git -C "$WT" checkout -b task/T-yyy main              # 切新分支：在执行副本
```

## 模型等级、思考等级与 Jev 前筛

按等级选 ZCode 模型，不写模型名——模型换代不用改习惯。`models` 能看每个等级当前分到了谁。Jev 只增加可选前筛，不改变执行会话的 `--tier`，原 ZCode 快筛与慢判仍保留。

| 活儿 | 建议 |
| --- | --- |
| 机械改动（改名、搬代码、按明确清单补测试） | `--tier fast` |
| 常规实现（有明确验收标准的功能） | `--tier fast` |
| 难活（要设计取舍、调试难复现、改陌生代码） | `--tier strong` |

- **思考等级默认 `high`，都不用调。** Flash 类模型不要往低调，实测效果差。
- provider 优先账号型个人版 coding plan（App 账号登录后的 key），其次账号型团队版，再次 `config.json` 里的 coding plan；同名模型在多个 provider 下并存时不用管，插件自己选对，要换就在插件配置里写 `preferredProvider`（如 `account:bigmodel-team-coding-plan`）。
- 模型和思考等级建会话时定，同一条会话后续投递沿用。难活别复用之前建的 fast 会话，新开一条。
- **原 zcode 会话上下文超过 1M token 就换新会话派单。** 会话越长每次投递的固定开销越大、执行效率越低；
  旧会话攒的上下文对新任务多半是噪声。新任务一律新会话（必要时配新副本），把任务单和必要 diff 移交过去。

可选启用 Jev 前筛时，编辑 `~/.zcode-executor/config.json`；**不要**把真实 key 放进命令参数、环境变量、任务单、聊天消息或仓库文件：

```json
{
  "review": {
    "jev": {
      "apiKey": "jev_请替换为用户自己的密钥"
    }
  }
}
```

保存后必须执行：

```bash
chmod 600 ~/.zcode-executor/config.json
```

含 key 的配置文件必须由当前 UID 拥有、是普通非符号链接文件且权限不宽于 `0600`，否则命令会报配置错误。删除 `apiKey` 或留成全空白可只走原 ZCode 链；不要找 Jev 环境变量或 mode/shadow 开关。`doctor` 显示新 runner 的审批链，不联网调用 Jev、不显示 key：`review.pipeline` 有 key 为 `['jev','zcode-fast','zcode-slow']`，无 key 为后两项，禁用为 `[]`，配置错误为 `null`。兼容 `fastScreen` 不表示替代原快筛。

## 退出码与应对

| 码 | 含义 | 你该做什么 |
| --- | --- | --- |
| 0 | 回合干完了 | 去验收，别直接信它说完成了 |
| 1 | 用法错、zcode 起不来（含 ZCode App 早于 3.12） | 看报错；环境问题跑 `zcode-executor doctor`（不花额度的自检）。它退出码 1 时先看是哪一步不过：只有 ⑤ 闲时接口变了的话，普通派单照常能用 |
| 2 | 被拒：白名单外、会话不在登记簿、等级或思考等级不合法；闲时投递被拒 | 报错里写了原因，按原因处理，别原样重试；闲时的见「闲时投递」一节 |
| 3 | `send --wait` 超时（默认 3600 秒，`--timeout` 可改），**当前回合已取消**，会话还在 | 直接再投一次接着做；`follow` 的超时不取消任何东西，只是旁观者到点走了 |
| 4 | 回合异常：报错、被中止、撞输出上限 | 读 reason；撞上限就把任务拆小再投 |
| 5 | 挂起等人：审批请求或提问 | 见下一节，别想办法绕过去 |

## 退出码 5：挂起的两种情况

挂起不过期，会话停在原地等人。`send --wait` 立刻返回 5 并打印挂起内容；后台跑时用 `status <id>` 看。

**审批请求**（执行端要做某个操作，等你允许）：

1. 读取完整工具名、参数、目标路径和挂起理由，对照任务单与用户授权判断实际影响；证据不足先做只读核查。
2. **可自行确认**：操作明确在授权范围内、未命中红线，且上层指令允许自主应答 → 说明依据后 `zcode-executor approve <id>`（仅本次）；明确违反任务约束且允许自主拒绝 → `zcode-executor deny <id>`，将原因交回原执行会话调整方案。
3. **需人工决定**：越界（例如执行副本外写入）、新增权限或不可逆影响未获授权、证据不足，或上层指令明确要求转交 → 原样转述工具名、参数、理由，附建议与未确认点（Claude Code 用 AskUserQuestion，DSH 走宿主的上抛）；得到决定再 `approve` / `deny`。不得通过改任务单、关闭审批或换命令绕过限制。

   规则是 `self-modify-approval` 的要格外当心：那是执行端在碰 zcode-executor 自己的数据目录（审批的挂起与应答都在里面）或者在调 `zcode-executor` 命令。正常干活用不着这两样，原样告诉用户它想做什么，别替它解释成无害。

**应答绑定限制（approve / deny / answer 都适用）**：当前 CLI 会重新读取 pending，未提供 expected-requestId/CAS；只做两次读取或声称单人操作不能防止串答。只有已验证的外部串行化覆盖「核对当前请求 → CLI 读取」全过程，才执行应答；否则保留挂起并报告该限制，请用户协调安全处理，人工说“允许”本身也不消除竞态。不要虚构参数或直接写 answer.json。

**提问**（执行端反过来问你问题，要答案不是要允许）：

1. 先自己判断任务单里有没有答案，能答就不必惊动用户：`zcode-executor answer <id> <值…>`。
   有选项按序号、value 或 label 给值，多选逗号分隔；无选项就是自由文本。
2. 答不了再问人，拿到答案后同样 `answer` 回过去。

## 插话、跟看、叫停

```bash
zcode-executor send <id> "方向改一下，先只改 A" --steer   # 回合进行中插话，不打断
zcode-executor follow <id> [--stream]                     # 跟看后台 runner，写出新结果就返回
zcode-executor status <id>                                # 只读快照：状态、最近工具、队列、挂起多久
zcode-executor cancel <id>                                # 叫停：挂起的先答拒绝，再停回合，不再取队列
```

- **`--steer` 是插话不是打断。** 消息递给正在跑的回合，不打断它；要打断先 `cancel` 再重投。
  插话在下一个工具边界生效；没有边界时排到回合结束后执行。
- **`follow` 用后台任务起**（`run_in_background`），它退出时你会被自动叫回来。别在前台用 timeout
  包着等——超时切断的只是你这个旁观者，回合还在跑，你却容易当成它挂了。
- 想随时问「到哪了」用 `status`：只读、不花额度、不阻塞。phase 的取值：`running` 在跑、`pending` 挂起等人、
  `idle` runner 活着但队列空、`exited` runner 跑完正常退出（配合「上次: done」看，不是出事）、`stale` runner 跑到一半没了，最需要人工看。
  `list` 每行的 `[phase]` 同一套词，没有 running / pending 就是没有在跑的会话。
- 要边跑边看，`send` / `follow` 加 `--stream`：工具调用摘要和成型回答打到 stderr。

## 闲时投递与 start plan 投递

走闲时（`--offpeak`）或 Start Plan（`--start-plan`）额度前，先读 `references/offpeak-and-start-plan.md`：
什么时候提闲时、`quota` 话术、取号/排号/恢复的完整命令、退出码 2 的取号被拒对照表、cron 健康检查都在那里。

## 两种会话 id

- 命令一律用**本地 id**（`x_` 开头，`new` 返回的那个）。
- zcode 自己的 `sess_…` id 首回合之后才存在，只在 `list` 和各命令的 `--json` 里和本地 id 并排显示，对账用，不敲命令。

## 别做的事

- **不要在 ZCode App 里打开正在跑的会话。** 两边写同一个 sqlite，没有跨进程锁，会互相踩。
- **不在执行副本之外派活。** `new` 的 cwd 白名单默认只有 `~/.zcode-executor/worktrees/`，
  那是保护不是障碍；两个 agent 同时改同一批文件会互相覆盖。
- **不要把「zcode 说改好了」当验收写进汇报。** 验收只认 `git diff` 和测试结果（铁律 2）。
- 不要投第四轮。三四轮没过，改任务单（铁律 3）。
- **任务单里别写凭据。** 任务单会作为授权依据进入模型审批；启用 Jev 且通过本地证据检查时，脱敏后的任务契约与操作摘要会送往 TypeSafe；必要内容被截断则本地跳过。
- **不要用 shell 参数或环境变量配置 Jev key。** 唯一入口是受 `0600` 保护的 `review.jev.apiKey`；示例永远用占位值。

## 命令速查

```bash
zcode-executor doctor [--json]                  # 不花额度的自检：zcode、配置、握手、等级、新 runner 的审批链、闲时接口（⑤ 会联网，每跑一次留一条自检会话）
zcode-executor doctor --offpeak [--json]        # 只查闲时接口（同样留一条自检会话）；只有退出码 1（接口变了）要处理
zcode-executor quota [--json]                   # 今天本工具取过几次闲时号、服务器现在能不能取；零额度，不取号
zcode-executor models [--json]                  # 可用模型、思考等级、禁用原因、自动分到哪个等级
zcode-executor list [--project 关键字] [--json]  # 登记簿里的会话：本地 id、sess_、上次结果、是否挂起
zcode-executor new --cwd <绝对路径> [--title T] [--tier fast|strong] [--thought 档] [--deny "工具…"] [--provider id] [--json]
zcode-executor send <id> <正文|-> [--task 文件] [--wait] [--steer] [--timeout 秒] [--stream] [--json]
zcode-executor send <id> <正文|-> --offpeak [--task 文件] [--wait] [--timeout 秒] [--stream] [--json]  # 闲时投递：当场取号
zcode-executor send <id> --offpeak --resume [--wait] [--stream] [--json]  # 接着跑没收尾的闲时投递
zcode-executor send <id> <正文|-> --start-plan [--task 文件] [--wait] [--timeout 秒] [--stream] [--json]  # Start Plan 额度
zcode-executor follow <id> [--timeout 秒] [--stream] [--json]
zcode-executor status <id> [--tools N] [--json]  # 在跑/空闲/挂起/异常结束、最近工具调用、队列长度
zcode-executor cancel <id>                       # 叫停：挂起的先答拒绝，之后不再取队列
zcode-executor approve <id>                      # 应答挂起的审批请求，只回 allow_once
zcode-executor deny <id>                         # 应答挂起的审批请求为拒绝
zcode-executor answer <id> <值…>                 # 应答挂起的提问：按序号/value/label，多选逗号分隔
```

- 观察面板实时显示各会话进展（回复、工具、挂起与排队）：Claude Code 里用户输入 `/zcode` 打开（你投递时自动打开，
  状态栏汇总、关键变化弹提示），DSH 右侧栏有「zcode 任务」tab。面板只看不应答，挂起照样按上面的规矩在对话里处理。
  背后是你不需要自己跑的只读 `zcode-executor watch --json`。

- 正文写 `-` 从 stdin 读，长文本别跟命令行引号较劲：

  ```bash
  zcode-executor send <id> - --task "$WT/tasks/T-xxx.md" <<'EOF'
  执行 tasks/T-xxx.md，按验收标准逐条自检后再停
  EOF
  ```

- `--deny` 建会话时从工具注册中移除指定工具。**任务不需要执行端上网就带上 `--deny "WebFetch WebSearch"`**，减少联网工具的可用面。
  这只移除这两个工具，Bash、js 和 MCP 工具仍可能联网；需要断网时要由系统或容器限制网络。
- 你自己环境里的凭据变量（`ANTHROPIC_API_KEY`、`OPENAI_API_KEY`、`GEMINI_API_KEY`、`GITHUB_TOKEN`、`GH_TOKEN` 等）不会带给执行端。
  任务要用 GitHub 凭据时靠本机已登录的 `gh` / git 凭据助手，别指望环境变量。
- `--json` 给机器可读结构，脚本化处理时用；输出里的 `id` 都是本地 id。
- cwd 会解析成真实路径存进登记簿（macOS 的 `/tmp/x` 会变成 `/private/tmp/x`），`list --project` 按解析后的路径匹配。
- 数据目录 `~/.zcode-executor/`（`ZCODE_EXECUTOR_HOME` 可改）：`sessions.json` 登记簿，`runs/<id>/` 里有 `events.jsonl`（zcode 事件与闸门决定）、`last.json`、`pending.json`、`offpeak.json`（闲时投递时）、`runner.log`。
