看板工作车道(Worker Lanes)
工作车道(Worker Lane) 是看板调度器(dispatcher)可将任务路由到的一类进程。每条车道拥有一个标识(assignee 字符串)、一种生成机制(spawn mechanism),以及一份关于任务生成后必须执行什么工作的约定(contract)。
本页面描述的就是这份约定。它的目标读者有两类:
- 运维人员(Operators):负责选择将哪些车道接入看板(需要创建哪些配置文件、使用哪些 assignee)。
- 插件/集成开发者:希望新增一种车道形态(例如封装 Codex / Claude Code / OpenCode 的 CLI 工作器、容器化的评审工作器、通过 API 拉取任务的非 Hermes 外部服务)。
如果你要编写的是运行在车道内部的代理(agent)代码,请参考 kanban-worker 技能文档,其中有更详细的流程说明。
层级关系
Hermes 看板 = 标准任务生命周期 + 审计追踪
工作车道 = 单张已分配卡片的执行器
评审者 = 把关「完成」状态的人类或人类代理
GitHub PR = 可上游合并的产物(可选,仅代码车道涉及)
Hermes 看板拥有生命周期的唯一事实来源 —— ready → running → blocked / done / archived。工作车道负责执行工作,但绝不拥有该事实来源;它们产生的一切都通过 kanban_* 工具(或非 Hermes 外部工作器通过 API)回流至看板内核。评审者把关从「代码变更已写入」到「任务完成」的状态转换。
车道需要提供什么
要成为一个看板工作车道,一个集成必须提供三样东西:
1. 一个 assignee 字符串
调度器将 task.assignee 与 Hermes 配置文件名(默认车道形态)或已注册的非生成型标识符(插件车道形态 —— 见下方「添加外部 CLI 工作车道」)进行匹配。无法解析 assignee 的任务将停留在 ready 状态,并附带一条 skipped_nonspawnable 事件,以便看板运维人员修复;它们不会被静默丢弃或由任意回退车道执行。
2. 一种生成机制(Spawn Mechanism)
对于 Hermes 配置文件车道,调度器的 _default_spawn 在任务绑定的工作区内运行 hermes -p <assignee> chat -q <prompt>(若 $PATH 中不存在 hermes 入口脚本,则使用等效的模块调用形式),并传入以下环境变量:
| 变量 | 携带内容 |
|---|---|
HERMES_KANBAN_TASK | 工作器正在处理的任务 ID |
HERMES_KANBAN_DB | 每块看板对应的 SQLite 文件的绝对路径 |
HERMES_KANBAN_BOARD | 看板 slug |
HERMES_KANBAN_WORKSPACES_ROOT | 看板工作区目录树的 root |
HERMES_KANBAN_WORKSPACE | 本 任务工作区的绝对路径 |
HERMES_KANBAN_RUN_ID | 当前运行的 ID(用于生命周期门控) |
HERMES_KANBAN_CLAIM_LOCK | 声明锁字符串(<host>:<pid>:<uuid>) |
HERMES_PROFILE | 工作器自身的配置文件名(用于 kanban_comment 的作者归属) |
HERMES_TENANT | 租户命名空间(如果任务有设置的话) |
对于非 Hermes 车道(通过插件注册),插件需提供自己的 spawn_fn 可调用对象,该函数接收 task、workspace 和 board,并返回一个可选的 PID 用于崩溃检测。
3. 一个生命周期终止器(Lifecycle Terminator)
每次声明(claim)必须恰好以以下结果之一结束:
kanban_complete(summary=..., metadata=...)— 任务成功,状态翻转为done。kanban_block(reason=...)— 任务等待人工输入,状态翻转为blocked。当kanban_unblock被调用时,调度器会重新生成(respawn)工作器。- 工作器进程退出时未调用任何工具。内核会回收它并发出
crashed(PID 已死)、gave_up(连续失败断路器触发)或timed_out(超过 max_runtime)。这是失败路径;健康的工作器不会在此结束。
看板内核强制规定每次运行必须恰好以上述结果之一终止。一个既未调用 kanban_complete 也未调用 kanban_block 就正常退出的工作器,会被视为崩溃(crashed)。
输出与「需要评审」约定
对于大多数涉及代码变更的任务,工作完成的时刻并不是工作器结束的那一刻 —— 它需要一位人类评审者。「需要评审(review-required)」的区分由看板内核强制执行(「代码变更任务」的边界很模糊,如果强制要求每个代码工作器都改为调用 block 而不是 complete,会破坏那些不需要评审的流程)。这是一层建立在之上的约定:
- 调用
block而非complete,并在reason前加上review-required:前缀,以便仪表盘 /hermes kanban show将该行标记为「等待评审」。 - 先将结构化元数据写入一条
kanban_comment,因为kanban_block只携带人类可读的reason。评论是持久的注解通道 —— 每个与审计相关的字段(changed_files、tests_run、diff_path 或 PR URL、decisions)都应放在那里。 - 评审者要么批准并解除阻塞(unblock),这会带着评论线程重新生成工作器以进行跟进;要么通过另一条评论要求修改,下一次工作器运行会将这条评论视为
kanban_show上下文的一部分。
kanban-worker 技能中包含 kanban_complete(真正终端型的任务 —— 错别字修复、文档变更、研究综述)和 review-required 阻塞模式的具体示例。
日志与审计追踪
调度器将每个任务的 worker stdout/stderr 写入 <board-root>/logs/<task_id>.log。日志可通过看板元数据审计:
task_runs表行携带log_path、退出码(如可用)、summary 和 metadata。task_events表行携带每一次状态转换(promoted、claimed、heartbeat、completed、blocked、gave_up、crashed、timed_out、reclaimed、claim_extended)。kanban_show会返回以上两者,因此评审者(或后续工作器)读取任务时,无需访问仪表盘即可获得完整历史。
仪表盘会渲染运行历史,包含摘要、元数据块和退出状态徽章。CLI 用户可以运行 hermes kanban tail <task_id> 实时跟踪,或运行 hermes kanban runs <task_id> 查看历史尝试列表。
现有的车道形态
Hermes 配置文件车道(默认)
这是当前所有看板工作器采用的形态:assignee 是一个配置文件名,调度器生成 hermes -p <profile>,工作器自动加载 kanban-worker 技能和 KANBAN_GUIDANCE 系统提示块,并使用 kanban_* 工具终止运行。除了定义配置文件外,无需其他设置。
为你的工作器集群(fleet)创建配置文件时,请选择与你希望编排器(orchestrator)路由到的角色相匹配的名称。编排器(当存在时)通过 hermes profile list 发现你的配置文件名 —— 系统不假定任何固定的名册(参见 kanban-orchestrator 技能了解合约的编排器侧)。
编排器配置文件车道
配置文件车道的一种特化:编排器是一个 Hermes 配置文件,其工具集包含 kanban 但排除 terminal / file / code / web 等实现类工具。它的工作是通过 kanban_create + kanban_link 将高层级目标分解为子任务,然后退出。编排器技能中编码了「反诱惑(anti-temptation)」规则。
添加外部 CLI 工作车道
将非 Hermes 的 CLI 工具(Codex CLI、Claude Code CLI、OpenCode CLI、本地编码模型运行器等)接驳为看板工作车道,目前还不是一条铺设好的路径。调度器的生成函数是可插拔的(spawn_fn 是 dispatch_once 的一个参数),插件可以为非 Hermes 的 assignee 注册自己的 spawn_fn,但围绕它的集成工作 —— 将 CLI 的退出码包装为 kanban_complete / kanban_block 调用、将 CLI 的工作区/沙箱约定映射到调度器的 HERMES_KANBAN_WORKSPACE 环境变量、处理认证和每个 CLI 的策略 —— 仍然是每个集成各自的设计工作。
如果你正在考虑添加一条 CLI 车道,请开一个 issue 描述具体的 CLI 和你想要启用的流程。上述约定是任何此类车道都必须满足的约束;实现形态(每个 CLI 一个插件 vs 一个由配置参数化的通用 CLI 运行器插件)尚待确定。
相关的历史 issue 是 #19931,以及已关闭但未合并的 Codex 专用 PR #19924 —— 这些描述了最初的架构提案,但并未落地一个运行器。
调度器处理的失败模式
车道开发者无需自行重新实现以下内容:
- 陈旧声明 TTL —— 一个声明了任务但从不发送心跳 / 完成 / 阻塞的工作器,会在
DEFAULT_CLAIM_TTL_SECONDS(默认 15 分钟)后被回收 —— 但前提是工作器进程确实已经死亡。一个活跃的工作器(慢速模型在一次无工具调用的 LLM 调用中花费 20+ 分钟)会导致声明被延长而非终止;只有已死的 PID 才会被回收。 - 崩溃的工作器 —— 宿主本地 PID 已消失的工作器由
detect_crashed_workers检测并回收;任务递增consecutive_failures,断路器触发时可能自动阻塞。 - 运行级别重试 —— 当任务被重试时(阻塞后、崩溃后、回收后),工作器可以使用终止工具上的
expected_run_id参数来快速失败(fail fast),如果它自己的运行已被取代的话。 - 单任务最大运行时间 ——
task.max_runtime_seconds硬性限制每次运行的实际经过时间(wall-clock time),不论 PID 是否存活。这能捕获那些「真正死锁」的工作器 —— 否则存活 PID 延期机制会让它们一直运行下去。 - 滞留任务检测 —— 一个 ready 任务在其 assignee 在
kanban.stranded_threshold_seconds(默认 30 分钟)内从未产生声明时,hermes kanban diagnostics会将其显示为stranded_in_ready警告。严重性在阈值的 2 倍时升级为 error,6 倍时升级为 critical。这能用一个信号同时捕获打错的 assignee、已删除的配置文件和宕机的外部工作器池 —— 与身份无关,无需维护每个看板的允许列表。
相关文档
- 看板概览 — 面向用户的介绍。
- 看板教程 — 配合仪表盘操作的逐步指南。
kanban-worker— 工作器进程加载的技能。kanban-orchestrator— 编排器侧。