---
name: kanban-workflow
description: "Hermes Kanban multi-agent workflow: orchestrator decomposition, worker lifecycle, handoffs, and task graph management."
version: 3.0.0
platforms: [linux, macos, windows]
metadata:
  hermes:
    tags: [kanban, multi-agent, orchestration, collaboration, workflow, task-graph, dispatcher]
    related_skills: []
---

# Kanban Workflow

Multi-agent task routing, worker execution, and handoff management via the Hermes Kanban board. Covers both the orchestrator (who decomposes and routes) and the worker (who executes and reports back).

> **Platform note:** This is a Hermes-native feature. The `kanban_*` tools and `hermes kanban` CLI are available on any Hermes installation. Load this skill whenever the user asks about Kanban tasks, the dispatcher, the board, multi-agent workflows, or task graphs.

---

## Part A: Orchestrator — Decomposition and Routing

### Step 0: Discover Available Profiles

Hermes setups vary. There is **no default specialist roster**. Before fanning out:

```bash
hermes profile list        # or terminal("hermes profile list")
```

Cache the result. Unknown assignees silently fail — the dispatcher drops them and the card sits in `ready` forever.

### When to Use the Board

Create Kanban tasks when any of these are true:

1. Multiple specialists are needed (research + analysis + writing = three profiles)
2. Work should survive a crash/restart (long-running, recurring, or important)
3. Human-in-the-loop review expected
4. Multiple subtasks can run in parallel
5. Review/iteration is expected (reviewer loops on drafter output)
6. Audit trail matters (board rows persist in SQLite forever)

If none apply — it's a small one-shot reasoning task — use `delegate_task` instead.

### The Anti-Temptation Rules

- **Do not execute the work yourself.** Your restricted toolset usually lacks terminal/file/code. If you find yourself "just fixing this quickly" — stop and create a task.
- **For any concrete task, create a Kanban task and assign it.** Every single time.
- **Split multi-lane requests before creating cards.** A user prompt may contain several independent workstreams. Extract lanes first, one card per lane.
- **Run independent lanes in parallel.** If two cards don't need each other's output, leave them unlinked.
- **Never create dependent work as independent ready cards.** Use `parents=[...]` in `kanban_create` so the dispatcher gates promotion.
- **Decompose, route, and summarize — that's the whole job.**

### Decomposition Playbook

#### Step 1 — Understand the goal

Ask clarifying questions if ambiguous.

#### Step 2 — Sketch the task graph

Draft the graph out loud before creating anything:

1. Extract lanes from the request
2. Map each lane to an available profile
3. Decide independence vs. gating
4. Create independent lanes as parallel cards (no parents)
5. Create synthesis/review/integration cards with parent links

Examples of fan-out patterns:

- "Build an app" → design card (parallel) + engineering card(s) + integration/review card (depends on both)
- "Fix blockers and check model variants" → fix card (parallel) + discovery card (parallel) + reviewer card (depends on both)
- "Research docs and implement" → docs-research card (parallel) + codebase-discovery card (parallel) → implementation card (depends on both)

Words like "also," "finally," "and" do NOT automatically imply dependency. Only link when one card literally cannot start until another's output exists.

#### Step 3 — Create tasks and link

```python
t1 = kanban_create(
    title="research: Postgres cost vs current",
    assignee="<research-profile>",
    body="Compare estimated infrastructure costs...",
    tenant=os.environ.get("HERMES_TENANT"),
)["task_id"]

t2 = kanban_create(
    title="research: Postgres performance vs current",
    assignee="<research-profile>",
    body="Compare query latency and throughput...",
)["task_id"]

t3 = kanban_create(
    title="synthesize migration recommendation",
    assignee="<analyst-profile>",
    body="Read findings from T1 and T2...",
    parents=[t1, t2],
)["task_id"]

t4 = kanban_create(
    title="draft decision memo",
    assignee="<writer-profile>",
    body="Turn the analyst's recommendation into a CTO memo...",
    parents=[t3],
)["task_id"]
```

`parents=[...]` gates promotion — children stay in `todo` until every parent reaches `done`.

#### Step 4 — Complete your own orchestrator task

If you were spawned as a planner/orchestrator task:

```python
kanban_complete(
    summary="decomposed into T1-T4: 2 research lanes in parallel, 1 synthesis, 1 prose draft",
    metadata={
        "task_graph": {
            "T1": {"assignee": "...", "parents": []},
            "T2": {"assignee": "...", "parents": []},
            "T3": {"assignee": "...", "parents": ["T1", "T2"]},
            "T4": {"assignee": "...", "parents": ["T3"]},
        },
    },
)
```

