kanban-code-orchestrator:用 Hermes Kanban 构建可恢复的编程工作流

项目地址:lichenrobo/kanban-code-orchestrator
核心入口:SKILL.md
完整协议:workflow-protocol.md

摘要

kanban-code-orchestrator 是一个运行在 Hermes Agent 上的软件开发编排 Skill。它把调用 Skill 的持久 TUI/Gateway chat session 直接作为 Orchestrator,只创建三个执行型 Profile:

  • kanban-code-coder:实现需求并修复缺陷;
  • kanban-code-tester:独立验证并输出 QA 结论;
  • kanban-code-reviewer:在 QA 通过后执行最终工程审查。

工作流不是把三个 Agent 一次性并发启动,也不是依赖自然语言“约定俗成”地交接,而是在 Hermes Kanban 的持久 Task、run、parent link 和 terminal event 之上实现一台显式状态机。Orchestrator 只接受 Worker 最新 completed run 的 metadata.result 作为状态转换输入;网络中断、Worker 崩溃和通知丢失都不会隐式转移工作流所有权。

这套设计要解决的不是“让三个模型互相聊天”,而是四个更具体的工程问题:

  1. 谁拥有创建下一张 Task 的唯一权限;
  2. 如何区分 Task 生命周期结束与业务验收通过;
  3. 如何在会话恢复后重建状态并避免重复建卡;
  4. 如何把业务重试与基础设施重试拆成两个独立熔断域。

Hermes Kanban 是什么

Hermes Kanban 是 Hermes Agent 内置的多 Profile 持久任务板。它不是一次性的 subagent 调用,而是一个由 SQLite 持久化的任务队列与状态系统:Task、run、依赖关系、评论和事件都写入 Kanban 数据层,Worker 则以具有独立 Profile 身份的 OS 进程运行。

Hermes 官方文档将它与 delegate_task 的差别概括为两类计算模型:后者接近同步 RPC,父 Agent 发起调用并等待返回;Kanban 更接近 durable work queue,任务可以跨会话、跨进程、跨人工干预继续存在。

Kanban 的关键组件包括:

组件 技术职责
Board 隔离任务、事件、Workspace 和日志的持久队列;默认 Board 使用 ~/.hermes/kanban.db
Task 带 assignee、body、status、Workspace 和依赖关系的工作单元
Run Worker 对 Task 的一次执行尝试,保存 outcome、summary 和 metadata
Profile 独立的 Hermes Home,拥有自己的模型配置、.envSOUL.md、memory 和 session
Dispatcher 周期性提升、原子认领 Task,并拉起对应 Profile 的 Worker 进程
Terminal event completedblockedgave_upcrashedtimed_out 等终态事件

模型通过 kanban_createkanban_showkanban_complete 等结构化工具访问任务板,而不是在 shell 中执行 hermes kanban ...。CLI 面向人类和脚本;kanban_* toolset 面向正在推理的 Agent。两条入口最终访问同一数据层,但工具调用避免了 shell quoting、远程终端中找不到 Hermes CLI,以及从 stderr 反解析状态等问题。

Profile 与 Workspace 也必须区分。按照 Hermes Profiles 文档,Profile 是 Agent 的配置与状态边界,并不等价于文件系统沙箱;Workspace 才是本次 Task 实际操作的目录。kanban-code-orchestrator 因此不会把某个项目路径永久写入 Worker 的 terminal.cwd,而是在每张 Task 上显式传递绝对 dir:<path> Workspace。

为什么不能只写一句“Coder 完成后交给 Tester”

自然语言角色提示可以约束单个 Agent,却不能自动提供分布式工作流需要的控制语义。例如:

  • Coder 说“完成了”,但没有调用 kanban_complete,Task 仍未形成持久交接;
  • Task 进入 done,只说明某次执行结束,不代表 Tester 返回了 QA_PASS
  • Reviewer 拒绝后直接重新审查,跳过 Tester,会让修复代码绕过回归测试;
  • Orchestrator 断线后,其他 session 若擅自补建 Task,可能产生两个并行状态分支;
  • QA_FAIL 与模型 API 崩溃都表现为“没成功”,但它们需要完全不同的重试预算。

