Skip to content

第 7 章 · 实验性后端:herdr、zellij、Orca 与 cmux

本章目标:掌握 tmux 之外的四个实验性会话后端的选择规则、安装要点与安全边界,学会为不同工作环境挑选合适的后端并正确切换。

7.1 为什么需要多个后端

第 6 章我们学习了 tmux 参考后端——它是 FirstMate 唯一的"参考实现"(reference backend),行为保证最完整。但并非所有人都用 tmux:有人习惯 Herdr 这类新一代终端管理器,有人在 macOS 上把 cmux 当主力终端,还有人已经深度使用 zellij 或 Orca。

FirstMate 的答案是:会话端点可以替换,监督协议不变。无论 crewmate 跑在哪个后端的窗口里,firstmate 都通过统一的元数据路由接口(bin/fm-peek.sh <id> 查看、FM_HOME=<home> bin/fm-send.sh <id> '<text>' 发消息)来监督它——你不需要为每个后端学一套新指令。

不过官方对这四个后端的定位很明确:它们都是实验性的(experimental)。tmux 拥有最完整的维护者验证(见 docs/verification/runtime-backends.md),其余后端各有已记录的能力边界。选择时的优先级建议是:

text
tmux        参考默认,保证最完整,无特殊理由就选它
herdr       想要原生 busy/idle/blocked 状态信号时选它
zellij      已经在用 zellij 且接受其当前限制时选它
orca        macOS 用户想连 worktree 一起交给 Orca 管理时选它
cmux        macOS GUI 用户想把任务放进 cmux 侧边栏时选它

7.2 选择规则:显式设置与自动检测

所有后端的共享选择语义由 docs/configuration.md 统一拥有。指定后端有三种方式:

bash
# 方式一:本地 gitignored 配置文件(持久生效)
echo "zellij" > config/backend

# 方式二:环境变量(仅本次启动生效)
FM_BACKEND=zellij claude

# 方式三:对话中直接向 firstmate 提出明确请求

关键区别在于谁能被自动检测(runtime auto-detection):

后端自动检测说明
tmux参考默认
herdr主会话原生跑在 HERDR_ENV=1 下且不在 tmux 内时选中
cmux主会话跑在 cmux 内时选中
zellij永不自动检测,必须显式设置
orca永不自动检测,必须显式设置
codex-app尚不是 runtime backend(blocked boundary,见 docs/codex-app-backend.md

自动检测遵循"内层多路复用器胜出"原则。以 cmux 为例,检测顺序是先查 tmux,再查 herdr,最后才是 cmux——所以嵌套在 cmux 里的 tmux pane 会被识别为 tmux 后端。herdr 同理:tmux pane 嵌套在 Herdr 内时解析为 tmux。

还有一个重要细节:自动检测只决定后端,永不授予凭据或放行安全设置。比如 cmux 的套接字访问没配置好,自动检测也不会帮你绕过——spawn 会带着可操作的设置指引拒绝启动。

7.3 herdr:带原生状态信号的终端管理器

herdr 的最大卖点是提供原生的 busy / idle / blocked 状态信号——其他后端没有这种信号,监督只能靠屏幕截图哈希轮询加各 harness 自己的生命周期语义来判断忙碌状态。

基础要求:herdr、jq,以及协议版本底线。它的拓扑约定是:

text
Herdr 会话
└── home 工作区(home label: firstmate 或 2ndmate-<id>)
    ├── fm-<task-id-1>  任务 tab
    ├── fm-<task-id-2>  任务 tab
    └── ...

每个任务 tab 放在与发起者完全一致的工作区里(从注入的 socket 身份现场解析,而不是信任快照)。身份无法精确解析时 spawn 直接拒绝,绝不退化为按标签搜索——因为 Herdr 不强制工作区/tab 标签唯一,标签不能决定工人去向。

Presentation spaces 与版本底线

Herdr 0.8.0 起,每个新 crewmate/scout 默认被投影到一个一次性的单任务工作区(presentation space),让视觉上每任务一栏。但这有一个版本底线:

bash
# 关闭投影(写入本地 gitignored 配置)
echo "off" > config/herdr-presentation-spaces

# 强制开启(即使低于版本底线也接受已文档化的焦点移动代价)
echo "on" > config/herdr-presentation-spaces

# 文件不存在 → 由版本底线决定:
#   Herdr >= 0.8.0 → 默认开启投影
#   Herdr <  0.8.0 → 使用普通的按 home 平铺布局,并警告一次

为什么有这条底线?0.8.0 之前的 Herdr 有一个焦点缺陷:清理任务时活动工作区会被短暂移走再恢复(约七分之一秒),而"每次清理都清空整个工作区"恰好是唯一能避开该缺陷的删除形态。0.8.0 修复了这个问题,所以未配置的家只在达标版本上才默认开投影。

仓库还自带两个辅助脚本 bin/backends/herdr-workspace-move.pybin/backends/herdr-eventwait.py,分别处理工作区移动和事件等待。

7.4 zellij:显式专用的共享会话后端

zellij 是显式专用(explicit-only)后端:永远不会被自动检测到。它使用一个共享会话(默认名 firstmate),每个任务一个 tab:

bash
# 用 FM_ZELLIJ_SESSION 为隔离验证选择别的会话名
FM_ZELLIJ_SESSION=fm-test zellij --session fm-test

# 附上去围观(日常监督不需要 attach)
zellij attach firstmate

要求 zellij 0.44+ 和 jq。可见标题采用 home 作用域格式 fm-<home-label>-<id>(如 fm-firstmate-42),加上 FirstMate 安装路径的稳定短哈希——这样主家、secondmate 家、甚至同一台机器上的多个 FirstMate 安装共用一个会话时,任务也不会互相混淆。

zellij 有个著名的坑:CLI 动作命令对不存在的会话或 pane 也返回 exit 0。适配器的对策是不信任退出码,而是在每次操作前后做结构化校验:

text
操作前:验证会话存在 → 验证 terminal pane 存在 → 验证期望的 scoped title
操作后:校验 JSON/整数响应形状
残余竞态:pane 可能在校验后、操作前消失 → 下游报告这个窄竞态,
         而不是把 exit 0 当成功

发送消息也有类似严谨性:Enter 发出前,适配器必须证明 composer 内容恰好增加了粘贴的文本;不可读的 composer、粘贴落到别处、无关输出都会失败而不提交。死 pane 更是永远 fail-safe——空屏幕分类为 unknown,绝不会被误认为"确认送达"。

7.5 Orca:worktree 与终端一体化的 macOS 后端

Orca 是四个实验后端中最特别的一个:Orca app 同时拥有任务的 git worktree 和终端端点。其他后端都是"Treehouse 提供 worktree + 多路复用器提供会话"的两层结构,Orca 把两层合并了:

text
tmux/herdr/zellij/cmux 任务:Treehouse worktree + 会话端点(两层)
orca 任务:                  Orca-managed worktree + Orca 终端(一层)

因此 fm-spawn.sh 对 orca 任务不调用 Treehouse。spawn 前 firstmate 要求 orca status --json 报告 reachable=truestate="ready";项目的第一个任务会自动用 orca repo add --path 注册仓库,无需手工注册。

要点清单:

text
平台          仅 macOS
CLI 安装      brew install orca
选择方式      config/backend 写 orca / FM_BACKEND=orca / 明确请求(永不自动检测)
secondmate    不支持 secondmate spawn
按键支持      Enter、Ctrl-C 支持;Escape 不支持
隔离规则      正常的隔离与 unlanded-work 拒绝规则照常适用

清理时保留全部共享安全检查:scout 仍然必须先交出报告并完成 decision inventory 才能收尾。

7.6 cmux:macOS GUI 工作区后端

cmux 同样是 macOS 专属、GUI 优先的后端,任务以 workspace + surface 形态出现在 cmux 侧边栏里。它不适合 headless 或纯 SSH 环境。

套接字访问:一次性手动设置

cmux 默认的控制模式拒绝外部 shell 连接,而 FirstMate 必须从外部进程控制它。首次 spawn 前需要在 cmux 的 Settings > Automation 里选一个可行的 Socket Control Mode:

设置FirstMate 支持安全边界
Offoff套接字监听关闭
cmux processes onlycmuxOnly仅 cmux 应用后代可连
Automation modeautomation✅ 推荐属主 0600 套接字,限当前 macOS 用户进程
Password modepassword0600 套接字 + 认证握手
Full open accessallowAll✅ 不推荐0666 世界可写,任何本地用户无需认证即可执行命令

Password 模式下密码存放有两种途径:

bash
# 途径一:本地 gitignored 文件的第一行
echo "your-secret" > config/cmux-socket-password

# 途径二:环境变量 CMUX_SOCKET_PASSWORD

适配器每次调用都现读密码文件,并且文件不存在时绝不会覆盖你环境中已有的 CMUX_SOCKET_PASSWORD。注意:模式和密码都要通过 cmux UI 配置,不要手改 cmux.json——应用不保留手工添加的密码键。

UUID 与恢复

每个任务拥有一个 workspace + 一个 surface,元数据形如:

text
backend=cmux
window=<workspace-uuid>:<surface-uuid>
cmux_workspace_id=<workspace-uuid>
cmux_surface_id=<surface-uuid>

workspace UUID 在应用重启后会变,所以恢复逻辑按作用域标题搜索再解析当前 surface id,UUID 从不作为恢复权威。运行时检测标记是 CMUX_WORKSPACE_IDCMUX_SOCKET_PATH 不算数,因为可能在 cmux 外设置)。