### Common Patterns

| Pattern | Structure | Parent links |
|---------|-----------|--------------|
| Fan-out + fan-in | N research cards + 1 synthesis card | synthesis.parents = all research |
| Parallel impl + validation | implementer + explorer (parallel) + reviewer (depends on both) | reviewer.parents = [impl, explorer] |
| Pipeline with gates | planner → implementer → reviewer | each stage.parents = [previous] |
| Same-profile queue | N tasks, same profile, no dependencies | none; dispatcher serializes |
| Human-in-the-loop | any task calls `kanban_block()` | operator unblocks with feedback |

### Goal-Mode Cards (Persistent Workers)

For open-ended cards where one turn rarely finishes:

```python
kanban_create(
    title="Translate the full docs site to French",
    body="Acceptance: every page translated, no English left, links intact.",
    assignee="<translator-profile>",
    goal_mode=True,
    goal_max_turns=15,
)["task_id"]
```

- After each turn, a judge evaluates against title+body (acceptance criteria)
- Budget exhausted without completion → card blocked for human review (never silent exit)
- Write the body as explicit acceptance criteria — the judge is only as good as the goal text

---

## Part B: Worker — Execution and Handoffs

> You're seeing this because the Hermes Kanban dispatcher spawned you as a worker. The **lifecycle** (6 steps: orient → work → heartbeat → block/complete) is also auto-injected into your system prompt as `KANBAN_GUIDANCE`. This section is the deeper detail: workspace handling, good handoff shapes, retry diagnostics, edge cases.

### Workspace Handling

| Kind | What it is | How to work |
|---|---|---|
| `scratch` | Fresh tmp dir, yours alone | Read/write freely; GC'd when archived |
| `dir:<path>` | Shared persistent directory | Other runs read what you write; treat as long-lived state |
| `worktree` | Git worktree at resolved path | If `.git` doesn't exist, run `git worktree add <path> ${HERMES_KANBAN_BRANCH:-wt/$HERMES_KANBAN_TASK}` first |

### Tenant Isolation

If `$HERMES_TENANT` is set, prefix memory entries with the tenant:

- Good: `business-a: Acme is our biggest customer`
- Bad (leaks): `Acme is our biggest customer`

### Good Handoff Shapes (`kanban_complete`)

**Coding task:**
```python
kanban_complete(
    summary="shipped rate limiter — token bucket, 14 tests pass",
    metadata={
        "changed_files": ["rate_limiter.py", "tests/test_rate_limiter.py"],
        "tests_run": 14, "tests_passed": 14,
        "decisions": ["user_id primary, IP fallback for unauthenticated"],
    },
)
```

**Review-required coding task (block instead of complete):**
```python
import json
kanban_comment(
    body="review-required handoff:\n" + json.dumps({
        "changed_files": ["rate_limiter.py"],
        "tests_run": 14, "tests_passed": 14,
        "diff_path": "/path/to/worktree",
    }, indent=2),
)
kanban_block(
    reason="review-required: rate limiter shipped, 14/14 tests pass — needs eyes on the user_id/IP fallback choice before merging",
)
```

**Research task:**
```python
kanban_complete(
    summary="3 libraries reviewed; vLLM wins on throughput, SGLang on latency",
    metadata={
        "sources_read": 12,
        "recommendation": "vLLM",
        "benchmarks": {"vllm": 1.0, "sglang": 0.87},
    },
)
```

### Claiming Cards You Actually Created

If you produced new tasks via `kanban_create`, pass their ids in `created_cards` on `kanban_complete`:

```python
c1 = kanban_create(title="remediate SQL injection", assignee="security-worker")
c2 = kanban_create(title="fix CSRF middleware", assignee="web-worker")

kanban_complete(
    summary="Review done; spawned remediations for both findings.",
    metadata={"pr_number": 123, "approved": False},
    created_cards=[c1["task_id"], c2["task_id"]],
)
```

The kernel verifies each id exists and was created by your profile. Phantom ids block completion.

### Block Reasons That Get Answered Fast

Bad: `"stuck"` — human has no context.

Good: one sentence naming the specific decision you need. Leave longer context as a comment.

```python
kanban_comment(body="Full context: ...")
kanban_block(reason="Rate limit key choice: IP (simple, NAT-unsafe) or user_id (requires auth, skips anonymous)?")
```

### Heartbeats Worth Sending