因此,本项目把角色提示、Task 数据契约和 Orchestrator 状态机同时纳入协议。SOUL 负责约束 Worker 能做什么;Task body 负责携带不可丢失的上下文;metadata.result 负责机器可判定的交接;当前 chat session 负责唯一的状态转换。

Skill 的整体工作流

1. 首次运行:配置控制面与三个执行面

第一次调用 /kanban-code-orchestrator 时,Skill 先做只读 readiness 检测。只有发现缺项时才加载 first-run-setup.md,并与用户交互确认:

  • 同名 Worker Profile 是复用、备份后更新,还是重建;
  • 三个 Worker 继承现有模型/API 配置,还是分别手动配置;
  • 当前 Profile 是否具有 Kanban 编排工具;
  • 三个 Worker 是否启用 coding toolset;
  • auto_decompose=falseauto_subscribe_on_create=true 是否成立;
  • 哪个 Gateway 是唯一 Dispatcher owner;
  • 是否允许执行可能产生模型费用的 smoke test。

首次配置只创建三个 Worker Profile,不创建 kanban-code-orchestrator Profile。Worker SOUL 来自仓库中的固定模板;API Key、OAuth token 和 bot token 不进入聊天或仓库,只在本地 setup、环境变量或 Profile .env 中配置。

2. 正常运行:当前 session 成为唯一 Orchestrator

后续调用若 readiness 已满足,当前 TUI/Gateway chat session 直接成为 workflow 的 initiator、owner 与 Orchestrator:

Current persistent chat session
          (Orchestrator)
                 │
                 ▼
       kanban-code-coder
                 │ IMPLEMENTATION_COMPLETE
                 ▼
       kanban-code-tester
          ┌──────┴──────┐
       QA_FAIL        QA_PASS
          │              │
          ▼              ▼
     Coder Fix   kanban-code-reviewer
          │         ┌────┴─────────┐
          └─Tester  │              │
              REVIEW_REJECT   REVIEW_APPROVE
                    │              │
                    ▼              ▼
             Coder Fix → Tester  WORKFLOW_DONE

启动新 workflow 前必须确定:稳定的 workflow_id、绝对 Workspace、完整需求、acceptance criteria,以及业务重试和 Worker 基础设施重试上限。

3. 状态机

定义阶段集合:

S = {CODER, TESTER, REVIEWER, DONE, ESCALATION, ERROR}

Worker 允许输出的业务结果集合:

R_coder    = {IMPLEMENTATION_COMPLETE}
R_tester   = {QA_PASS, QA_FAIL}
R_reviewer = {REVIEW_APPROVE, REVIEW_REJECT}

状态转换函数可以写成:

δ(CODER, IMPLEMENTATION_COMPLETE) = create(TESTER)
δ(TESTER, QA_PASS)                 = create(REVIEWER)
δ(TESTER, QA_FAIL)                 = tester_retry++ ; create(CODER_FIX)
δ(REVIEWER, REVIEW_REJECT)         = reviewer_retry++ ; create(CODER_FIX)
δ(REVIEWER, REVIEW_APPROVE)        = DONE
δ(any, unknown_or_missing_result)  = ERROR

其中两个回路存在严格约束:

QA_FAIL       → Coder Fix → Tester
REVIEW_REJECT → Coder Fix → Tester → Reviewer

任何 Coder 修改都必须重新经过 Tester。Reviewer 拒绝后的修复不能直接回到 Reviewer,这是协议最重要的回归测试不变量之一。

工作流实现原理

原理一:Chat session 是控制平面,不再创建 Orchestrator Worker

