docs: align kanban readiness docs and smoke tests

Salvages #28199 by @bensargotest-sys. Aligns Kanban docs with current
tool registration: dispatcher-spawned task workers get task tools,
profiles that explicitly enable the kanban toolset get orchestrator
routing tools (kanban_list, kanban_unblock). Corrects failure-limit
text to current default of 2. Hardens the e2e subprocess script to
resolve repo root and use the spawnable default assignee. Updates the
diagnostics severity fixture to assert error below the critical
threshold.
This commit is contained in:
bensargotest-sys
2026-05-18 21:06:57 -07:00
committed by Teknium
parent d37574775b
commit 81584940fe
11 changed files with 56 additions and 37 deletions
+8 -6
View File
@@ -830,10 +830,11 @@ kanban task.
`unlink`, `comment`, `complete`, `block`, `unblock`, `archive`, `unlink`, `comment`, `complete`, `block`, `unblock`, `archive`,
`tail`, plus less-commonly-used `watch`, `stats`, `runs`, `log`, `tail`, plus less-commonly-used `watch`, `stats`, `runs`, `log`,
`assignees`, `heartbeat`, `notify-*`, `dispatch`, `daemon`, `gc`. `assignees`, `heartbeat`, `notify-*`, `dispatch`, `daemon`, `gc`.
- **Worker toolset:** `tools/kanban_tools.py` exposes `kanban_show`, - **Worker/orchestrator toolset:** `tools/kanban_tools.py` exposes
`kanban_complete`, `kanban_block`, `kanban_heartbeat`, `kanban_comment`, `kanban_show`, `kanban_complete`, `kanban_block`, `kanban_heartbeat`,
`kanban_create`, `kanban_link` — gated by `HERMES_KANBAN_TASK` so `kanban_comment`, `kanban_create`, `kanban_link`; profiles that
the schema only appears for processes actually running as a worker. explicitly enable the `kanban` toolset outside a dispatcher-spawned
task also get `kanban_list` and `kanban_unblock` for board routing.
- **Dispatcher:** long-lived loop that (default every 60s) reclaims - **Dispatcher:** long-lived loop that (default every 60s) reclaims
stale claims, promotes ready tasks, atomically claims, and spawns stale claims, promotes ready tasks, atomically claims, and spawns
assigned profiles. Runs **inside the gateway** by default via assigned profiles. Runs **inside the gateway** by default via
@@ -849,8 +850,9 @@ Isolation model:
- **Tenant** is a soft namespace *within* a board — one specialist - **Tenant** is a soft namespace *within* a board — one specialist
fleet can serve multiple businesses with workspace-path + memory-key fleet can serve multiple businesses with workspace-path + memory-key
isolation. isolation.
- After ~5 consecutive spawn failures on the same task the dispatcher - After `kanban.failure_limit` consecutive non-success attempts on the
auto-blocks it to prevent spin loops. same task (default: 2), the dispatcher auto-blocks it to prevent spin
loops.
Full user-facing docs: `website/docs/user-guide/features/kanban.md`. Full user-facing docs: `website/docs/user-guide/features/kanban.md`.
@@ -680,15 +680,19 @@ User docs: https://hermes-agent.nousresearch.com/docs/user-guide/features/curato
Durable SQLite board for multi-profile / multi-worker collaboration. Durable SQLite board for multi-profile / multi-worker collaboration.
Users drive it via `hermes kanban <verb>`; dispatcher-spawned workers Users drive it via `hermes kanban <verb>`; dispatcher-spawned workers
see a focused `kanban_*` toolset gated by `HERMES_KANBAN_TASK` so the see a focused `kanban_*` toolset gated by `HERMES_KANBAN_TASK`, and
schema footprint is zero outside worker processes. orchestrator profiles can opt into the broader `kanban` toolset. Normal
sessions still have zero `kanban_*` schema footprint unless configured.
- **CLI verbs (common):** `init`, `create`, `list` (alias `ls`), - **CLI verbs (common):** `init`, `create`, `list` (alias `ls`),
`show`, `assign`, `link`, `unlink`, `comment`, `complete`, `block`, `show`, `assign`, `link`, `unlink`, `comment`, `complete`, `block`,
`unblock`, `archive`, `tail`. Less common: `watch`, `stats`, `runs`, `unblock`, `archive`, `tail`. Less common: `watch`, `stats`, `runs`,
`log`, `dispatch`, `daemon`, `gc`. `log`, `dispatch`, `daemon`, `gc`.
- **Worker toolset:** `kanban_show`, `kanban_complete`, `kanban_block`, - **Worker/orchestrator toolset:** `kanban_show`, `kanban_complete`,
`kanban_heartbeat`, `kanban_comment`, `kanban_create`, `kanban_link`. `kanban_block`, `kanban_heartbeat`, `kanban_comment`, `kanban_create`,
`kanban_link`; profiles that explicitly enable the `kanban` toolset
outside a dispatcher-spawned task also get `kanban_list` and
`kanban_unblock` for board routing.
- **Dispatcher** runs inside the gateway by default - **Dispatcher** runs inside the gateway by default
(`kanban.dispatch_in_gateway: true`) — reclaims stale claims, (`kanban.dispatch_in_gateway: true`) — reclaims stale claims,
promotes ready tasks, atomically claims, spawns assigned profiles. promotes ready tasks, atomically claims, spawns assigned profiles.
@@ -1898,10 +1898,11 @@ def test_diagnostics_endpoint_severity_filter(client):
# requires ``t_[a-f0-9]{8,}``. # requires ``t_[a-f0-9]{8,}``.
p1 = kb.create_task(conn, title="prose", assignee="a") p1 = kb.create_task(conn, title="prose", assignee="a")
kb.complete_task(conn, p1, summary="mentioned t_deadbeef1234") kb.complete_task(conn, p1, summary="mentioned t_deadbeef1234")
# An error-severity diagnostic (spawn failures) on another # An error-severity diagnostic (spawn failures) on another.
# Keep this below critical severity (failure_threshold * 2).
p2 = kb.create_task(conn, title="spawn", assignee="b") p2 = kb.create_task(conn, title="spawn", assignee="b")
conn.execute( conn.execute(
"UPDATE tasks SET consecutive_failures=5, last_failure_error='x' WHERE id=?", "UPDATE tasks SET consecutive_failures=2, last_failure_error='x' WHERE id=?",
(p2,), (p2,),
) )
conn.commit() conn.commit()
+3 -2
View File
@@ -12,6 +12,7 @@ This validates the IPC + lifecycle story that mocks can't:
import json import json
import os import os
from pathlib import Path
import subprocess import subprocess
import sys import sys
import tempfile import tempfile
@@ -81,7 +82,7 @@ exec {PY} -m hermes_cli.main "$@"
tids = [] tids = []
for i in range(3): for i in range(3):
tid = kb.create_task( tid = kb.create_task(
conn, title=f"real-e2e-{i}", assignee="worker", conn, title=f"real-e2e-{i}", assignee="default",
) )
tids.append(tid) tids.append(tid)
@@ -145,7 +146,7 @@ exec {PY} -m hermes_cli.main "$@"
print("=" * 60) print("=" * 60)
crash_tid = kb.create_task( crash_tid = kb.create_task(
conn, title="crash-e2e", assignee="worker", conn, title="crash-e2e", assignee="default",
) )
# Spawn a worker that sleeps long enough for us to kill it. # Spawn a worker that sleeps long enough for us to kill it.
+8 -5
View File
@@ -1,8 +1,10 @@
"""Kanban tools — structured tool-call surface for worker + orchestrator agents. """Kanban tools — structured tool-call surface for worker + orchestrator agents.
These tools are only registered into the model's schema when the agent is These tools are registered into the model's schema when the agent is
running under the dispatcher (env var ``HERMES_KANBAN_TASK`` set). A running under the dispatcher (env var ``HERMES_KANBAN_TASK`` set) or when
normal ``hermes chat`` session sees **zero** kanban tools in its schema. the active profile explicitly enables the ``kanban`` toolset for
orchestrator work. A normal ``hermes chat`` session still sees **zero**
kanban tools in its schema unless configured.
Why tools instead of just shelling out to ``hermes kanban``? Why tools instead of just shelling out to ``hermes kanban``?
@@ -20,8 +22,9 @@ Why tools instead of just shelling out to ``hermes kanban``?
Humans continue to use the CLI (``hermes kanban …``), the dashboard Humans continue to use the CLI (``hermes kanban …``), the dashboard
(``hermes dashboard``), and the slash command (``/kanban …``) — all (``hermes dashboard``), and the slash command (``/kanban …``) — all
three bypass the agent entirely. The tools are ONLY for the worker three bypass the agent entirely. The tools are for dispatcher-spawned
agent's handoff back to the kernel. worker handoffs and for configured orchestrator profiles that route work
through the board.
""" """
from __future__ import annotations from __future__ import annotations
+1 -1
View File
@@ -384,7 +384,7 @@ Multi-profile, multi-project collaboration board. Each install can host many boa
|------|---------| |------|---------|
| `--board <slug>` | Operate on a specific board. Defaults to the current board (set via `hermes kanban boards switch`, the `HERMES_KANBAN_BOARD` env var, or `default`). | | `--board <slug>` | Operate on a specific board. Defaults to the current board (set via `hermes kanban boards switch`, the `HERMES_KANBAN_BOARD` env var, or `default`). |
**This is the human / scripting surface.** Agent workers spawned by the dispatcher drive the board through a dedicated `kanban_*` [toolset](/docs/user-guide/features/kanban#how-workers-interact-with-the-board) (`kanban_show`, `kanban_complete`, `kanban_block`, `kanban_create`, `kanban_link`, `kanban_comment`, `kanban_heartbeat`) instead of shelling to `hermes kanban`. Workers have `HERMES_KANBAN_BOARD` pinned in their env so they physically cannot see other boards. **This is the human / scripting surface.** Agent workers spawned by the dispatcher drive the board through a dedicated `kanban_*` [toolset](/docs/user-guide/features/kanban#how-workers-interact-with-the-board) (`kanban_show`, `kanban_complete`, `kanban_block`, `kanban_create`, `kanban_link`, `kanban_comment`, `kanban_heartbeat`; orchestrator profiles also get `kanban_list` and `kanban_unblock`) instead of shelling to `hermes kanban`. Workers have `HERMES_KANBAN_BOARD` pinned in their env so they physically cannot see other boards.
| Action | Purpose | | Action | Purpose |
|--------|---------| |--------|---------|
+10 -8
View File
@@ -118,17 +118,19 @@ Scoped to the Feishu document-comment handler. Drives comment read/write operati
## `kanban` toolset ## `kanban` toolset
Registered only when the agent is spawned by the kanban dispatcher (`HERMES_KANBAN_TASK` env set). Lets workers mark tasks done with structured handoffs, block for human input, heartbeat during long ops, comment on threads, and (for orchestrators) fan out into child tasks. See [Kanban Multi-Agent](/docs/user-guide/features/kanban) for the full workflow. Registered when the agent is either (a) spawned by the kanban dispatcher (`HERMES_KANBAN_TASK` env set) or (b) running in a profile that explicitly enables the `kanban` toolset. Task-scoped workers use lifecycle tools for their assigned task; orchestrator profiles additionally get board-routing tools like `kanban_list` and `kanban_unblock`. See [Kanban Multi-Agent](/docs/user-guide/features/kanban) for the full workflow.
| Tool | Description | Requires environment | | Tool | Description | Requires environment |
|------|-------------|----------------------| |------|-------------|----------------------|
| `kanban_show` | Show the active kanban task assigned to this worker (title, description, comments, dependencies). | `HERMES_KANBAN_TASK` | | `kanban_show` | Show the active kanban task assigned to this worker (title, description, comments, dependencies). | `HERMES_KANBAN_TASK` or `kanban` toolset |
| `kanban_complete` | Mark the current task done with a structured handoff payload (results, artifacts, follow-ups). | `HERMES_KANBAN_TASK` | | `kanban_list` | List board tasks with filters. Orchestrator-only; hidden from dispatcher-spawned task workers. | profile with `kanban` toolset |
| `kanban_block` | Block the current task on a question for the user — the dispatcher pauses, surfaces the question, and resumes once a human replies. | `HERMES_KANBAN_TASK` | | `kanban_complete` | Mark the current task done with a structured handoff payload (results, artifacts, follow-ups). | `HERMES_KANBAN_TASK` or `kanban` toolset |
| `kanban_heartbeat` | Send a progress heartbeat during a long-running operation so the dispatcher knows the worker is still alive. | `HERMES_KANBAN_TASK` | | `kanban_block` | Block the current task on a question for the user — the dispatcher pauses, surfaces the question, and resumes once a human replies. | `HERMES_KANBAN_TASK` or `kanban` toolset |
| `kanban_comment` | Add a comment to the task thread without changing its state — useful for surfacing intermediate findings. | `HERMES_KANBAN_TASK` | | `kanban_heartbeat` | Send a progress heartbeat during a long-running operation so the dispatcher knows the worker is still alive. | `HERMES_KANBAN_TASK` or `kanban` toolset |
| `kanban_create` | (Orchestrator only) Fan out child tasks from the current task. | `HERMES_KANBAN_TASK` + orchestrator role | | `kanban_comment` | Add a comment to the task thread without changing its state — useful for surfacing intermediate findings. | `HERMES_KANBAN_TASK` or `kanban` toolset |
| `kanban_link` | (Orchestrator only) Link related tasks together (blocks/blocked-by/related). | `HERMES_KANBAN_TASK` + orchestrator role | | `kanban_create` | Fan out child tasks from the current task. Used by orchestrators and follow-up-spawning workers. | `HERMES_KANBAN_TASK` or `kanban` toolset |
| `kanban_link` | Link tasks with a parent → child dependency edge. | `HERMES_KANBAN_TASK` or `kanban` toolset |
| `kanban_unblock` | Return a blocked task to `ready`. Orchestrator-only; hidden from dispatcher-spawned task workers. | profile with `kanban` toolset |
## `memory` toolset ## `memory` toolset
+1 -1
View File
@@ -67,7 +67,7 @@ Or in-session:
| `computer_use` | `computer_use` | Background macOS desktop control via cua-driver — does not steal cursor/focus. Works with any tool-capable model. macOS only; requires `cua-driver` on `$PATH`. | | `computer_use` | `computer_use` | Background macOS desktop control via cua-driver — does not steal cursor/focus. Works with any tool-capable model. macOS only; requires `cua-driver` on `$PATH`. |
| `image_gen` | `image_generate` | Text-to-image generation via FAL.ai (with opt-in OpenAI / xAI backends). | | `image_gen` | `image_generate` | Text-to-image generation via FAL.ai (with opt-in OpenAI / xAI backends). |
| `video_gen` | `video_generate` | Text-to-video and image-to-video via plugin-registered backends (xAI Grok-Imagine, FAL.ai Veo 3.1 / Pixverse v6 / Kling O3). Pass `image_url` to animate an image; omit it for text-to-video. | | `video_gen` | `video_generate` | Text-to-video and image-to-video via plugin-registered backends (xAI Grok-Imagine, FAL.ai Veo 3.1 / Pixverse v6 / Kling O3). Pass `image_url` to animate an image; omit it for text-to-video. |
| `kanban` | `kanban_block`, `kanban_comment`, `kanban_complete`, `kanban_create`, `kanban_heartbeat`, `kanban_link`, `kanban_show` | Multi-agent coordination tools — only registered when the agent is spawned by the kanban dispatcher (`HERMES_KANBAN_TASK` env set). Lets workers mark tasks done with structured handoffs, block for human input, heartbeat during long ops, comment on threads, and (for orchestrators) fan out into child tasks. | | `kanban` | `kanban_block`, `kanban_comment`, `kanban_complete`, `kanban_create`, `kanban_heartbeat`, `kanban_link`, `kanban_list`, `kanban_show`, `kanban_unblock` | Multi-agent coordination tools. Registered for dispatcher-spawned task workers (`HERMES_KANBAN_TASK`) and for profiles that explicitly enable the `kanban` toolset. Workers mark tasks done, block, heartbeat, comment, and create/link follow-up tasks; orchestrator profiles additionally get board-routing tools like list/unblock. |
| `memory` | `memory` | Persistent cross-session memory management. | | `memory` | `memory` | Persistent cross-session memory management. |
| `messaging` | `send_message` | Send messages to other platforms (Telegram, Discord, etc.) from within a session. | | `messaging` | `send_message` | Send messages to other platforms (Telegram, Discord, etc.) from within a session. |
| `moa` | `mixture_of_agents` | Multi-model consensus via Mixture of Agents. | | `moa` | `mixture_of_agents` | Multi-model consensus via Mixture of Agents. |
@@ -236,10 +236,11 @@ A deploy task that can't spawn its worker because `AWS_ACCESS_KEY_ID` isn't set
```bash ```bash
hermes kanban create "Deploy to staging (missing creds)" \ hermes kanban create "Deploy to staging (missing creds)" \
--assignee deploy-bot --tenant ops --assignee deploy-bot --tenant ops \
--max-retries 3
``` ```
The dispatcher tries to spawn the worker. Spawn fails (`RuntimeError: AWS_ACCESS_KEY_ID not set`). The dispatcher releases the claim, increments a failure counter, and tries again next tick. After three consecutive failures (the default `failure_limit`), the circuit trips: the task goes to `blocked` with outcome `gave_up`. No more retries until a human unblocks it. The dispatcher tries to spawn the worker. Spawn fails (`RuntimeError: AWS_ACCESS_KEY_ID not set`). The dispatcher releases the claim, increments a failure counter, and tries again next tick. Because this example sets `--max-retries 3`, the circuit trips after three consecutive failures: the task goes to `blocked` with outcome `gave_up`. If you omit the flag, Hermes uses `kanban.failure_limit` (default: 2). No more retries until a human unblocks it.
Click the blocked task: Click the blocked task:
+1 -1
View File
@@ -290,7 +290,7 @@ Three reasons:
2. **No shell-quoting fragility.** Passing `--metadata '{"files": [...]}'` through shlex + argparse is a latent footgun. Structured tool args skip it entirely. 2. **No shell-quoting fragility.** Passing `--metadata '{"files": [...]}'` through shlex + argparse is a latent footgun. Structured tool args skip it entirely.
3. **Better errors.** Tool results are structured JSON the model can reason about, not stderr strings it has to parse. 3. **Better errors.** Tool results are structured JSON the model can reason about, not stderr strings it has to parse.
**Zero schema footprint on normal sessions.** A regular `hermes chat` session has zero `kanban_*` tools in its schema. The `check_fn` on each tool only returns True when `HERMES_KANBAN_TASK` is set, which only happens when the dispatcher spawned this process. No tool bloat for users who never touch kanban. **Zero schema footprint on normal sessions.** A regular `hermes chat` session has zero `kanban_*` tools in its schema unless the active profile explicitly enables the `kanban` toolset for orchestrator work. Dispatcher-spawned task workers get task-scoped tools because `HERMES_KANBAN_TASK` is set; orchestrator profiles get the broader routing surface through config. No tool bloat for users who never touch kanban.
The `kanban-worker` and `kanban-orchestrator` skills teach the model which tool to call when and in what order. The `kanban-worker` and `kanban-orchestrator` skills teach the model which tool to call when and in what order.
@@ -697,19 +697,24 @@ User docs: https://hermes-agent.nousresearch.com/docs/user-guide/features/curato
Durable SQLite board for multi-profile / multi-worker collaboration. Durable SQLite board for multi-profile / multi-worker collaboration.
Users drive it via `hermes kanban <verb>`; dispatcher-spawned workers Users drive it via `hermes kanban <verb>`; dispatcher-spawned workers
see a focused `kanban_*` toolset gated by `HERMES_KANBAN_TASK` so the see a focused `kanban_*` toolset gated by `HERMES_KANBAN_TASK`, and
schema footprint is zero outside worker processes. orchestrator profiles can opt into the broader `kanban` toolset. Normal
sessions still have zero `kanban_*` schema footprint unless configured.
- **CLI verbs (common):** `init`, `create`, `list` (alias `ls`), - **CLI verbs (common):** `init`, `create`, `list` (alias `ls`),
`show`, `assign`, `link`, `unlink`, `comment`, `complete`, `block`, `show`, `assign`, `link`, `unlink`, `comment`, `complete`, `block`,
`unblock`, `archive`, `tail`. Less common: `watch`, `stats`, `runs`, `unblock`, `archive`, `tail`. Less common: `watch`, `stats`, `runs`,
`log`, `dispatch`, `daemon`, `gc`. `log`, `dispatch`, `daemon`, `gc`.
- **Worker toolset:** `kanban_show`, `kanban_complete`, `kanban_block`, - **Worker/orchestrator toolset:** `kanban_show`, `kanban_complete`,
`kanban_heartbeat`, `kanban_comment`, `kanban_create`, `kanban_link`. `kanban_block`, `kanban_heartbeat`, `kanban_comment`, `kanban_create`,
`kanban_link`; profiles that explicitly enable the `kanban` toolset
outside a dispatcher-spawned task also get `kanban_list` and
`kanban_unblock` for board routing.
- **Dispatcher** runs inside the gateway by default - **Dispatcher** runs inside the gateway by default
(`kanban.dispatch_in_gateway: true`) — reclaims stale claims, (`kanban.dispatch_in_gateway: true`) — reclaims stale claims,
promotes ready tasks, atomically claims, spawns assigned profiles. promotes ready tasks, atomically claims, spawns assigned profiles.
Auto-blocks a task after ~5 consecutive spawn failures. Auto-blocks a task after the configured `kanban.failure_limit`
consecutive non-success attempts (default: 2).
- **Isolation:** board is the hard boundary (workers get - **Isolation:** board is the hard boundary (workers get
`HERMES_KANBAN_BOARD` pinned in env); tenant is a soft namespace `HERMES_KANBAN_BOARD` pinned in env); tenant is a soft namespace
within a board for workspace-path + memory-key isolation. within a board for workspace-path + memory-key isolation.