Good: `"epoch 12/50, loss 0.31"`, `"scanned 1.2M/2.4M rows"`, `"uploaded 47/120 videos"`

Bad: `"still working"`, empty notes, sub-second intervals. Every few minutes max; skip for tasks under ~2 minutes.

### Retry Diagnostics

If `kanban_show` returns `runs: [...]` with closed prior runs:

| `outcome` | Meaning | Action |
|-----------|---------|--------|
| `timed_out` | Hit `max_runtime_seconds` | Chunk work or shorten it |
| `crashed` | OOM or segfault | Reduce memory footprint |
| `spawn_failed` + error | Profile config issue (missing skill/credential) | Ask human via `kanban_block` |
| `reclaimed` + `task archived...` | Operator archived task | Check status carefully before proceeding |
| `blocked` | Previous attempt blocked | Thread should contain unblock comment |

### Do NOT

- Call `delegate_task` as a substitute for `kanban_create` — `delegate_task` is for short reasoning inside YOUR run; `kanban_create` is for cross-agent handoffs
- Call `clarify` to ask a question — you are headless; use `kanban_comment` + `kanban_block` instead
- Modify files outside `$HERMES_KANBAN_WORKSPACE` unless the task body says to
- Create follow-up tasks assigned to yourself — assign to the right specialist
- Complete a task you didn't actually finish — block it instead

### Recovering Stuck Workers

The kanban dashboard flags stuck tasks with ⚠ and opens a Recovery section:

1. **Reclaim** (`hermes kanban reclaim <task_id>`) — abort immediately, reset to `ready`
2. **Reassign** (`hermes kanban reassign <id> <new-profile> --reclaim`) — switch profile, fresh worker
3. **Change profile model** — edit `hermes -p <profile> model` on disk, then Reclaim

### Notification Routing

Receive cross-profile Kanban notifications by adding to `~/.hermes/config.yaml`:

```yaml
notification_sources: ['*']           # all profiles
notification_sources: ['default', 'x'] # or restrict
```

---

## Part C: Pitfalls (Both Roles)

**Inventing profile names that don't exist.** The dispatcher silently fails to spawn unknown assignees. Always assign to a discovered profile; ask the user if unsure.

**Bundling independent lanes into one card.** If the user asks for two independent outcomes, create two cards.

**Over-linking because of wording.** "Finally check X" may still be parallel with implementation if X is static config/docs discovery.

**Forgetting dependency links.** If the task graph says `research → implement → review`, do not create all as independent ready cards.

**Reassignment vs. new task.** If a reviewer blocks with "needs changes," create a NEW task linked from the reviewer's task — don't re-run the same task.

**Argument order for links.** `kanban_link(parent_id=..., child_id=...)` — parent first. Mixing them up demotes the wrong task.

**Don't pre-create the whole graph if the shape depends on intermediate findings.** If T3's structure depends on what T1 and T2 find, let T3 exist as a "synthesize findings" task whose own first step is to read parent handoffs and plan the rest.

**Tenant inheritance.** Pass `tenant=os.environ.get("HERMES_TENANT")` on every `kanban_create` so child tasks stay in the same namespace.

**Task state can change between dispatch and startup.** Always `kanban_show` first. If it reports `blocked` or `archived`, stop.

**Workspace may have stale artifacts.** Read the comment thread — it explains why you're running again.

**Don't rely on the CLI when the guidance is available.** The `kanban_*` tools work across all terminal backends (Docker, Modal, SSH). `hermes kanban <verb>` from your terminal tool fails in containerized backends.

---

## Part D: Quick Reference

| Tool | CLI equivalent | Use case |
|------|---------------|----------|
| `kanban_create` | `hermes kanban create "title" --assignee <p> [--parent <id>]` | Spawn a task |
| `kanban_complete` | `hermes kanban complete <id> --summary "..." --metadata '{...}'` | Finish a task |
| `kanban_block` | `hermes kanban block <id> "reason"` | Pause for human input |
| `kanban_show` | `hermes kanban show <id> --json` | Inspect a task |
| `kanban_comment` | `hermes kanban comment <id> "body"` | Annotate a task |
| `kanban_link` | `hermes kanban link <parent> <child>` | Add dependency |

Use tools from inside an agent; CLI exists for the human at the terminal.

---

## Resources

- For the full orchestrator decomposition playbook with profile discovery recipes: load archived `kanban-orchestrator` references.
- For extended worker lifecycle examples and retry diagnostics: load archived `kanban-worker` references.