早期常见设计会创建一个独立 Orchestrator Profile,再由它的无头 Worker 派发任务。问题在于,无头进程未必携带正确的平台 session identity;如果它错误继承其他进程的 session key,自动订阅可能绑定到错误会话。Orchestrator 一旦断线,外层 chat 又可能误以为自己需要“接管”,最终形成双写控制面。

本项目直接让发起 Skill 的持久 TUI/Gateway chat 成为 Orchestrator。这样,创建者身份、事件订阅目标和用户可见会话天然重合:

workflow_owner_kind: chat_session
workflow_owner_profile: <current profile>
workflow_owner_session_id: <current session identity when available>

它只编排,不直接编码、测试或审查。Dispatcher 只负责拉起 Worker,也不拥有业务分支决策权。

原理二:唯一写入者与显式所有权

每条 workflow 只有 owner session 可以执行以下 mutation:

  • 创建 Coder、Tester、Reviewer 和 Fix Task;
  • 增加业务重试计数器;
  • 选择下一状态;
  • 建立 parent chain。

Worker 只能 complete 或 block 自己当前的 Task,不能创建下一阶段。网络中断、Gateway 重启、通知失败、Worker crashedtimed_out 都不是 ownership transfer event。owner 不可用时系统 fail closed,进入 WORKFLOW_ORCHESTRATOR_UNAVAILABLE,而不是让任意 Profile 猜测并补写任务图。

新的 session 只有在用户明确授权后才能接管,并且必须先读取完整 Task、run、parent 与 event 链,找到最后一个合法 completed run,再检查下一阶段 Task 是否已经存在。

原理三:通过 kanban_create 建立 creator-session subscription

Owner chat 必须直接调用 kanban_create,不能退化为 shell、subprocess 或 hermes kanban create。这不仅是接口风格问题,还关系到订阅来源:auto_subscribe_on_create=true 会把 terminal event 绑定到发起创建的持久 session。

创建后必须保存并验证:

task_id = <new task id>
subscribed = true
subscription target = owner session
delivery_mode = notify | notify+wake

Hermes 官方订阅语义 中,notify 只投递被动消息,notify+wake 还会触发目标 Agent 的新推理轮次。因此本 Skill 接受两者,但不会把 notify 宣称为完全无人值守:若只有被动通知,用户可能需要在同一 session 中执行:

/kanban-code-orchestrator continue <workflow_id>

工作流不使用 sleep 或轮询保活。事件到达后才读取 Task;用户主动查询状态时也只执行只读检查。

原理四:Terminal event 只是中断信号,metadata.result 才是业务输入

completed event 只意味着 Worker run 到达终态,可能只携带截断摘要。Orchestrator 收到事件后的读取路径固定为:

event.task_id
  → kanban_show(task_id)
  → latest run where outcome=completed
  → run.summary + run.metadata
  → branch on run.metadata.result only

它不会依据 Task 标题、顶层 status=done、通知文本、summary 首行或顶层 result 推进。缺少或出现未知 metadata.result 时进入 WORKFLOW_PROTOCOL_ERROR

这是典型的控制面/数据面分离:terminal event 类似中断,只负责通知“有状态变化”;completed run metadata 才是状态机读取并验证的持久数据。

Worker 的结构化交接示例:

{
  "result": "QA_FAIL",
  "tests_executed": ["pytest tests/test_auth.py"],
  "tests_passed": 17,
  "tests_failed": 1,
  "failing_tests": ["test_expired_refresh_token"],
  "reproduction_steps": ["issue an expired refresh token", "POST /token/refresh"],
  "expected_result": "401 with token_expired",
  "actual_result": "500 Internal Server Error",
  "suspected_location": ["src/auth/refresh.py"],
  "severity": "high"
}

代码缺陷是正常业务结果,所以 QA_FAILREVIEW_REJECT 仍通过 kanban_complete 形成成功交接;只有缺少权限、外部服务不可达、测试环境缺失或需要用户决策等真正外部阻塞才调用 kanban_block

原理五:Task body 是可恢复日志,不依赖模型短期记忆

