@Teknium: The Hermes Agent Kanban just got a big automation upgrade. Drop one prompt into the triage, and the orchestrator agent …
Summary
Hermes Agent's Kanban feature receives a major automation upgrade: dropping a prompt into triage now triggers an orchestrator agent to decompose it into subtasks and assign specialized agent profiles, with added support for profile descriptions.
View Cached Full Text
Cached at: 05/18/26, 04:34 PM
The Hermes Agent Kanban just got a big automation upgrade.
Drop one prompt into the triage, and the orchestrator agent can take it from there - decomposing it into all the subtasks necessary and automatically assigning agent profiles that fit the specialization needed.
You can also now add descriptions for each agent profile, to better assist the orchestrator in determining what tasks should go to which profile!
Docs: https://hermes-agent.nousresearch.com/docs/user-guide/features/kanban… PR: https://github.com/NousResearch/hermes-agent/pull/27572…
Kanban (Multi-Agent Board) | Hermes Agent
Source: https://hermes-agent.nousresearch.com/docs/user-guide/features/kanban
Kanban — Multi-Agent Profile Collaboration
**Want a walkthrough?**Read theKanban tutorial— four user stories (solo dev, fleet farming, role pipeline with retry, circuit breaker) with dashboard screenshots of each. This page is the reference; the tutorial is the narrative.
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\_list,kanban\_complete,kanban\_block,kanban\_heartbeat,kanban\_comment,kanban\_create,kanban\_link,kanban\_unblock. The dispatcher spawns each worker with these tools already in its schema; orchestrator profiles can also enable thekanbantoolset explicitly. The model reads and routes tasks by calling tools directly,notby shelling out tohermes kanban. SeeHow workers interact with the boardbelow. - **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 samekanban\_dblayer, 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 workloadsdelegate\_taskcan’t:
- Research triage— parallel researchers + analyst + writer, human-in-the-loop.
- Scheduled ops— recurring daily briefs that build a journal over weeks.
- Digital twins— persistent named assistants (
inbox\-triage,ops\-review) that accumulate memory over time. - Engineering pipelines— decompose → implement in parallel worktrees → review → iterate → PR.
- Fleet work— one specialist managing N subjects (50 social accounts, 12 monitored services).
For the full design rationale, comparative analysis against Cline Kanban / Paperclip / NanoClaw / Google Gemini Enterprise, and the eight canonical collaboration patterns, seedocs/hermes\-kanban\-v1\-spec\.pdfin the repository.
Kanban vs.delegate\_task
They look similar; they are not the same primitive.
delegate\_taskKanbanShapeRPC call (fork → join)Durable message queue + state machineParentBlocks until child returnsFire-and-forget aftercreateChild identityAnonymous subagentNamed profile with persistent memoryResumabilityNone — failed = failedBlock → unblock → re-run; crash → reclaimHuman in the loopNot supportedComment / unblock at any pointAgents per taskOne call = one subagentN agents over task’s life (retry, review, follow-up)Audit trailLost on context compressionDurable rows in SQLite foreverCoordinationHierarchical (caller → callee)Peer — any profile reads/writes any taskOne-sentence distinction:delegate\_taskis a function call; Kanban is a work queue where every handoff is a row any profile (or human) can see and edit.
Usedelegate\_taskwhenthe parent agent needs a short reasoning answer before continuing, no humans involved, result goes back into the parent’s context.
Use Kanban whenwork crosses agent boundaries, needs to survive restarts, might need human input, might be picked up by a different role, or needs to be discoverable after the fact.
They coexist: a kanban worker may calldelegate\_taskinternally during its run.
Core concepts
- Board— a standalone queue of tasks with its own SQLite DB, workspaces directory, and dispatcher loop. A single install can have many boards (e.g. one per project, repo, or domain); seeBoards (multi-project)below. Single-project users stay on the
defaultboard and never see the word “board” outside this docs section. - Task— a row with title, optional body, one assignee (a profile name), status (
triage \| todo \| ready \| running \| blocked \| done \| archived), optional tenant namespace, optional idempotency key (dedup for retried automation). - Link—
task\_linksrow recording a parent → child dependency. The dispatcher promotestodo → readywhen all parents aredone. - Comment— the inter-agent protocol. Agents and humans append comments; when a worker is (re-)spawned it reads the full comment thread as part of its context.
- Workspace— the directory a worker operates in. Three kinds:-
scratch(default) — fresh tmp dir under~/\.hermes/kanban/workspaces/<id\>/(or~/\.hermes/kanban/boards/<slug\>/workspaces/<id\>/on non-default boards). -dir:<path\>— an existing shared directory (Obsidian vault, mail ops dir, per-account folder).**Must be an absolute path.**Relative paths likedir:\.\./tenants/foo/are rejected at dispatch because they’d resolve against whatever CWD the dispatcher happens to be in, which is ambiguous and a confused-deputy escape vector. The path is otherwise trusted — it’s your box, your filesystem, the worker runs with your uid. This is the trusted-local-user threat model; kanban is single-host by design. -worktree— a git worktree under\.worktrees/<id\>/for coding tasks. Worker-sidegit worktree addcreates it. - Dispatcher— a long-lived loop that, every N seconds (default 60): reclaims stale claims, reclaims crashed workers (PID gone but TTL not yet expired), promotes ready tasks, atomically claims, spawns assigned profiles. Runsinside the gatewayby default (
kanban\.dispatch\_in\_gateway: true). One dispatcher sweeps all boards per tick; workers are spawned withHERMES\_KANBAN\_BOARDpinned so they can’t see other boards. Afterkanban\.failure\_limitconsecutive spawn failures on the same task (default: 2) the dispatcher auto-blocks it with the last error as the reason — prevents thrashing on tasks whose profile doesn’t exist, workspace can’t mount, etc. - Tenant— optional string namespacewithina board. One specialist fleet can serve multiple businesses (
\-\-tenant business\-a) with data isolation by workspace path and memory key prefix. Tenants are a soft filter; boards are the hard isolation boundary.
Boards (multi-project)
Boards let you separate unrelated streams of work — one per project, repo, or domain — into isolated queues. A new install has exactly one board calleddefault(DB at~/\.hermes/kanban\.dbfor back-compat). Users who only want one stream of work never need to know about boards; the feature is opt-in.
Per-board isolation is absolute:
- Separate SQLite DB per board (
~/\.hermes/kanban/boards/<slug\>/kanban\.db). - Separate
workspaces/andlogs/directories. - Workers spawned for a task seeonlytheir board’s tasks — the dispatcher sets
HERMES\_KANBAN\_BOARDin the child env and everykanban\_\*tool the worker has access to reads it. - Linking tasks across boards is not allowed (keeps the schema simple; if you really need cross-project refs, use free-text mentions and look them up by id manually).
Managing boards from the CLI
# See what's on disk. Fresh installs show only "default".hermes kanban boards list# Create a new board.hermes kanban boards create atm10-server \ --name "ATM10 Server" \ --description "Minecraft modded server ops" \ --icon 🎮 \ --switch # optional: make it the active board# Operate on a specific board without switching.hermes kanban --board atm10-server listhermes kanban --board atm10-server create "Restart ATM server" --assignee ops# Change which board is "current" for subsequent calls.hermes kanban boards switch atm10-serverhermes kanban boards show # who's active right now?# Rename the display name (the slug is immutable — it's the directory name).hermes kanban boards rename atm10-server "ATM10 (Prod)"# Archive (default) — moves the board's dir to boards/_archived/<slug>-<ts>/.# Recoverable by moving the dir back.hermes kanban boards rm atm10-server# Hard delete — `rm -rf` the board dir. No recovery.hermes kanban boards rm atm10-server --delete
Board resolution order (highest precedence first):
- Explicit
\-\-board <slug\>on the CLI call. HERMES\_KANBAN\_BOARDenv var (set by the dispatcher when spawning a worker, so workers can’t see other boards).~/\.hermes/kanban/current— the slug persisted byhermes kanban boards switch.default.
Slugs are validated: lowercase alphanumerics + hyphens + underscores, 1-64 chars, must start with alphanumeric. Uppercase input is auto-downcased. Anything else (slashes, spaces, dots,\.\.) is rejected at the CLI layer so path-traversal tricks can’t name a board.
Managing boards from the dashboard
hermes dashboard→ Kanban tab shows a board switcher at the top as soon as more than one board exists (or any board has tasks). Single-board users see only a small\+ New boardbutton; the switcher is hidden until it matters.
- Board dropdown— pick the active board. Your selection is saved to the browser’s
localStorageso it persists across reloads without shifting the CLI’scurrentpointer out from under a terminal you left open. - + New board— opens a modal asking for slug, display name, description, and icon. Option to auto-switch to the new board.
- Archive— only shown on non-
defaultboards. Confirms, then moves the board dir toboards/\_archived/.
All dashboard API endpoints accept?board=<slug\>for board scoping. The events WebSocket is pinned to a board at connection time; switching in the UI opens a fresh WS against the new board.
Quick start
The commands below areyou(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 therethe model drives the task throughkanban\_\*tool calls, not CLI commands— seeHow workers interact with 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 (you — or an orchestrator agent via kanban_create)hermes kanban create "research AI funding landscape" --assignee researcher# 4. Watch activity live (you)hermes kanban watch# 5. See the board (you)hermes kanban listhermes kanban stats
When the dispatcher picks upt\_abcdand spawns theresearcherprofile, the very first thing that worker’s model does is callkanban\_show\(\)to read its task. It doesn’t runhermes kanban show t\_abcd.
Gateway-embedded dispatcher (default)
The dispatcher runs inside the gateway process. Nothing to install, no separate service to manage — if the gateway is up, ready tasks get picked up on the next tick (60s by default).
# config.yamlkanban: dispatch_in_gateway: true # default dispatch_interval_seconds: 60 # default
Override the config flag at runtime viaHERMES\_KANBAN\_DISPATCH\_IN\_GATEWAY=0for debugging. Standard gateway supervision applies: runhermes gateway startdirectly, or wire the gateway up as a systemd user unit (see the gateway docs). Without a running gateway,readytasks stay where they are until one comes up —hermes kanban createwarns about this at creation time.
Runninghermes kanban daemonas a separate process isdeprecated; use the gateway. If you truly cannot run the gateway (headless host policy forbids long-lived services, etc.) a\-\-forceescape hatch keeps the old standalone daemon alive for one release cycle, but running both a gateway-embedded dispatcher AND a standalone daemon against the samekanban\.dbcauses claim races and is not supported.
Idempotent create (for automation / webhooks)
# First call creates the task. Any subsequent call with the same key# returns the existing task id instead of duplicating.hermes kanban create "nightly ops review" \ --assignee ops \ --idempotency-key "nightly-ops-$(date -u +%Y-%m-%d)" \ --json
Bulk CLI verbs
All the lifecycle verbs accept multiple ids so you can clean up a batch in one command:
hermes kanban complete t_abc t_def t_hij --result "batch wrap"hermes kanban archive t_abc t_def t_hijhermes kanban unblock t_abc t_defhermes kanban block t_abc "need input" --ids t_def t_hij
How workers interact with the board
Workers do not shell out tohermes kanban.When the dispatcher spawns a worker it setsHERMES\_KANBAN\_TASK=t\_abcdin the child’s env, and that env var flips on a dedicatedkanban toolsetin the model’s schema. The same toolset is also available to orchestrator profiles that enablekanbanin their toolsets config. These tools read and mutate the board directly via the Pythonkanban\_dblayer, same as the CLI does. A running worker calls these like any other tool; it never sees or needs thehermes kanbanCLI.
ToolPurposeRequired paramskanban\_showRead the current task (title, body, prior attempts, parent handoffs, comments, full pre-formattedworker\_context). Defaults to the env’s task id.—kanban\_listList task summaries with filters forassignee,status,tenant, archived visibility, and limit. Intended for orchestrators discovering board work.—kanban\_completeFinish withsummary+metadatastructured handoff.at least one ofsummary/result``kanban\_blockEscalate for human input with areason.reason``kanban\_heartbeatSignal liveness during long operations. Pure side-effect.—kanban\_commentAppend a durable note to the task thread.task\_id,body``kanban\_create(Orchestrators) fan out into child tasks with anassignee, optionalparents,skills, etc.title,assignee``kanban\_link(Orchestrators) add aparent\_id → child\_iddependency edge after the fact.parent\_id,child\_id``kanban\_unblock(Orchestrators) move a blocked task back toready.task\_idA typical worker turn looks like:
# 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},)
Anorchestratorworker 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 “(Orchestrators)” tools —kanban\_list,kanban\_create,kanban\_link,kanban\_unblock, andkanban\_commenton foreign tasks — are available through the same toolset; the convention (enforced by thekanban\-orchestratorskill) is that worker profiles don’t fan out or route unrelated work, and orchestrator profiles don’t execute implementation work. Dispatcher-spawned workers are still task-scoped for destructive lifecycle operations and cannot mutate unrelated tasks.
Why tools instead of shelling tohermes kanban
Three reasons:
- **Backend portability.**Workers whose terminal tool points at a remote backend (Docker / Modal / Singularity / SSH) would run
hermes kanban completeinsidethe container, wherehermesisn’t installed and~/\.hermes/kanban\.dbisn’t mounted. The kanban tools run in the agent’s own Python process and always reach~/\.hermes/kanban\.dbregardless of terminal backend. - **No shell-quoting fragility.**Passing
\-\-metadata '\{"files": \[\.\.\.\]\}'through shlex + argparse is a latent footgun. Structured tool args skip it entirely. - **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 regularhermes chatsession has zerokanban\_\*tools in its schema. Thecheck\_fnon each tool only returns True whenHERMES\_KANBAN\_TASKis set, which only happens when the dispatcher spawned this process. No tool bloat for users who never touch kanban.
Thekanban\-workerandkanban\-orchestratorskills teach the model which tool to call when and in what order.
Recommended handoff evidence
kanban\_complete\(summary=\.\.\., metadata=\{\.\.\.\}\)is intentionally flexible: the summary is the human-readable closeout, andmetadatais the machine-readable handoff that downstream agents, reviewers, or dashboards can reuse without scraping prose.
For engineering and review tasks, prefer this optional metadata shape:
{ "changed_files": ["path/to/file.py"], "verification": ["pytest tests/hermes_cli/test_kanban_db.py -q"], "dependencies": ["parent task id or external issue, if any"], "blocked_reason": null, "retry_notes": "what failed before, if this was a retry", "residual_risk": ["what was not tested or still needs human review"]}
These keys are a convention, not a schema requirement. The useful property is that every worker leaves enough evidence for the next reader to answer four questions quickly:
- What changed?
- How was it verified?
- What can unblock or retry this if it fails?
- What risk is still deliberately left open?
Keep secrets, raw logs, tokens, OAuth material, and unrelated transcripts out ofmetadata. Store pointers and summaries instead. If a task has no files or tests, say so explicitly insummaryand usemetadatafor the evidence that does exist, such as source URLs, issue ids, or manual review steps.
The worker skill
Any profile that should be able to work kanban tasks must load thekanban\-workerskill. It teaches the worker the full lifecycle intool calls, not CLI commands:
- On spawn, call
kanban\_show\(\)to read title + body + parent handoffs + prior attempts + full comment thread. cd $HERMES\_KANBAN\_WORKSPACE(via the terminal tool) and do the work there.- Call
kanban\_heartbeat\(note="\.\.\."\)every few minutes during long operations. - Complete with
kanban\_complete\(summary="\.\.\.", metadata=\{\.\.\.\}\), orkanban\_block\(reason="\.\.\."\)if stuck.
kanban\-workeris a bundled skill, synced into every profile during install and update — there is no separate Skills Hub install step. Verify it is present in whichever profile you use for kanban workers (researcher,writer,ops, etc.):
hermes -p <your-worker-profile> skills list | grep kanban-worker
If the bundled copy is missing, restore it for that profile:
hermes -p <your-worker-profile> skills reset kanban-worker --restore
The dispatcher also auto-passes\-\-skills kanban\-workerwhen spawning every worker, so the worker always has the pattern library available even if a profile’s default skills config doesn’t include it.
Sometimes a single task needs specialist context the assignee profile doesn’t carry by default — a translation job that needs thetranslationskill, a review task that needsgithub\-code\-review, a security audit that needssecurity\-pr\-audit. Rather than editing the assignee’s profile every time, attach the skills directly to the task.
From an orchestrator agent(the usual case — one agent routing work to another), use thekanban\_createtool’sskillsarray:
kanban_create( title="translate README to Japanese", 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\-\-skillfor each one:
hermes kanban create "translate README to Japanese" \ --assignee linguist \ --skill translationhermes kanban create "audit auth flow" \ --assignee reviewer \ --skill security-pr-audit \ --skill github-code-review
From the dashboard, type the skills comma-separated into theskillsfield of the inline create form.
These skills areadditiveto the built-inkanban\-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 (runhermes skills listto 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 one of the profiles you’ve set up, and steps back. Thekanban\-orchestratorskill encodes this as tool-call patterns: anti-temptation rules, a Step-0 profile-discovery prompt (the dispatcher silently fails on unknown assignee names, so the orchestrator must ground every card in profiles that actually exist on your machine), and a decomposition playbook keyed onkanban\_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_r1kanban_create(title="research ICP funding, EU angle", assignee="researcher-b", body="…") # → t_r2kanban_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 taskskanban_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",)
kanban\-orchestratoris a bundled skill. It is synced into each profile during install and update, so there is no separate Skills Hub install step. Verify it is present in your orchestrator profile:
hermes -p orchestrator skills list | grep kanban-orchestrator
If the bundled copy is missing, restore it for that profile:
hermes -p orchestrator skills reset kanban-orchestrator --restore
For best results, pair it with a profile whose toolsets are restricted to board operations (kanban,gateway,memory) so the orchestrator literally cannot execute implementation tasks even if it tries.
Dashboard (GUI)
The/kanbanCLI and slash command are enough to run the board headlessly, but a visual board is often the right interface for humans-in-the-loop: triage, cross-profile supervision, reading comment threads, and dragging cards between columns. Hermes ships this as abundled dashboard pluginatplugins/kanban/— not a core feature, not a separate service — following the model laid out inExtending the Dashboard.
Open it with:
hermes kanban init # one-time: create kanban.db if not already presenthermes dashboard # "Kanban" tab appears in the nav, after "Skills"
What the plugin gives you
- AKanbantab showing one column per status:
triage,todo,ready,running,blocked,done(plusarchivedwhen the toggle is on).-triageis the parking column for rough ideas. By default (kanban\.auto\_decompose: true), the dispatcher auto-runs thedecomposeron tasks that land here — the orchestrator profile reads the rough idea, looks at your profile roster (with descriptions), and fans the task out into a small graph of child tasks routed to the best-fit specialists. The original task stays alive as the parent of every child so the orchestrator wakes back up to judge completion when everything finishes. Flip theOrchestration: Auto/Manualpill at the top of the page (or setkanban\.auto\_decompose: false) to switch to manual mode, where triage tasks stay put until you click**⚗ Decomposeon a card or runhermes kanban decompose <id\>. For tasks that don’t need fan-out (or for setups without an orchestrator profile), the✨ Specify**button does a single-task spec rewrite (title + body with goal, approach, acceptance criteria) via the same LLM machinery. SeeAuto vs Manual orchestrationbelow. - Cards show the task id, title, priority badge, tenant tag, assigned profile, comment/link counts, aprogress pill(
N/Mchildren done when the task has dependents), and “created N ago”. A per-card checkbox enables multi-select. - Per-profile lanes inside Running— toolbar checkbox toggles sub-grouping of the Running column by assignee.
- Live updates via WebSocket— the plugin tails the append-only
task\_eventstable on a short poll interval; the board reflects changes the instant any profile (CLI, gateway, or another dashboard tab) acts. Reloads are debounced so a burst of events triggers a single refetch. - Drag-dropcards between columns to change status. The drop sends
PATCH /api/plugins/kanban/tasks/:idwhich routes through the samekanban\_dbcode the CLI uses — the three surfaces can never drift. Moves into destructive statuses (done,archived,blocked) prompt for confirmation. Touch devices use a pointer-based fallback so the board is usable from a tablet. - Inline create— click
\+on any column header to type a title, assignee, priority, and (optionally) a parent task from a dropdown over every existing task. Creating from the Triage column automatically parks the new task in triage. - Multi-select with bulk actions— shift/ctrl-click a card or tick its checkbox to add it to the selection. A bulk action bar appears at the top with batch status transitions, archive, and reassign (by profile dropdown, or “(unassign)”). Destructive batches confirm first. Per-id partial failures are reported without aborting the rest.
- Click a card(without shift/ctrl) to open a side drawer (Escape or click-outside closes) with:- Editable title— click the heading to rename. - Editable assignee / priority— click the meta row to rewrite. - Editable description— markdown-rendered by default (headings, bold, italic, inline code, fenced code,
http\(s\)/mailto:links, bullet lists), with an “edit” button that swaps in a textarea. Markdown rendering is a tiny, XSS-safe renderer — every substitution runs on HTML-escaped input, onlyhttp\(s\)/mailto:links pass through, andtarget="\_blank"+rel="noopener noreferrer"are always set. - Dependency editor— chip list of parents and children, each with an×to unlink, plus dropdowns over every other task to add a new parent or child. Cycle attempts are rejected server-side with a clear message. - Status action row(→ triage / → ready / → running / block / unblock / complete / archive) with confirm prompts for destructive transitions. For cards in theTriagecolumn the row also exposes two LLM-driven actions:⚗ Decomposefans the task out into a graph of child tasks routed to specialist profiles by description (the orchestrator-driven path), and**✨ Specify**does a single-task spec rewrite. Decompose falls back to specify-style promotion when the LLM decides the task doesn’t benefit from fan-out, so it’s a strict superset. Both are reachable from the CLI (hermes kanban decompose <id\>/specify <id\>/\-\-all), from any gateway platform (/kanban decompose <id\>), and programmatically viaPOST /api/plugins/kanban/tasks/:id/decomposeand…/specify. Configure the models underauxiliary\.kanban\_decomposerandauxiliary\.triage\_specifierinconfig\.yaml. - Result section (also markdown-rendered), comment thread with Enter-to-submit, the last 20 events. - Toolbar filters— free-text search, tenant dropdown (defaults to
dashboard\.kanban\.default\_tenantfromconfig\.yaml), assignee dropdown, “show archived” toggle, “lanes by profile” toggle, and aNudge dispatcherbutton so you don’t have to wait for the next 60 s tick.
Visually the target is the familiar Linear / Fusion layout: dark theme, column headers with counts, coloured status dots, pill chips for priority and tenant. The plugin reads only theme CSS vars (\-\-color\-\*,\-\-radius,\-\-font\-mono, ...), so it reskins automatically with whichever dashboard theme is active.
Auto vs Manual orchestration
The kanban board has two ways to handle a task you drop into the Triage column:
Auto (default)—kanban\.auto\_decompose: true. The gateway-embedded dispatcher runs thedecomposeron each tick, capped bykanban\.auto\_decompose\_per\_tick(default 3 tasks per tick) so a bulk-load of triage tasks doesn’t burst-spend the auxiliary LLM. The decomposer reads the rough idea, looks at your installed profiles + their descriptions, and asks the LLM to produce a JSON task graph: which tasks to spawn, who they go to, and which depend on which. The original triage task becomes the parent of every leaf in the graph, so it stays alive until the whole graph completes — and then promotes back toreadyso its assignee (the orchestrator profile) can judge completion and add more tasks if the work isn’t done. This is the “drop a one-liner, walk away” flow.
Manual—kanban\.auto\_decompose: false. Triage tasks stay in triage until you act. Click the**⚗ Decompose**button on a card, runhermes kanban decompose <id\>(or\-\-all), or use/kanban decompose <id\>from a chat. This matches the pre-decomposer behavior of the board, useful when you want full control over what runs when.
Flip between the two modes from theOrchestration: Auto/Manualpill at the top of the kanban page (emerald = Auto, muted gray = Manual), or by editingconfig\.yamldirectly. Both modes coexist withhermes kanban specify— that’s still available as a single-task spec rewrite when you don’t want fan-out.
The decomposer’s routing decisions depend on profile descriptions, which is a per-profile labeling primitive you set withhermes profile create \-\-description "\.\.\.",hermes profile describe <name\> \-\-text "\.\.\.",hermes profile describe <name\> \-\-auto(LLM-generates from the profile’s installed skills + model), or the dashboard’s per-profile editor in the expandedOrchestration settingspanel. Profiles without a description still appear in the roster — they’re routable by name, just less precisely. The decomposer NEVER lands a child task withassignee=None: when the LLM picks an unknown profile, the child gets routed tokanban\.default\_assignee(or the active default profile if that’s unset).
Config knobs (all underkanban:in~/\.hermes/config\.yaml):
KeyDefaultPurposeauto\_decompose``trueDispatcher auto-runs the decomposer every tick.auto\_decompose\_per\_tick``3Cap on decompositions per dispatcher tick. Excess defers to the next tick.orchestrator\_profile``""Profile that owns decomposition. Empty = fall back to active default profile.default\_assignee``""Where a child task lands when the LLM picks an unknown profile. Empty = fall back to active default.And the two auxiliary LLM slots:
KeyPurposeauxiliary\.kanban\_decomposerModel that produces the task graph (called by Decompose). Setprovider/modelto override the main chat model.auxiliary\.profile\_describerModel that auto-generates profile descriptions (called byhermes profile describe \-\-auto).### Architecture
The GUI is strictly aread-through-the-DB + write-through-kanban_dblayer with no domain logic of its own:
┌────────────────────────┐ WebSocket (tails task_events)│ React SPA (plugin) │ ◀──────────────────────────────────┐│ HTML5 drag-and-drop │ │└──────────┬─────────────┘ │ │ REST over fetchJSON │ ▼ │┌────────────────────────┐ writes call kanban_db.* ││ FastAPI router │ directly — same code path ││ plugins/kanban/ │ the CLI /kanban verbs use ││ dashboard/plugin_api.py │└──────────┬─────────────┘ │ │ │ ▼ │┌────────────────────────┐ ││ ~/.hermes/kanban.db │ ───── append task_events ──────────┘│ (WAL, shared) │└────────────────────────┘
REST surface
All routes are mounted under/api/plugins/kanban/and protected by the dashboard’s ephemeral session token:
MethodPathPurposeGET``/board?tenant=<name\>&include\_archived=…Full board grouped by status column, plus tenants + assignees for filter dropdownsGET``/tasks/:idTask + comments + events + linksPOST``/tasksCreate (wrapskanban\_db\.create\_task, acceptstriage: boolandparents: \[id, …\])PATCH``/tasks/:idStatus / assignee / priority / title / body / resultPOST``/tasks/bulkApply the same patch (status / archive / assignee / priority) to every id inids. Per-id failures reported without aborting siblingsPOST``/tasks/:id/commentsAppend a commentPOST``/tasks/:id/specifyRun the triage specifier — auxiliary LLM fleshes out the task body and promotes it fromtriagetotodo. Returns\{ok, task\_id, reason, new\_title\};ok=falsewith a human-readable reason on “not in triage” / no aux client / LLM error is a 200, not a 4xxPOST``/tasks/:id/decomposeRun the kanban decomposer — auxiliary LLM produces a task graph and the helper atomically creates the children + links the root + flipstriage → todo. Returns\{ok, task\_id, reason, fanout, child\_ids, new\_title\}. Same 200-on-LLM-error convention as/specify.GET``/profilesList installed profiles with their descriptions (consumed by the dashboard’s profile-description editor and the orchestrator picker).PATCH``/profiles/:nameSet or clear a profile’s description (user-authored —description\_auto: false). Returns\{ok, profile, description\}.POST``/profiles/:name/describe\-autoGenerate a description for a profile viaauxiliary\.profile\_describer. Persists withdescription\_auto: trueso the dashboard can surface a “review” badge.GET``/orchestrationRead the kanban orchestration settings (orchestrator\_profile,default\_assignee,auto\_decompose) plus theresolvedeffective values after fallbacks.PUT``/orchestrationUpdate one or more of the three orchestration keys inconfig\.yaml. Validates that non-empty profile names actually exist.POST``/linksAdd a dependency (parent\_id→child\_id)DELETE``/links?parent\_id=…&child\_id=…Remove a dependencyPOST``/dispatch?max=…&dry\_run=…Nudge the dispatcher — skip the 60 s waitGET``/configReaddashboard\.kanbanpreferences fromconfig\.yaml—default\_tenant,lane\_by\_profile,include\_archived\_by\_default,render\_markdown``WS``/events?since=<event\_id\>Live stream oftask\_eventsrowsEvery handler is a thin wrapper — the plugin is ~700 lines of Python (router + WebSocket tail + bulk batcher + config reader) and adds no new business logic. A tiny\_conn\(\)helper auto-initializeskanban\.dbon every read and write, so a fresh install works whether the user opened the dashboard first, hit the REST API directly, or ranhermes kanban init.
Dashboard config
Any of these keys underdashboard\.kanbanin~/\.hermes/config\.yamlchanges the tab’s defaults — the plugin reads them at load time viaGET /config:
dashboard: kanban: default_tenant: acme # preselects the tenant filter lane_by_profile: true # default for the "lanes by profile" toggle include_archived_by_default: false render_markdown: true # set false for plain <pre> rendering
Each key is optional and falls back to the shown default.
Security model
The dashboard’s HTTP auth middlewareexplicitly skips/api/plugins/— plugin routes are unauthenticated by design because the dashboard binds to localhost by default. That means the kanban REST surface is reachable from any process on the host.
The WebSocket takes one additional step: it requires the dashboard’s ephemeral session token as a?token=…query parameter (browsers can’t setAuthorizationon an upgrade request), matching the pattern used by the in-browser PTY bridge.
If you runhermes dashboard \-\-host 0\.0\.0\.0, every plugin route — kanban included — becomes reachable from the network.**Don’t do that on a shared host.**The board contains task bodies, comments, and workspace paths; an attacker reaching these routes gets read access to your entire collaboration surface and can also create / reassign / archive tasks.
Tasks in~/\.hermes/kanban\.dbare profile-agnostic on purpose (that’s the coordination primitive). If you open the dashboard withhermes \-p <profile\> dashboard, the board still shows tasks created by any other profile on the host. Same user owns all profiles, but this is worth knowing if multiple personas coexist.
Live updates
task\_eventsis an append-only SQLite table with a monotonicid. The WebSocket endpoint holds each client’s last-seen event id and pushes new rows as they land. When a burst of events arrives, the frontend reloads the (very cheap) board endpoint — simpler and more correct than trying to patch local state from every event kind. WAL mode means the read loop never blocks the dispatcher’sBEGIN IMMEDIATEclaim transactions.
Extending it
The plugin uses the standard Hermes dashboard plugin contract — seeExtending the Dashboardfor the full manifest reference, shell slots, page-scoped slots, and the Plugin SDK. Extra columns, custom card chrome, tenant-filtered layouts, or fulltab\.overridereplacements are all expressible without forking this plugin.
To disable without removing: adddashboard\.plugins\.kanban\.enabled: falsetoconfig\.yaml(or deleteplugins/kanban/dashboard/manifest\.json).
Scope boundary
The GUI is deliberately thin. Everything the plugin does is reachable from the CLI; the plugin just makes it comfortable for humans. Auto-assignment, budgets, governance gates, and org-chart views remain user-space — a router profile, another plugin, or a reuse oftools/approval\.py— exactly as listed in the out-of-scope section of the design spec.
CLI command reference
This is the surfaceyou(or scripts, cron, the dashboard) use to drive the board. Workers running inside the dispatcher use thekanban\_\*tool surfacefor the same operations — the CLI here and the tools there both route throughkanban\_db, so the two surfaces agree by construction.
hermes kanban init # create kanban.db + print daemon hinthermes kanban create "<title>" [--body ...] [--assignee <profile>] [--parent <id>]... [--tenant <name>] [--workspace scratch|worktree|dir:<path>] [--priority N] [--triage] [--idempotency-key KEY] [--max-runtime 30m|2h|1d|<seconds>] [--skill <name>]... [--json]hermes kanban list [--mine] [--assignee P] [--status S] [--tenant T] [--archived] [--json]hermes kanban show <id> [--json]hermes kanban assign <id> <profile> # or 'none' to unassignhermes kanban link <parent_id> <child_id>hermes kanban unlink <parent_id> <child_id>hermes kanban claim <id> [--ttl SECONDS]hermes kanban comment <id> "<text>" [--author NAME]# Bulk verbs — accept multiple ids:hermes kanban complete <id>... [--result "..."]hermes kanban block <id> "<reason>" [--ids <id>...]hermes kanban unblock <id>...hermes kanban archive <id>...hermes kanban tail <id> # follow a single task's event streamhermes kanban watch [--assignee P] [--tenant T] # live stream ALL events to the terminal [--kinds completed,blocked,…] [--interval SECS]hermes kanban heartbeat <id> [--note "..."] # worker liveness signal for long opshermes kanban runs <id> [--json] # attempt history (one row per run)hermes kanban assignees [--json] # profiles on disk + per-assignee task countshermes kanban dispatch [--dry-run] [--max N] # one-shot pass [--failure-limit N] [--json]hermes kanban daemon --force # DEPRECATED — standalone dispatcher (use `hermes gateway start` instead) [--failure-limit N] [--pidfile PATH] [-v]hermes kanban stats [--json] # per-status + per-assignee countshermes kanban log <id> [--tail BYTES] # worker log from ~/.hermes/kanban/logs/hermes kanban notify-subscribe <id> # gateway bridge hook (used by /kanban in the gateway) --platform <name> --chat-id <id> [--thread-id <id>] [--user-id <id>]hermes kanban notify-list [<id>] [--json]hermes kanban notify-unsubscribe <id> --platform <name> --chat-id <id> [--thread-id <id>]hermes kanban context <id> # what a worker seeshermes kanban specify [<id> | --all] [--tenant T] # flesh out a triage-column idea [--author NAME] [--json] # into a full spec and promote to todohermes kanban gc [--event-retention-days N] # workspaces + old events + old logs [--log-retention-days N]
All commands are also available as a slash command in the interactive CLI and in the messaging gateway (see/kanbanslash commandbelow).
/kanbanslash command
Everyhermes kanban <action\>verb is also reachable as/kanban <action\>— from inside an interactivehermes chatsessionandfrom any gateway platform (Telegram, Discord, Slack, WhatsApp, Signal, Matrix, Mattermost, email, SMS). Both surfaces call the exact samehermes\_cli\.kanban\.run\_slash\(\)entry point that reuses thehermes kanbanargparse tree, so the argument surface, flags, and output format are identical across CLI,/kanban, andhermes 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/kanban specify t_abcd # flesh out a triage one-liner into a real spec/kanban specify --all --tenant engineering # sweep every triage task in one tenant
Quote multi-word arguments the same way you would on a shell —run\_slashparses the rest of the line withshlex\.split, so"\.\.\."and'\.\.\.'both work.
Mid-run usage:/kanbanbypasses 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.**/kanbanis 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\_abcdfrom 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 thenextrun of that task will read it inkanban\_show\(\). - You want to know what your fleet is doing without stopping the orchestrator →
/kanban list \-\-mineor/kanban statsinspects 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 oncompleted— without having to poll or remember the task id.
you> /kanban create "transcribe today's podcast" --assignee transcriberbot> 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 reachesdoneorarchived. 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 tailproduce 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/kanbanand 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
The board supports these eight patterns without any new primitives:
PatternShapeExampleP1 Fan-outN siblings, same role“research 5 angles in parallel“P2 Pipelinerole chain: scout → editor → writerdaily brief assemblyP3 Voting / quorumN siblings + 1 aggregator3 researchers → 1 reviewer picksP4 Long-running journalsame profile + shared dir + cronObsidian vaultP5 Human-in-the-loopworker blocks → user comments → unblockambiguous decisionsP6@mentioninline routing from prose@reviewer look at thisP7 Thread-scoped workspace/kanban herein a threadper-project gateway threadsP8 Fleet farmingone profile, N subjects50 social accountsP9 Triage specifierrough idea →triage→hermes kanban specifyexpands body →todo“turn this one-liner into a spec’d task“For worked examples of each, seedocs/hermes\-kanban\-v1\-spec\.pdf.
Multi-tenant usage
When one specialist fleet serves multiple businesses, tag each task with a tenant:
hermes kanban create "monthly report" \ --assignee researcher \ --tenant business-a \ --workspace dir:~/tenants/business-a/data/
Workers receive$HERMES\_TENANTand namespace their memory writes by prefix. The board, the dispatcher, and the profile definitions are all shared; only the data is scoped.
Gateway notifications
When you run/kanban create …from the gateway (Telegram, Discord, Slack, etc.), the originating chat is automatically subscribed to the new task. The gateway’s background notifier pollstask\_eventsevery few seconds and delivers one message per terminal event (completed,blocked,gave\_up,crashed,timed\_out) to that chat. Completed tasks also send the first line of the worker’s\-\-resultso you see the outcome without having to/kanban show.
You can manage subscriptions explicitly from the CLI — useful when a script / cron job wants to notify a chat it didn’t originate from:
hermes kanban notify-subscribe t_abcd \ --platform telegram --chat-id 12345678 --thread-id 7hermes kanban notify-listhermes kanban notify-unsubscribe t_abcd \ --platform telegram --chat-id 12345678 --thread-id 7
A subscription removes itself automatically once the task reachesdoneorarchived; no cleanup needed.
Runs — one row per attempt
A task is a logical unit of work; arunis one attempt to execute it. When the dispatcher claims a ready task it creates a row intask\_runsand pointstasks\.current\_run\_idat it. When that attempt ends — completed, blocked, crashed, timed out, spawn-failed, reclaimed — the run row closes with anoutcomeand the task’s pointer clears. A task that’s been attempted three times has threetask\_runsrows.
Why two tables instead of just mutating the task: you needfull attempt historyfor 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 wherestructured handofflives. When a worker completes a task (viakanban\_complete\(\.\.\.\)) it can pass:
summary(tool param) /\-\-summary(CLI) — human handoff; goes on the run; downstream children see it in theirbuild\_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:
hermes kanban complete t_abcd \ --result "rate limiter shipped" \ --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}'# Review the attempt history on a retried task:hermes kanban runs t_abcd# # OUTCOME PROFILE ELAPSED STARTED# 1 blocked worker 12s 2026-04-27 14:02# → BLOCKED: need decision on rate-limit key# 2 completed worker 8m 2026-04-27 15:18# → implemented token bucket, keys on user_id with IP fallback
Runs are exposed on the dashboard (Run History section in the drawer, one coloured row per attempt) and on the REST API (GET /api/plugins/kanban/tasks/:idreturns aruns\[\]array).PATCH /api/plugins/kanban/tasks/:idwith\{status: "done", summary, metadata\}forwards both to the kernel, so the dashboard’s “mark done” button is CLI-equivalent.task\_eventsrows carry therun\_idthey belong to so the UI can group them by attempt, and thecompletedevent embeds the first-line summary in its payload (capped at 400 chars) so gateway notifiers can render structured handoffs without a second SQL round-trip.
Bulk close caveat.hermes kanban complete a b c \-\-summary Xis refused — structured handoff is per-run, so copy-pasting the same summary to N tasks is almost always wrong. Bulk closewithout\-\-summary/\-\-metadatastill works for the common “I finished a pile of admin tasks” case.
**Reclaimed runs from status changes.**If you drag a running task offrunningin the dashboard (back toready, or straight totodo), or archive a task that was still running, the in-flight run closes withoutcome='reclaimed'rather than being orphaned. Thetask\_runsrow is always in a terminal state whentasks\.current\_run\_idisNULL, and vice versa — that invariant holds across CLI, dashboard, dispatcher, and notifier.
**Synthetic runs for never-claimed completions.**Completing or blocking a task that was never claimed (e.g. a human closes areadytask from the dashboard with a summary, or a CLI user runshermes kanban complete <ready\-task\> \-\-summary X) would otherwise drop the handoff. Instead the kernel inserts a zero-duration run row (started\_at == ended\_at) carrying the summary / metadata / reason so attempt history stays complete. Thecompleted/blockedevent’srun\_idpoints at that row.
**Live drawer refresh.**When the dashboard’s WebSocket event stream reports new events for the task the user is currently viewing, the drawer reloads itself (via a per-task event counter threaded into itsuseEffectdependency list). Closing and reopening is no longer required to see a run’s new row or updated outcome.
Forward compatibility
Two nullable columns ontasksare reserved for v2 workflow routing:workflow\_template\_id(which template this task belongs to) andcurrent\_step\_key(which step in that template is active). The v1 kernel ignores them for routing but lets clients write them, so a v2 release can add the routing machinery without another schema migration.
Event reference
Every transition appends a row totask\_events. Each row carries an optionalrun\_idso UIs can group events by attempt. Kinds group into three clusters so filtering is easy (hermes kanban watch \-\-kinds completed,gave\_up,timed\_out):
Lifecycle(what changed about the task as a logical unit):
KindPayloadWhencreated``\{assignee, status, parents, tenant\}Task inserted.run\_idisNULL.promoted—todo → readybecause all parents hitdone.run\_idisNULL.claimed``\{lock, expires, run\_id\}Dispatcher atomically claimed areadytask for spawn.completed``\{result\_len, summary?\}Worker wrote\-\-result/\-\-summaryand task hitdone.summaryis the first-line handoff (400-char cap); full version lives on the run row. Ifcomplete\_taskis called on a never-claimed task with handoff fields, a zero-duration run is synthesized sorun\_idstill points at something.blocked``\{reason\}Worker or human flipped the task toblocked. Synthesizes a zero-duration run when called on a never-claimed task with\-\-reason.unblocked—blocked → ready, either manually or via/unblock.run\_idisNULL.archived—Hidden from the default board. If the task was still running, carries therun\_idof the run that was reclaimed as a side effect.Edits(human-driven changes that aren’t transitions):
KindPayloadWhenassigned``\{assignee\}Assignee changed (including unassignment).edited``\{fields\}Title or body updated.reprioritized``\{priority\}Priority changed.status``\{status\}Dashboard drag-drop wrote a status directly (e.g.todo → ready). Carries therun\_idof the run that was reclaimed when dragging offrunning; otherwiserun\_idis NULL.Worker telemetry(about the execution process, not the logical task):
KindPayloadWhenspawned``\{pid\}Dispatcher successfully started a worker process.heartbeat``\{note?\}Worker calledhermes kanban heartbeat $TASKto signal liveness during long operations.reclaimed``\{stale\_lock\}Claim TTL expired without a completion; task goes back toready.crashed``\{pid, claimer\}Worker PID no longer alive but TTL hadn’t expired yet.timed\_out``\{pid, elapsed\_seconds, limit\_seconds, sigkill\}``max\_runtime\_secondsexceeded; dispatcher SIGTERM’d (then SIGKILL’d after 5 s grace) and re-queued.spawn\_failed``\{error, failures\}One spawn attempt failed (missing PATH, workspace unmountable, …). Counter increments; task returns toreadyfor retry.gave\_up``\{failures, error\}Circuit breaker fired after N consecutivespawn\_failed. Task auto-blocks with the last error. Default N = 5; override via\-\-failure\-limit.hermes kanban tail <id\>shows these for a single task.hermes kanban watchstreams them board-wide.
Out of scope
Kanban is deliberately single-host.~/\.hermes/kanban\.dbis a local SQLite file and the dispatcher spawns workers on the same machine. Running a shared board across two hosts is not supported — there’s no coordination primitive for “worker X on host A, worker Y on host B,” and the crash-detection path assumes PIDs are host-local. If you need multi-host, run an independent board per host and usedelegate\_task/ a message queue to bridge them.
Design spec
The complete design — architecture, concurrency correctness, comparison with other systems, implementation plan, risks, open questions — lives indocs/hermes\-kanban\-v1\-spec\.pdf. Read that before filing any behavior-change PR.
Similar Articles
@tonysimons_: If you’re using Hermes Agent and you’re not using Kanban yet, this one’s for you. Say goodbye to drift, unfinished proj…
Tony Simons recommends using Kanban methodology with Hermes Agent to avoid drift and unfinished projects.
@Teknium: Hermes Agent now can orchestrate the @OpenHandsDev agents with a new optional skill! `hermes update` then do `hermes sk…
Hermes Agent now supports orchestrating OpenHands agents via a new optional skill, joining existing built-in skills for Claude Code, Codex, OpenCode, and Hermes itself.
@Saboo_Shubham_: This is so insane. Use native Hermes Agent /goal as the orchestrator and Codex /goal as the builder. Thank you @Teknium!
A developer highlights an AI workflow using Hermes Agent as an orchestrator and Codex as a builder for automated tasks.
@intheworldofai: Hermes Agent is evolving FAST. In just the past week, Nous Research added: - A full WebUI/Desktop App - Background Comp…
Nous Research releases a major update to the open-source Hermes Agent, adding native macOS background computer use, multi-agent orchestration via Kanban, and Lightpanda browser integration.
@Teknium: .@NetworkChuck's overview of what makes Hermes Agent unique is really well done. He's clearly been a power user and don…
Teknium recommends NetworkChuck's video overview of Hermes Agent, praising its unique features and encouraging others to try it.