7.7 切换后端与验证

无论选哪个后端,日常监督接口完全一致:

bash
bin/fm-peek.sh <id>                              # 查看 crewmate 屏幕
FM_HOME=<home> bin/fm-send.sh <id> 'steer text'  # 发送转向指令

验证后端是否生效,方法是 spawn 一个小任务并检查任务元数据中的 backend= 字段及对应后端的专属字段:

text
tmux     → backend=tmux     window=<session>:<window>
herdr    → backend=herdr    (tab/workspace 标识)
zellij   → backend=zellij   zellij_session= zellij_tab_id= zellij_pane_id=
orca     → backend=orca     orca_worktree_id= worktree=
cmux     → backend=cmux     cmux_workspace_id= cmux_surface_id=

每个后端的活跃维护者验证(源码与实测证据,包括套接字模式、焦点行为等)统一记录在 docs/verification/runtime-backends.md。切换后端前的检查单:

text
□ 新后端的 CLI 与 jq 已安装且版本达标
□ 平台匹配(orca/cmux 仅 macOS)
□ 特殊设置已完成(cmux 套接字模式 / Orca app 就绪)
□ config/backend 或 FM_BACKEND 写入正确的后端名
□ spawn 小任务验证 backend= 元数据字段

本章小结

  • 四个实验后端(herdr/zellij/orca/cmux)各有取舍,tmux 始终是行为保证最完整的参考默认;
  • 自动检测只覆盖 herdr 与 cmux,zellij 与 orca 永远必须显式设置,codex-app 还不是 runtime backend;
  • 检测顺序 tmux → herdr → cmux 保证"内层多路复用器胜出",且自动检测永不授予凭据或放行安全设置;
  • 各后端的安全边界要提前配好:cmux 套接字模式与密码、herdr presentation spaces 版本底线、zellij 结构化校验弥补 exit 0 陷阱;
  • 无论后端如何切换,监督接口 fm-peek.sh / fm-send.sh 与元数据路由保持不变。

🧪 随堂测验

点击你认为正确的选项。答错时会展示正确答案与原因解析。

1. 关于四个实验性后端的自动检测规则,正确的是?

2. cmux Password 模式下,适配器读取 config/cmux-socket-password 的行为是?

3. zellij 的 CLI 动作命令对不存在的会话或 pane 也返回 exit 0,FirstMate 适配器如何应对?

4. 一个从未创建过 config/herdr-presentation-spaces 的 home,在 Herdr 0.7.5 上 spawn 新任务会发生什么?

🛠️ 动手实践

  1. 对照 7.2 的表格检查你的环境:你正在用的终端复用器属于哪一类?如果它是 zellij 或 orca,写出让它生效的两种显式设置方式,并解释为什么这两个后端不能依赖自动检测。
  2. 在 macOS 上安装 cmux 并完成 Socket Control Mode 设置(推荐 automation 模式):先故意保持默认的 cmuxOnly 模式尝试 spawn,观察拒绝信息中的设置指引,再改为 automation 模式重试,最后 spawn 小任务验证元数据中出现 cmux_workspace_idcmux_surface_id
  3. 你的 Herdr 是 0.7.x 版本:分别实验"不创建配置文件"、"写入 off"、"写入 on"三种情况下 spawn 新任务的工作区布局差异,观察低于 0.8.0 版本底线时的一次性警告内容,并用 state/.herdr-presentation-floor-<release> 标记文件解释为什么删掉标记后警告会再次出现。