每张 Task 都重复携带恢复所需的最小完整状态:

workflow_id: login-api-001
workflow_owner_kind: chat_session
workflow_owner_profile: default
workflow_owner_session_id: <session id>
workspace: dir:/workspace/myproject

tester_retry: 0
tester_retry_limit: 5
reviewer_retry: 0
reviewer_retry_limit: 3

同时保留原始需求和 acceptance criteria,并尽可能把直接上游 completed Task 设为 parent。parent chain 提供因果关系,但不是唯一状态源;关键字段仍显式写入每张 Task,避免 parent 数据缺失或上下文压缩导致不可恢复。

恢复算法是确定性的:

  1. 枚举 workflow 的 Task、run、parent 和 terminal event;
  2. 找到最后一个协议合法的 completed run;
  3. 从 Task body 恢复 Workspace 与计数器;
  4. 根据 metadata.result 计算应有的下一阶段;
  5. 检查该 Task 是否已经存在;
  6. 只有不存在时才创建。

第 5 步是恢复路径上的幂等性闸门,可避免 session 在“Task 已创建、回复尚未送达”之间断线后重复建卡。

原理六:业务重试与基础设施重试分离

默认业务预算:

tester_retry_limit = 5
reviewer_retry_limit = 3

tester_retry 只在 QA_FAIL 时增加;reviewer_retry 只在 REVIEW_REJECT 时增加。计数器在后续通过时也不清零,因为它们描述的是整条 workflow 已消耗的业务迭代预算。

Worker 的 max_retries=3 则用于 spawn failure、模型 API 异常、crash 或 protocol failure 等基础设施问题。把两者分开可以避免两个错误:代码持续不满足验收却被基础设施重试无限掩盖,或者一次临时 API 故障错误消耗 QA 修复额度。

关键不变量

这套工作流的正确性依赖以下不变量,而不是依赖 Agent “大致理解流程”:

  1. 当前持久 chat 是唯一 Orchestrator,不存在 Orchestrator Profile 或 Task;
  2. 所有 Worker/Fix Task 只能由 owner chat 直接调用 kanban_create 创建;
  3. Worker 只能结束或阻塞自己的当前 Task;
  4. done 不等于业务通过,只读取最新 completed run 的 metadata.result
  5. QA_FAIL 必须返回 Coder;
  6. 所有 Coder 修改后必须重新 Tester;
  7. REVIEW_REJECT 后固定执行 Coder → Tester → Reviewer;
  8. 只有 REVIEW_APPROVE 可以产生 WORKFLOW_DONE
  9. 每张 Task 显式携带绝对 Workspace,不永久修改 Worker terminal.cwd
  10. ownership 不因断网、通知失败或 Worker 故障自动转移。

安装与调用

直接从 GitHub 安装:

hermes skills install lichenrobo/kanban-code-orchestrator/skills/kanban-code-orchestrator

或者将仓库添加为 Skill Tap:

hermes skills tap add lichenrobo/kanban-code-orchestrator
hermes skills install lichenrobo/kanban-code-orchestrator/kanban-code-orchestrator

必须在持续运行的 TUI 或 Gateway chat 中调用:

/kanban-code-orchestrator 在 C:\projects\example 中实现登录 API,验收条件是……

不要从 chat -q、cron、一次性 CLI 或 Dispatcher worker 启动。那些无头上下文无法可靠提供 owner chat 所需的持续 session identity 和通知路由。

边界与结论

kanban-code-orchestrator 不是通用 CI/CD 替代品,也不提供容器级安全隔离。它解决的是 Agent 编码工作流中的控制一致性:用持久 Task 保存交接,用结构化结果驱动状态机,用唯一 owner 防止多写者竞争,用 terminal event 代替轮询,并在失败恢复时从 Task 图而不是聊天记忆重建状态。

其核心判断可以浓缩成一句话:LLM 可以负责实现和判断,但工作流推进必须由持久协议约束,而不能依赖自然语言暗示。

参考资料