mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-05-05 02:31:47 +00:00
docs: document /kanban slash command (#19584)
* docs: document /kanban slash command
The kanban user guide and slash-commands reference only mentioned the
/kanban slash command in passing. Add a proper section covering:
- CLI and gateway both expose the full hermes kanban surface via
hermes_cli.kanban.run_slash (identical argument surface)
- Mid-run usage: /kanban bypasses the running-agent guard, so reads
and writes land immediately while an agent is still in a turn
- Auto-subscribe on /kanban create from the gateway — originating
chat is subscribed to terminal events, with a worked example
- Output truncation (~3800 chars) in messaging
- Autocomplete hint list vs full subcommand surface
Also adds /kanban rows to both slash-command tables (CLI + messaging)
in reference/slash-commands.md and moves it into the 'works in both'
notes bucket.
* docs(kanban): frame the model's tool surface as primary, CLI as the human surface
The kanban user guide and CLI reference read as if you drive the board
by running `hermes kanban` commands everywhere. In practice:
- **You** (human, scripts, cron, dashboard) use the `hermes kanban …`
CLI, the `/kanban …` slash command, or the REST/dashboard.
- **Workers** spawned by the dispatcher use a dedicated `kanban_*`
toolset (`kanban_show`, `kanban_complete`, `kanban_block`,
`kanban_heartbeat`, `kanban_comment`, `kanban_create`,
`kanban_link`) and never shell out to the CLI.
Changes to `user-guide/features/kanban.md`:
- New 'Two surfaces' intro distinguishes the two front doors up front.
- Quick-start section re-labelled so each step says who is running it
(you vs. orchestrator vs. worker).
- 'How workers interact with the board' rewritten:
- Lead with "Workers do not shell out to `hermes kanban`."
- Tool table extended with required params.
- Concrete worker-turn example (`kanban_show` → `kanban_heartbeat`
→ `kanban_complete`) and an orchestrator fan-out example
(`kanban_create` x N with `parents=[...]`).
- Moved 'Why tools not CLI' from a defensive aside to a clean
follow-up section.
- 'Worker skill' section explicitly says the lifecycle is taught
in tool calls, not CLI commands.
- 'Pinning extra skills' reordered — orchestrator tool form first
(the usual case), human/CLI second, dashboard third.
- 'Orchestrator skill' now shows a canonical `kanban_create` /
`kanban_link` / `kanban_complete` tool-call sequence instead of
only describing what the skill teaches.
- CLI-command-reference heading now clarifies this is the human
surface, with a cross-link to the tool-surface section.
- 'Runs — one row per attempt' structured-handoff example replaced:
the primary example is now `kanban_complete(summary=..., metadata=...)`
(what a worker actually does), with the CLI form retained as
"when you, the human, need to close a task a worker can't."
Changes to `reference/cli-commands.md`:
- `hermes kanban` intro marks itself as the human / scripting surface
and links out to the worker tool surface.
- Corrected `comment <id>` description — the next worker reads it via
`kanban_show()`, not by running `hermes kanban show`.
* docs(kanban-tutorial): reframe worker actions as tool calls
Honest answer to Teknium's follow-up: no, the first pass missed the
tutorial. The four stories all showed `hermes kanban claim /
complete / block / unblock` as if the backend-dev, pm, and reviewer
personas were humans running CLI commands. In a real hermes kanban
run those agents are dispatcher-spawned workers driving the board
through the `kanban_*` tool surface.
Changes:
- Setup intro now distinguishes the three surfaces up front
(dashboard / CLI for you, `kanban_*` tools for workers) and
establishes the convention: `bash` blocks are commands *you* run,
`# worker tool calls` blocks are what the agent emits.
- Story 1 (solo dev schema): 'Claim the schema task, do the work,
hand off' block replaced with the dispatcher spawning the
backend-dev worker and a `kanban_show → kanban_heartbeat →
kanban_complete` tool-call sequence. The 'On the CLI' `hermes
kanban show / runs` block re-labelled as 'you peeking at the board'
to keep it correct as a human inspection step.
- Story 2 (fleet farming): note about structured handoff updated
from `--summary` / `--metadata` CLI flags to
`kanban_complete(summary=..., metadata=...)` tool form.
- Story 3 (role pipeline): the big PM/engineer/reviewer block fully
rewritten as three worker tool-call sequences — PM worker
completes spec, engineer worker blocks, human/reviewer
`hermes kanban unblock` (or `/kanban unblock`), engineer worker
respawns and completes. The respawn-as-new-run mechanic is now
explicit.
- Reviewer paragraph: `build_worker_context` replaced with
`kanban_show()` — that's the tool that delivers the parent
handoff to the model.
- Structured handoff section heading and body updated:
`--summary`/`--metadata` → `summary`/`metadata` (tool params),
with a note that the tool surface doesn't expose a bulk variant
for the same reason the CLI refuses multi-task `complete`.
Story 4 (circuit breaker) unchanged — its workers fail to spawn,
so there are no tool calls to show; the `hermes kanban create` and
`hermes kanban runs` commands in it are correctly human-driven.
This commit is contained in:
parent
0628004709
commit
986ec04048
4 changed files with 271 additions and 82 deletions
|
|
@ -10,7 +10,9 @@ hermes dashboard # opens http://127.0.0.1:9119 in your browser
|
|||
# click Kanban in the left nav
|
||||
```
|
||||
|
||||
The dashboard is the most comfortable place to learn the system. Everything you see here is also available via `hermes kanban <verb>` on the CLI — the two surfaces share the same SQLite database at `~/.hermes/kanban.db`.
|
||||
The dashboard is the most comfortable place for **you** to watch the system. Agent workers the dispatcher spawns never see the dashboard or the CLI — they drive the board through a dedicated `kanban_*` [toolset](./kanban#how-workers-interact-with-the-board) (`kanban_show`, `kanban_complete`, `kanban_block`, `kanban_heartbeat`, `kanban_comment`, `kanban_create`, `kanban_link`). All three surfaces — dashboard, CLI, worker tools — route through the same `~/.hermes/kanban.db`, so the board is consistent no matter which side of the fence a change came from.
|
||||
|
||||
Throughout the tutorial, **code blocks labelled `bash` are commands *you* run.** Code blocks labelled `# worker tool calls` are what the spawned worker's model emits as tool calls — shown here so you can see the loop end-to-end, not because you'd ever run them yourself.
|
||||
|
||||
## The board at a glance
|
||||
|
||||
|
|
@ -57,22 +59,32 @@ hermes kanban create "Write auth integration tests" \
|
|||
|
||||
Because `API` has `SCHEMA` as its parent, and `tests` has `API` as its parent, only `SCHEMA` starts in `ready`. The other two sit in `todo` until their parents complete. This is the dependency promotion engine doing its job — no other worker will pick up the test-writing until there's an API to test.
|
||||
|
||||
Claim the schema task, do the work, hand off:
|
||||
On the next dispatcher tick (60s by default, or immediately if you hit **Nudge dispatcher**) the `backend-dev` profile spawns as a worker with `HERMES_KANBAN_TASK=$SCHEMA` in its env. Here's what the worker's tool-call loop looks like from inside the agent:
|
||||
|
||||
```bash
|
||||
hermes kanban claim $SCHEMA
|
||||
```python
|
||||
# worker tool calls — NOT commands you run
|
||||
kanban_show()
|
||||
# → returns title, body, worker_context, parents, prior attempts, comments
|
||||
|
||||
# (you design the schema, commit, etc.)
|
||||
# (worker reads worker_context, uses terminal/file tools to design the schema,
|
||||
# write migrations, run its own checks, commit — the real work happens here)
|
||||
|
||||
hermes kanban complete $SCHEMA \
|
||||
--summary "users(id, email, pw_hash), sessions(id, user_id, jti, expires_at); refresh tokens stored as sessions with type='refresh'" \
|
||||
--metadata '{
|
||||
kanban_heartbeat(note="schema drafted, writing migrations now")
|
||||
|
||||
kanban_complete(
|
||||
summary="users(id, email, pw_hash), sessions(id, user_id, jti, expires_at); "
|
||||
"refresh tokens stored as sessions with type='refresh'",
|
||||
metadata={
|
||||
"changed_files": ["migrations/001_users.sql", "migrations/002_sessions.sql"],
|
||||
"decisions": ["bcrypt for hashing", "JWT for session tokens", "7-day refresh, 15-min access"]
|
||||
}'
|
||||
"decisions": ["bcrypt for hashing", "JWT for session tokens",
|
||||
"7-day refresh, 15-min access"],
|
||||
},
|
||||
)
|
||||
```
|
||||
|
||||
When `SCHEMA` hits `done`, the dependency engine promotes `API` to `ready` automatically. The API worker, when it picks up, will read `SCHEMA`'s summary and metadata in its context — so it knows the schema decisions without re-reading a long design doc.
|
||||
`kanban_show` defaults `task_id` to `$HERMES_KANBAN_TASK`, so the worker doesn't need to know its own id. `kanban_complete` writes the summary + metadata onto the current `task_runs` row, closes that run, and transitions the task to `done` — all in one atomic hop through `kanban_db`.
|
||||
|
||||
When `SCHEMA` hits `done`, the dependency engine promotes `API` to `ready` automatically. The API worker, when it picks up, will call `kanban_show()` and see `SCHEMA`'s summary and metadata attached to the parent handoff — so it knows the schema decisions without re-reading a long design doc.
|
||||
|
||||
Click the completed schema task on the board and the drawer shows everything:
|
||||
|
||||
|
|
@ -80,7 +92,7 @@ Click the completed schema task on the board and the drawer shows everything:
|
|||
|
||||
The Run History section at the bottom is the key addition. One attempt: outcome `completed`, worker `@backend-dev`, duration, timestamp, and the handoff summary in full. The metadata blob (`changed_files`, `decisions`) is stored on the run too and surfaced to any downstream worker that reads this parent.
|
||||
|
||||
On the CLI:
|
||||
You can inspect the same data from your terminal at any time — these commands are **you** peeking at the board, not the worker:
|
||||
|
||||
```bash
|
||||
hermes kanban show $SCHEMA
|
||||
|
|
@ -125,7 +137,7 @@ Now filter the board to `content-ops` (or just search for "Transcribe") and you
|
|||
|
||||
Two transcribes done, one running, two ready waiting for the next dispatcher tick. The In Progress column is grouped by profile (the "Lanes by profile" default) so you see each worker's active task without scanning a mixed list. The dispatcher will promote the next ready task to running as soon as the current one completes. With three daemons working on three assignee pools in parallel, the whole content queue drains without further human input.
|
||||
|
||||
**Everything Story 1 said about structured handoff still applies here.** A translator worker completing a call can pass `--summary "translated 4 pages, style matched existing marketing voice"` and `--metadata '{"duration_seconds": 720, "tokens_used": 2100}'` — useful for analytics and for any downstream task that depends on this one.
|
||||
**Everything Story 1 said about structured handoff still applies here.** A translator worker completing a call emits `kanban_complete(summary="translated 4 pages, style matched existing marketing voice", metadata={"duration_seconds": 720, "tokens_used": 2100})` — useful for analytics and for any downstream task that depends on this one.
|
||||
|
||||
## Story 3 — Role pipeline with retry
|
||||
|
||||
|
|
@ -137,32 +149,64 @@ The dashboard view, filtered by `auth-project`:
|
|||
|
||||
Three-stage chain visible at once: `Spec: password reset flow` (DONE, pm), `Implement password reset flow` (DONE, backend-dev), `Review password reset PR` (READY, reviewer). Each has its parent in green at the bottom and children as dependencies.
|
||||
|
||||
The interesting one is the implementation task, because it was blocked and retried:
|
||||
The interesting one is the implementation task, because it was blocked and retried. Here's the full three-agent choreography, shown as the tool calls each worker's model makes:
|
||||
|
||||
```bash
|
||||
# PM completes the spec with acceptance criteria in metadata
|
||||
hermes kanban complete $SPEC \
|
||||
--summary "spec approved; POST /forgot-password sends email, GET /reset/:token renders form, POST /reset applies new password" \
|
||||
--metadata '{"acceptance": [
|
||||
```python
|
||||
# --- PM worker spawns on $SPEC and writes the acceptance criteria ---
|
||||
# worker tool calls
|
||||
kanban_show()
|
||||
kanban_complete(
|
||||
summary="spec approved; POST /forgot-password sends email, "
|
||||
"GET /reset/:token renders form, POST /reset applies new password",
|
||||
metadata={"acceptance": [
|
||||
"expired token returns 410",
|
||||
"reused last-3 password returns 400 with message",
|
||||
"successful reset invalidates all active sessions"
|
||||
]}'
|
||||
"successful reset invalidates all active sessions",
|
||||
]},
|
||||
)
|
||||
# → $SPEC is done; $IMPL auto-promotes from todo to ready
|
||||
|
||||
# Engineer claims + implements, but review blocks it for missing strength check
|
||||
hermes kanban claim $IMPL
|
||||
hermes kanban block $IMPL "Review: password strength check missing, reset link isn't single-use (can be replayed within 30min)"
|
||||
# --- Engineer worker spawns on $IMPL (first attempt) ---
|
||||
# worker tool calls
|
||||
kanban_show() # reads $SPEC's summary + acceptance metadata in worker_context
|
||||
# (engineer writes code, runs tests, opens PR)
|
||||
# Reviewer feedback arrives — engineer decides the concerns are valid and blocks
|
||||
kanban_block(
|
||||
reason="Review: password strength check missing, reset link isn't "
|
||||
"single-use (can be replayed within 30min)",
|
||||
)
|
||||
# → $IMPL transitions to blocked; run 1 closes with outcome='blocked'
|
||||
```
|
||||
|
||||
# Engineer iterates, resolves, completes
|
||||
Now you (the human, or a separate reviewer profile) read the block reason, decide the fix direction is clear, and unblock from the dashboard's "Unblock" button — or from the CLI / slash command:
|
||||
|
||||
```bash
|
||||
hermes kanban unblock $IMPL
|
||||
hermes kanban claim $IMPL
|
||||
hermes kanban complete $IMPL \
|
||||
--summary "added zxcvbn strength check, reset tokens are now single-use (stored + deleted on success)" \
|
||||
--metadata '{
|
||||
"changed_files": ["auth/reset.py", "auth/tests/test_reset.py", "migrations/003_single_use_reset_tokens.sql"],
|
||||
# or from a chat: /kanban unblock $IMPL
|
||||
```
|
||||
|
||||
The dispatcher promotes `$IMPL` back to `ready` and, on the next tick, respawns the `backend-dev` worker. This second spawn is a **new run** on the same task:
|
||||
|
||||
```python
|
||||
# --- Engineer worker spawns on $IMPL (second attempt) ---
|
||||
# worker tool calls
|
||||
kanban_show()
|
||||
# → worker_context now includes the run 1 block reason, so this worker knows
|
||||
# which two things to fix instead of re-reading the whole spec
|
||||
# (engineer adds zxcvbn check, makes reset tokens single-use, re-runs tests)
|
||||
kanban_complete(
|
||||
summary="added zxcvbn strength check, reset tokens are now single-use "
|
||||
"(stored + deleted on success)",
|
||||
metadata={
|
||||
"changed_files": [
|
||||
"auth/reset.py",
|
||||
"auth/tests/test_reset.py",
|
||||
"migrations/003_single_use_reset_tokens.sql",
|
||||
],
|
||||
"tests_run": 11,
|
||||
"review_iteration": 2
|
||||
}'
|
||||
"review_iteration": 2,
|
||||
},
|
||||
)
|
||||
```
|
||||
|
||||
Click the implementation task. The drawer shows **two attempts**:
|
||||
|
|
@ -178,7 +222,7 @@ The reviewer picks up next. When they open `Review password reset PR`, they see:
|
|||
|
||||

|
||||
|
||||
The parent link is the completed implementation. When the reviewer's worker calls `build_worker_context`, it pulls the parent's most-recent-completed-run summary + metadata — so the reviewer reads "added zxcvbn strength check, reset tokens are now single-use" and has the list of changed files in hand before looking at a diff.
|
||||
The parent link is the completed implementation. When the reviewer's worker spawns on `Review password reset PR` and calls `kanban_show()`, the returned `worker_context` includes the parent's most-recent-completed-run summary + metadata — so the reviewer reads "added zxcvbn strength check, reset tokens are now single-use" and has the list of changed files in hand before looking at a diff.
|
||||
|
||||
## Story 4 — Circuit breaker and crash recovery
|
||||
|
||||
|
|
@ -234,18 +278,18 @@ The drawer shows the full two-attempt history:
|
|||
|
||||
Run 1 — `crashed`, with the error `OOM kill at row 2.3M (process 99999 gone)`. Run 2 — `completed`, with `"strategy": "chunked with LIMIT + WHERE id > last_id"` in its metadata. The retrying worker saw the crash of run 1 in its context and picked a safer strategy; the metadata makes it obvious to a future observer (or postmortem writer) what changed.
|
||||
|
||||
## Structured handoff — why `--summary` and `--metadata` matter
|
||||
## Structured handoff — why `summary` and `metadata` matter
|
||||
|
||||
In every story above, workers passed `--summary` and `--metadata` on completion. That's not decoration — it's the primary handoff channel between stages of a workflow.
|
||||
In every story above, workers called `kanban_complete(summary=..., metadata=...)` at the end. That's not decoration — it's the primary handoff channel between stages of a workflow.
|
||||
|
||||
When a worker on task B reads its context, it gets:
|
||||
When a worker on task B is spawned and calls `kanban_show()`, the `worker_context` it gets back includes:
|
||||
|
||||
- B's **prior attempts** (previous runs: outcome, summary, error, metadata) so a retrying worker doesn't repeat a failed path.
|
||||
- **Parent task results** — for each parent, the most-recent completed run's summary and metadata — so downstream workers see why and how the upstream work was done.
|
||||
|
||||
This replaces the "dig through comments and the work output" dance that plagues flat kanban systems. A PM writes acceptance criteria in the spec's metadata, and the engineer's worker sees them structurally. An engineer records which tests they ran and how many passed, and the reviewer's worker has that list in hand before opening a diff.
|
||||
This replaces the "dig through comments and the work output" dance that plagues flat kanban systems. A PM writes acceptance criteria in the spec's metadata, and the engineer's worker sees them structurally in the parent handoff. An engineer records which tests they ran and how many passed, and the reviewer's worker has that list in hand before opening a diff.
|
||||
|
||||
The bulk-close guard exists because this data is per-run. `hermes kanban complete a b c --summary X` is refused — copy-pasting the same summary to three tasks is almost always wrong. Bulk close without the handoff flags still works for the common "I finished a pile of admin tasks" case.
|
||||
The bulk-close guard exists because this data is per-run. `hermes kanban complete a b c --summary X` (you, from the CLI) is refused — copy-pasting the same summary to three tasks is almost always wrong. Bulk close without the handoff flags still works for the common "I finished a pile of admin tasks" case. The tool surface doesn't expose a bulk variant at all; `kanban_complete` is always single-task-at-a-time for the same reason.
|
||||
|
||||
## Inspecting a task currently running
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,15 @@ description: "Durable SQLite-backed task board for coordinating multiple Hermes
|
|||
|
||||
Hermes Kanban is a durable task board, shared across all your Hermes profiles, that lets multiple named agents collaborate on work without fragile in-process subagent swarms. Every task is a row in `~/.hermes/kanban.db`; every handoff is a row anyone can read and write; every worker is a full OS process with its own identity.
|
||||
|
||||
### Two surfaces: the model talks through tools, you talk through the CLI
|
||||
|
||||
The board has two front doors, both backed by the same `~/.hermes/kanban.db`:
|
||||
|
||||
- **Agents drive the board through a dedicated `kanban_*` toolset** — `kanban_show`, `kanban_complete`, `kanban_block`, `kanban_heartbeat`, `kanban_comment`, `kanban_create`, `kanban_link`. The dispatcher spawns each worker with these tools already in its schema; the model reads its task and hands work off by calling them directly, *not* by shelling out to `hermes kanban`. See [How workers interact with the board](#how-workers-interact-with-the-board) below.
|
||||
- **You (and scripts, and cron) drive the board through `hermes kanban …`** on the CLI, `/kanban …` as a slash command, or the dashboard. These are for humans and automation — the places without a tool-calling model behind them.
|
||||
|
||||
Both surfaces route through the same `kanban_db` layer, so reads see a consistent view and writes can't drift. The rest of this page shows CLI examples because they're easy to copy-paste, but every CLI verb has a tool-call equivalent the model uses.
|
||||
|
||||
This is the shape that covers the workloads `delegate_task` can't:
|
||||
|
||||
- **Research triage** — parallel researchers + analyst + writer, human-in-the-loop.
|
||||
|
|
@ -57,24 +66,28 @@ They coexist: a kanban worker may call `delegate_task` internally during its run
|
|||
|
||||
## Quick start
|
||||
|
||||
The commands below are **you** (the human) setting up the board and creating tasks. Once a task is assigned, the dispatcher spawns the assigned profile as a worker, and from there **the model drives the task through `kanban_*` tool calls, not CLI commands** — see [How workers interact with the board](#how-workers-interact-with-the-board).
|
||||
|
||||
```bash
|
||||
# 1. Create the board
|
||||
# 1. Create the board (you)
|
||||
hermes kanban init
|
||||
|
||||
# 2. Start the gateway (hosts the embedded dispatcher)
|
||||
hermes gateway start
|
||||
|
||||
# 3. Create a task
|
||||
# 3. Create a task (you — or an orchestrator agent via kanban_create)
|
||||
hermes kanban create "research AI funding landscape" --assignee researcher
|
||||
|
||||
# 4. Watch activity live
|
||||
# 4. Watch activity live (you)
|
||||
hermes kanban watch
|
||||
|
||||
# 5. See the board
|
||||
# 5. See the board (you)
|
||||
hermes kanban list
|
||||
hermes kanban stats
|
||||
```
|
||||
|
||||
When the dispatcher picks up `t_abcd` and spawns the `researcher` profile, the very first thing that worker's model does is call `kanban_show()` to read its task. It doesn't run `hermes kanban show t_abcd`.
|
||||
|
||||
### Gateway-embedded dispatcher (default)
|
||||
|
||||
The dispatcher runs inside the gateway process. Nothing to install, no
|
||||
|
|
@ -127,22 +140,61 @@ hermes kanban block t_abc "need input" --ids t_def t_hij
|
|||
|
||||
## How workers interact with the board
|
||||
|
||||
When the dispatcher spawns a worker, it sets `HERMES_KANBAN_TASK` in the child's env. That env var is the gate for a dedicated **kanban toolset** — 7 tools that the normal agent schema never sees:
|
||||
**Workers do not shell out to `hermes kanban`.** When the dispatcher spawns a worker it sets `HERMES_KANBAN_TASK=t_abcd` in the child's env, and that env var flips on a dedicated **kanban toolset** in the model's schema — seven tools that read and mutate the board directly via the Python `kanban_db` layer, same as the CLI does. A running worker calls these like any other tool; it never sees or needs the `hermes kanban` CLI.
|
||||
|
||||
| Tool | Purpose |
|
||||
|---|---|
|
||||
| `kanban_show` | Read the current task (title, body, prior attempts, parent handoffs, comments, full `worker_context`). Defaults to the env's task id. |
|
||||
| `kanban_complete` | Finish with `summary` + `metadata` structured handoff. |
|
||||
| `kanban_block` | Escalate for human input. |
|
||||
| `kanban_heartbeat` | Signal liveness during long operations. |
|
||||
| `kanban_comment` | Append to the task thread. |
|
||||
| `kanban_create` | (Orchestrators) fan out into child tasks. |
|
||||
| `kanban_link` | (Orchestrators) add dependency edges after the fact. |
|
||||
| Tool | Purpose | Required params |
|
||||
|---|---|---|
|
||||
| `kanban_show` | Read the current task (title, body, prior attempts, parent handoffs, comments, full pre-formatted `worker_context`). Defaults to the env's task id. | — |
|
||||
| `kanban_complete` | Finish with `summary` + `metadata` structured handoff. | at least one of `summary` / `result` |
|
||||
| `kanban_block` | Escalate for human input with a `reason`. | `reason` |
|
||||
| `kanban_heartbeat` | Signal liveness during long operations. Pure side-effect. | — |
|
||||
| `kanban_comment` | Append a durable note to the task thread. | `task_id`, `body` |
|
||||
| `kanban_create` | (Orchestrators) fan out into child tasks with an `assignee`, optional `parents`, `skills`, etc. | `title`, `assignee` |
|
||||
| `kanban_link` | (Orchestrators) add a `parent_id → child_id` dependency edge after the fact. | `parent_id`, `child_id` |
|
||||
|
||||
**Why tools and not just shelling to `hermes kanban`?** Three reasons:
|
||||
A typical worker turn looks like:
|
||||
|
||||
1. **Backend portability.** Workers whose terminal tool points at a remote backend (Docker / Modal / Singularity / SSH) would run `hermes kanban complete` inside the container where `hermes` isn't installed and the DB isn't mounted. The kanban tools run in the agent's own Python process and always reach `~/.hermes/kanban.db` regardless of terminal backend.
|
||||
2. **No shell-quoting fragility.** Passing `--metadata '{"files": [...]}'` through shlex + argparse is a latent footgun. Structured tool args skip it.
|
||||
```
|
||||
# Model's tool calls, in order:
|
||||
kanban_show() # no args — uses HERMES_KANBAN_TASK
|
||||
# (model reads the returned worker_context, does the work via terminal/file tools)
|
||||
kanban_heartbeat(note="halfway through — 4 of 8 files transformed")
|
||||
# (more work)
|
||||
kanban_complete(
|
||||
summary="migrated limiter.py to token-bucket; added 14 tests, all pass",
|
||||
metadata={"changed_files": ["limiter.py", "tests/test_limiter.py"], "tests_run": 14},
|
||||
)
|
||||
```
|
||||
|
||||
An **orchestrator** worker fans out instead:
|
||||
|
||||
```
|
||||
kanban_show()
|
||||
kanban_create(
|
||||
title="research ICP funding 2024-2026",
|
||||
assignee="researcher-a",
|
||||
body="focus on seed + series A, North America, AI-adjacent",
|
||||
)
|
||||
# → returns {"task_id": "t_r1", ...}
|
||||
kanban_create(title="research ICP funding — EU angle", assignee="researcher-b", body="…")
|
||||
# → returns {"task_id": "t_r2", ...}
|
||||
kanban_create(
|
||||
title="synthesize findings into launch brief",
|
||||
assignee="writer",
|
||||
parents=["t_r1", "t_r2"], # promotes to ready when both complete
|
||||
body="one-pager, 300 words, neutral tone",
|
||||
)
|
||||
kanban_complete(summary="decomposed into 2 research tasks + 1 writer; linked dependencies")
|
||||
```
|
||||
|
||||
The three "(Orchestrators)" tools — `kanban_create`, `kanban_link`, and `kanban_comment` on foreign tasks — are available to every worker; the convention (enforced by the `kanban-orchestrator` skill) is that worker profiles don't fan out and orchestrator profiles don't execute.
|
||||
|
||||
### Why tools instead of shelling to `hermes kanban`
|
||||
|
||||
Three reasons:
|
||||
|
||||
1. **Backend portability.** Workers whose terminal tool points at a remote backend (Docker / Modal / Singularity / SSH) would run `hermes kanban complete` *inside* the container, where `hermes` isn't installed and `~/.hermes/kanban.db` isn't mounted. The kanban tools run in the agent's own Python process and always reach `~/.hermes/kanban.db` regardless of terminal backend.
|
||||
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.
|
||||
|
||||
**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.
|
||||
|
|
@ -151,14 +203,14 @@ The `kanban-worker` and `kanban-orchestrator` skills teach the model which tool
|
|||
|
||||
### The worker skill
|
||||
|
||||
Any profile that should be able to work kanban tasks must load the `kanban-worker` skill. It teaches the worker the full lifecycle:
|
||||
Any profile that should be able to work kanban tasks must load the `kanban-worker` skill. It teaches the worker the full lifecycle in **tool calls**, not CLI commands:
|
||||
|
||||
1. On spawn, call `kanban_show()` to read title + body + parent handoffs + prior attempts + full comment thread.
|
||||
2. `cd $HERMES_KANBAN_WORKSPACE` and do the work there.
|
||||
2. `cd $HERMES_KANBAN_WORKSPACE` (via the terminal tool) and do the work there.
|
||||
3. Call `kanban_heartbeat(note="...")` every few minutes during long operations.
|
||||
4. Complete with `kanban_complete(summary="...", metadata={...})`, or `kanban_block(reason="...")` if stuck.
|
||||
|
||||
Load it with:
|
||||
Load it with (this one is **you**, installing into a profile — not a tool call):
|
||||
|
||||
```bash
|
||||
hermes skills install devops/kanban-worker
|
||||
|
|
@ -168,22 +220,9 @@ The dispatcher also auto-passes `--skills kanban-worker` when spawning every wor
|
|||
|
||||
### Pinning extra skills to a specific task
|
||||
|
||||
Sometimes a single task needs specialist context the assignee profile doesn't carry by default — a translation job that needs the `translation` skill, a review task that needs `github-code-review`, a security audit that needs `security-pr-audit`. Rather than editing the assignee's profile every time, attach the skills directly to the task:
|
||||
Sometimes a single task needs specialist context the assignee profile doesn't carry by default — a translation job that needs the `translation` skill, a review task that needs `github-code-review`, a security audit that needs `security-pr-audit`. Rather than editing the assignee's profile every time, attach the skills directly to the task.
|
||||
|
||||
```bash
|
||||
# CLI — repeat --skill for each extra skill
|
||||
hermes kanban create "translate README to Japanese" \
|
||||
--assignee linguist \
|
||||
--skill translation
|
||||
|
||||
# Multiple skills
|
||||
hermes kanban create "audit auth flow" \
|
||||
--assignee reviewer \
|
||||
--skill security-pr-audit \
|
||||
--skill github-code-review
|
||||
```
|
||||
|
||||
From the dashboard's inline create form, type the skills comma-separated into the **skills** field. From another agent (orchestrator pattern), use `kanban_create(skills=[...])`:
|
||||
**From an orchestrator agent** (the usual case — one agent routing work to another), use the `kanban_create` tool's `skills` array:
|
||||
|
||||
```
|
||||
kanban_create(
|
||||
|
|
@ -191,13 +230,53 @@ kanban_create(
|
|||
assignee="linguist",
|
||||
skills=["translation"],
|
||||
)
|
||||
|
||||
kanban_create(
|
||||
title="audit auth flow",
|
||||
assignee="reviewer",
|
||||
skills=["security-pr-audit", "github-code-review"],
|
||||
)
|
||||
```
|
||||
|
||||
**From a human (CLI / slash command)**, repeat `--skill` for each one:
|
||||
|
||||
```bash
|
||||
hermes kanban create "translate README to Japanese" \
|
||||
--assignee linguist \
|
||||
--skill translation
|
||||
|
||||
hermes kanban create "audit auth flow" \
|
||||
--assignee reviewer \
|
||||
--skill security-pr-audit \
|
||||
--skill github-code-review
|
||||
```
|
||||
|
||||
**From the dashboard**, type the skills comma-separated into the **skills** field of the inline create form.
|
||||
|
||||
These skills are **additive** to the built-in `kanban-worker` — the dispatcher emits one `--skills <name>` flag for each (and for the built-in), so the worker spawns with all of them loaded. The skill names must match skills that are actually installed on the assignee's profile (run `hermes skills list` to see what's available); there's no runtime install.
|
||||
|
||||
### The orchestrator skill
|
||||
|
||||
A **well-behaved orchestrator does not do the work itself.** It decomposes the user's goal into tasks, links them, assigns each to a specialist, and steps back. The `kanban-orchestrator` skill encodes this: anti-temptation rules, a standard specialist roster (`researcher`, `writer`, `analyst`, `backend-eng`, `reviewer`, `ops`), and a decomposition playbook.
|
||||
A **well-behaved orchestrator does not do the work itself.** It decomposes the user's goal into tasks, links them, assigns each to a specialist, and steps back. The `kanban-orchestrator` skill encodes this as tool-call patterns: anti-temptation rules, a standard specialist roster (`researcher`, `writer`, `analyst`, `backend-eng`, `reviewer`, `ops`), and a decomposition playbook keyed on `kanban_create` / `kanban_link` / `kanban_comment`.
|
||||
|
||||
A canonical orchestrator turn (two parallel researchers handing off to a writer):
|
||||
|
||||
```
|
||||
# Goal from user: "draft a launch post on the ICP funding landscape"
|
||||
kanban_create(title="research ICP funding, NA angle", assignee="researcher-a", body="…") # → t_r1
|
||||
kanban_create(title="research ICP funding, EU angle", assignee="researcher-b", body="…") # → t_r2
|
||||
kanban_create(
|
||||
title="synthesize ICP funding research into launch post draft",
|
||||
assignee="writer",
|
||||
parents=["t_r1", "t_r2"], # promoted to 'ready' when both researchers complete
|
||||
body="one-pager, neutral tone, cite sources inline",
|
||||
) # → t_w1
|
||||
# Optional: add cross-cutting deps discovered later without re-creating tasks
|
||||
kanban_link(parent_id="t_r1", child_id="t_followup")
|
||||
kanban_complete(
|
||||
summary="decomposed into 2 parallel research tasks → 1 synthesis task; writer starts when both researchers finish",
|
||||
)
|
||||
```
|
||||
|
||||
Load it into your orchestrator profile:
|
||||
|
||||
|
|
@ -324,6 +403,8 @@ The GUI is deliberately thin. Everything the plugin does is reachable from the C
|
|||
|
||||
## CLI command reference
|
||||
|
||||
This is the surface **you** (or scripts, cron, the dashboard) use to drive the board. Workers running inside the dispatcher use the `kanban_*` [tool surface](#how-workers-interact-with-the-board) for the same operations — the CLI here and the tools there both route through `kanban_db`, so the two surfaces agree by construction.
|
||||
|
||||
```
|
||||
hermes kanban init # create kanban.db + print daemon hint
|
||||
hermes kanban create "<title>" [--body ...] [--assignee <profile>]
|
||||
|
|
@ -369,7 +450,57 @@ hermes kanban gc [--event-retention-days N] # workspaces + old events
|
|||
[--log-retention-days N]
|
||||
```
|
||||
|
||||
All commands are also available as a slash command in the gateway (`/kanban list`, `/kanban comment t_abc "need docs"`, etc.). The slash command bypasses the running-agent guard, so you can `/kanban unblock` a stuck worker while the main agent is still chatting.
|
||||
All commands are also available as a slash command in the interactive CLI and in the messaging gateway (see [`/kanban` slash command](#kanban-slash-command) below).
|
||||
|
||||
## `/kanban` slash command {#kanban-slash-command}
|
||||
|
||||
Every `hermes kanban <action>` verb is also reachable as `/kanban <action>` — from inside an interactive `hermes chat` session **and** from any gateway platform (Telegram, Discord, Slack, WhatsApp, Signal, Matrix, Mattermost, email, SMS). Both surfaces call the exact same `hermes_cli.kanban.run_slash()` entry point that reuses the `hermes kanban` argparse tree, so the argument surface, flags, and output format are identical across CLI, `/kanban`, and `hermes kanban`. You don't have to leave the chat to drive the board.
|
||||
|
||||
```
|
||||
/kanban list
|
||||
/kanban show t_abcd
|
||||
/kanban create "write launch post" --assignee writer --parent t_research
|
||||
/kanban comment t_abcd "looks good, ship it"
|
||||
/kanban unblock t_abcd
|
||||
/kanban dispatch --max 3
|
||||
```
|
||||
|
||||
Quote multi-word arguments the same way you would on a shell — `run_slash` parses the rest of the line with `shlex.split`, so `"..."` and `'...'` both work.
|
||||
|
||||
### Mid-run usage: `/kanban` bypasses the running-agent guard
|
||||
|
||||
The gateway normally queues slash commands and user messages while an agent is still thinking — that's what stops you from accidentally starting a second turn while the first is in flight. **`/kanban` is explicitly exempted from this guard.** The board lives in `~/.hermes/kanban.db`, not in the running agent's state, so reads (`list`, `show`, `context`, `tail`, `watch`, `stats`, `runs`) and writes (`comment`, `unblock`, `block`, `assign`, `archive`, `create`, `link`, …) all go through immediately, even mid-turn.
|
||||
|
||||
This is the whole point of the separation:
|
||||
|
||||
- A worker blocks waiting on a peer → you send `/kanban unblock t_abcd` from your phone and the dispatcher picks the peer up on its next tick. The blocked worker isn't interrupted — it just stops being blocked.
|
||||
- You spot a card that needs human context → `/kanban comment t_xyz "use the 2026 schema, not 2025"` lands on the task thread and the *next* run of that task will read it in `kanban_show()`.
|
||||
- You want to know what your fleet is doing without stopping the orchestrator → `/kanban list --mine` or `/kanban stats` inspects the board without touching your main conversation.
|
||||
|
||||
### Auto-subscribe on `/kanban create` (gateway only)
|
||||
|
||||
When you create a task from the gateway with `/kanban create "…"`, the originating chat (platform + chat id + thread id) is automatically subscribed to that task's terminal events (`completed`, `blocked`, `gave_up`, `crashed`, `timed_out`). You'll get one message back per terminal event — including the first line of the worker's result summary on `completed` — without having to poll or remember the task id.
|
||||
|
||||
```
|
||||
you> /kanban create "transcribe today's podcast" --assignee transcriber
|
||||
bot> Created t_9fc1a3 (ready, assignee=transcriber)
|
||||
(subscribed — you'll be notified when t_9fc1a3 completes or blocks)
|
||||
|
||||
… ~8 minutes later …
|
||||
|
||||
bot> ✓ t_9fc1a3 completed by transcriber
|
||||
transcribed 42 minutes, saved to podcast/2026-05-04.md
|
||||
```
|
||||
|
||||
Subscriptions auto-remove themselves once the task reaches `done` or `archived`. If you script a create with `--json` (machine output) the auto-subscribe is skipped — the assumption is that scripted callers want to manage subscriptions explicitly via `/kanban notify-subscribe`.
|
||||
|
||||
### Output truncation in messaging
|
||||
|
||||
Gateway platforms have practical message-length caps. If `/kanban list`, `/kanban show`, or `/kanban tail` produce more than ~3800 characters of output, the response is truncated with a `… (truncated; use \`hermes kanban …\` in your terminal for full output)` footer. The CLI surface has no such cap.
|
||||
|
||||
### Autocomplete
|
||||
|
||||
In the interactive CLI, typing `/kanban ` and hitting Tab cycles through the built-in subcommand list (`list`, `ls`, `show`, `create`, `assign`, `link`, `unlink`, `claim`, `comment`, `complete`, `block`, `unblock`, `archive`, `tail`, `dispatch`, `context`, `init`, `gc`). The remaining verbs listed in the CLI reference above (`watch`, `stats`, `runs`, `log`, `assignees`, `heartbeat`, `notify-subscribe`, `notify-list`, `notify-unsubscribe`, `daemon`) also work — they're just not in the autocomplete hint list yet.
|
||||
|
||||
## Collaboration patterns
|
||||
|
||||
|
|
@ -424,16 +555,26 @@ A task is a logical unit of work; a **run** is one attempt to execute it. When t
|
|||
|
||||
Why two tables instead of just mutating the task: you need **full attempt history** for real-world postmortems ("the second reviewer attempt got to approve, the third merged"), and you need a clean place to hang per-attempt metadata — which files changed, which tests ran, which findings a reviewer noted. Those are run facts, not task facts.
|
||||
|
||||
Runs are also where **structured handoff** lives. When a worker completes a task it can pass:
|
||||
Runs are also where **structured handoff** lives. When a worker completes a task (via `kanban_complete(...)`) it can pass:
|
||||
|
||||
- `--result "<short log line>"` — goes on the task row as before (for back-compat).
|
||||
- `--summary "<human handoff>"` — goes on the run; downstream children see it in their `build_worker_context`.
|
||||
- `--metadata '{"changed_files": [...], "tests_run": 12}'` — JSON dict on the run; children see it serialized alongside the summary.
|
||||
- `summary` (tool param) / `--summary` (CLI) — human handoff; goes on the run; downstream children see it in their `build_worker_context`.
|
||||
- `metadata` (tool param) / `--metadata` (CLI) — free-form JSON dict on the run; children see it serialized alongside the summary.
|
||||
- `result` (tool param) / `--result` (CLI) — short log line that goes on the task row (legacy field, kept for back-compat).
|
||||
|
||||
Downstream children read the most recent completed run's summary + metadata for each parent. Retrying workers read the prior attempts on their own task (outcome, summary, error) so they don't repeat a path that already failed.
|
||||
|
||||
```
|
||||
# What a worker actually does — a tool call, from inside the agent loop:
|
||||
kanban_complete(
|
||||
summary="implemented token bucket, keys on user_id with IP fallback, all tests pass",
|
||||
metadata={"changed_files": ["limiter.py", "tests/test_limiter.py"], "tests_run": 14},
|
||||
result="rate limiter shipped",
|
||||
)
|
||||
```
|
||||
|
||||
The same handoff is reachable from the CLI when you (the human) need to close out a task a worker can't — e.g. a task that was abandoned, or one you marked done manually from the dashboard:
|
||||
|
||||
```bash
|
||||
# Worker completes with a structured handoff:
|
||||
hermes kanban complete t_abcd \
|
||||
--result "rate limiter shipped" \
|
||||
--summary "implemented token bucket, keys on user_id with IP fallback, all tests pass" \
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue