第 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),其余后端各有已记录的能力边界。选择时的优先级建议是:
tmux 参考默认,保证最完整,无特殊理由就选它
herdr 想要原生 busy/idle/blocked 状态信号时选它
zellij 已经在用 zellij 且接受其当前限制时选它
orca macOS 用户想连 worktree 一起交给 Orca 管理时选它
cmux macOS GUI 用户想把任务放进 cmux 侧边栏时选它7.2 选择规则:显式设置与自动检测
所有后端的共享选择语义由 docs/configuration.md 统一拥有。指定后端有三种方式:
# 方式一:本地 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,以及协议版本底线。它的拓扑约定是:
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),让视觉上每任务一栏。但这有一个版本底线:
# 关闭投影(写入本地 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.py 和 bin/backends/herdr-eventwait.py,分别处理工作区移动和事件等待。
7.4 zellij:显式专用的共享会话后端
zellij 是显式专用(explicit-only)后端:永远不会被自动检测到。它使用一个共享会话(默认名 firstmate),每个任务一个 tab:
# 用 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。适配器的对策是不信任退出码,而是在每次操作前后做结构化校验:
操作前:验证会话存在 → 验证 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 把两层合并了:
tmux/herdr/zellij/cmux 任务:Treehouse worktree + 会话端点(两层)
orca 任务: Orca-managed worktree + Orca 终端(一层)因此 fm-spawn.sh 对 orca 任务不调用 Treehouse。spawn 前 firstmate 要求 orca status --json 报告 reachable=true 且 state="ready";项目的第一个任务会自动用 orca repo add --path 注册仓库,无需手工注册。
要点清单:
平台 仅 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 支持 | 安全边界 |
|---|---|---|---|
| Off | off | ❌ | 套接字监听关闭 |
| cmux processes only | cmuxOnly | ❌ | 仅 cmux 应用后代可连 |
| Automation mode | automation | ✅ 推荐 | 属主 0600 套接字,限当前 macOS 用户进程 |
| Password mode | password | ✅ | 0600 套接字 + 认证握手 |
| Full open access | allowAll | ✅ 不推荐 | 0666 世界可写,任何本地用户无需认证即可执行命令 |
Password 模式下密码存放有两种途径:
# 途径一:本地 gitignored 文件的第一行
echo "your-secret" > config/cmux-socket-password
# 途径二:环境变量 CMUX_SOCKET_PASSWORD适配器每次调用都现读密码文件,并且文件不存在时绝不会覆盖你环境中已有的 CMUX_SOCKET_PASSWORD。注意:模式和密码都要通过 cmux UI 配置,不要手改 cmux.json——应用不保留手工添加的密码键。
UUID 与恢复
每个任务拥有一个 workspace + 一个 surface,元数据形如:
backend=cmux
window=<workspace-uuid>:<surface-uuid>
cmux_workspace_id=<workspace-uuid>
cmux_surface_id=<surface-uuid>workspace UUID 在应用重启后会变,所以恢复逻辑按作用域标题搜索再解析当前 surface id,UUID 从不作为恢复权威。运行时检测标记是 CMUX_WORKSPACE_ID(CMUX_SOCKET_PATH 不算数,因为可能在 cmux 外设置)。
7.7 切换后端与验证
无论选哪个后端,日常监督接口完全一致:
bin/fm-peek.sh <id> # 查看 crewmate 屏幕
FM_HOME=<home> bin/fm-send.sh <id> 'steer text' # 发送转向指令验证后端是否生效,方法是 spawn 一个小任务并检查任务元数据中的 backend= 字段及对应后端的专属字段:
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。切换后端前的检查单:
□ 新后端的 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 新任务会发生什么?
🛠️ 动手实践
- 对照 7.2 的表格检查你的环境:你正在用的终端复用器属于哪一类?如果它是 zellij 或 orca,写出让它生效的两种显式设置方式,并解释为什么这两个后端不能依赖自动检测。
- 在 macOS 上安装 cmux 并完成 Socket Control Mode 设置(推荐 automation 模式):先故意保持默认的 cmuxOnly 模式尝试 spawn,观察拒绝信息中的设置指引,再改为 automation 模式重试,最后 spawn 小任务验证元数据中出现
cmux_workspace_id与cmux_surface_id。 - 你的 Herdr 是 0.7.x 版本:分别实验"不创建配置文件"、"写入 off"、"写入 on"三种情况下 spawn 新任务的工作区布局差异,观察低于 0.8.0 版本底线时的一次性警告内容,并用
state/.herdr-presentation-floor-<release>标记文件解释为什么删掉标记后警告会再次出现。