Problem or Use Case
Orchestrator profiles need to read and steer the board, but the kanban toolset is shaped for workers — single-task, single-row operations. After issue #18968 made the existing 7 tools (kanban_show, _complete, _block, _heartbeat, _comment, _create, _link) visible to orchestrators with toolsets: [kanban], a real orchestration session immediately bumps into the gap:
> /goal Drive the space board: triage triage-status items, unblock spillover
tasks where work is already shipped, advance P1 items by decomposing
where needed.
Stopped. I can't actually drive the board — the space-orchestrator profile
is missing the enumeration tools.
What I have: kanban_show (needs an ID), create, link, comment, complete,
block, heartbeat.
What I'm missing: kanban_list (to see triage/blocked/P1 columns),
kanban_unblock, kanban_assign.
Without list, I can't:
- See what's in triage-status
- Find spillover blocked items with commit refs
- Enumerate P1 backlog
The orchestrator can kanban_show(t_abc) if it knows the ID, but it can't discover IDs. It can kanban_block(...) but not unblock. It can kanban_create(...) with an assignee but can't reassign. There's no clean tool path for the most natural orchestrator turn:
"Look at every blocked task, find the ones whose block reason references a shipped commit, unblock them, and complete them with that SHA as the result."
Today the only fixes are:
- Give the orchestrator profile the
terminal toolset so it can shell out to harness kanban list --json. Works, but defeats the toolset isolation that makes orchestrators safe (no terminal/file/browser was the whole point).
- Have the human paste board state into chat every turn. Defeats the autonomy.
- Pre-create child tasks for every possible ID and let the worker figure it out. Doesn't scale.
The ergonomic right answer is: kanban tools should include enumeration + orchestrator-mutation verbs, just like the CLI does.
Proposed Solution
Add four new entries in tools/kanban_tools.py that mirror existing CLI verbs. Each goes through the same kanban_db layer the CLI uses, so behavior is consistent across surfaces:
| Tool |
CLI equivalent |
Behavior |
kanban_list |
harness kanban list [--status …] [--assignee …] [--tenant …] |
Returns array of task summaries (id, title, status, assignee, priority, parents, children counts). Supports the same filters as the CLI. |
kanban_unblock |
harness kanban unblock <id> |
Transitions blocked → ready. Reuses the existing CLI handler. |
kanban_assign |
harness kanban assign <id> <profile> |
Reassigns a task. Accepts none to unassign. |
kanban_archive |
harness kanban archive <id> |
Removes the task from active columns. Already used by orchestrators sweeping stale items. |
All four are gated by the same _check_kanban_mode() check the existing tools use, so they appear only for workers (HERMES_KANBAN_TASK set) or orchestrator profiles (kanban in toolsets).
kanban_unblock and kanban_assign should respect _enforce_worker_task_ownership() for workers — those mutations on a foreign task ID don't make sense from a worker context, and orchestrators (no HERMES_KANBAN_TASK) bypass that check by design.
A reasonable also-nice-to-have:
kanban_link already exists. Add kanban_unlink(parent_id, child_id) for symmetry — orchestrators sometimes need to break dependency chains (e.g., the circular-dep recovery I hit in this session: parent awaiting visual on child, child blocked by parent's visual ACs).
Alternatives Considered
Just shell out via the terminal tool. This is what I'm doing today as a workaround. Two reasons it's not the right answer:
-
Backend portability. Workers running on Docker / Modal / Singularity terminal backends don't have harness on $PATH inside the container, so shelling fails. Tools run in the agent's own Python process and reach kanban.db regardless of backend (this is exactly the rationale already documented in the kanban docs for why the existing 7 tools aren't shell wrappers).
-
Toolset isolation. Granting terminal to an orchestrator profile lets the agent run arbitrary shell commands. Orchestrator personas typically encode a "no code, only board ops" rule, but that's a soft guardrail. A native kanban_list tool removes the temptation entirely.
kanban_show against an "all" magic ID. Considered making kanban_show() accept a status filter and return a list when no specific ID is given. Hacky overload, worse ergonomics. Better to add a sibling kanban_list than overload _show.
Shell out only to specific safe commands via a kanban-cli toolset wrapper. A new toolset that wraps harness kanban * subcommands as Python tools. More work than just registering 4 new tools in kanban_tools.py and accomplishes the same thing.
Feature Type
New tool
Scope
Small (single file, < 50 lines)
The handlers are 5-15 lines each — they call into kanban_db.list_tasks(), kanban_db.unblock_task(), etc., serialize the result to JSON, and return. The tools/kanban_tools.py module already has the patterns to copy from the existing 7 tools.
Test coverage in tests/cli/test_kanban_tools.py (or wherever the existing tool tests live) — one happy-path test per new tool, plus one orchestrator-vs-worker gating test.
Contribution
I'd be happy to put up a PR for this if it sounds aligned with the project's direction. Following the same shape as #18968 (which fixed the visibility gate) — small, focused, with tests covering the orchestrator and worker paths.
Pinging this for direction first because the design choice matters: kanban_list's schema (filter args, return shape) is the kind of thing where a quick "yes, looks right" or "no, do it like X" from a maintainer saves a wasted PR.
Discovered while building an autonomous orchestrator profile against a real Kanban board (32 migrated tasks, 5 specialist worker profiles) on the v0.12.0 release.
Problem or Use Case
Orchestrator profiles need to read and steer the board, but the kanban toolset is shaped for workers — single-task, single-row operations. After issue #18968 made the existing 7 tools (
kanban_show,_complete,_block,_heartbeat,_comment,_create,_link) visible to orchestrators withtoolsets: [kanban], a real orchestration session immediately bumps into the gap:The orchestrator can
kanban_show(t_abc)if it knows the ID, but it can't discover IDs. It cankanban_block(...)but not unblock. It cankanban_create(...)with anassigneebut can't reassign. There's no clean tool path for the most natural orchestrator turn:Today the only fixes are:
terminaltoolset so it can shell out toharness kanban list --json. Works, but defeats the toolset isolation that makes orchestrators safe (no terminal/file/browser was the whole point).The ergonomic right answer is: kanban tools should include enumeration + orchestrator-mutation verbs, just like the CLI does.
Proposed Solution
Add four new entries in
tools/kanban_tools.pythat mirror existing CLI verbs. Each goes through the samekanban_dblayer the CLI uses, so behavior is consistent across surfaces:kanban_listharness kanban list [--status …] [--assignee …] [--tenant …]kanban_unblockharness kanban unblock <id>kanban_assignharness kanban assign <id> <profile>noneto unassign.kanban_archiveharness kanban archive <id>All four are gated by the same
_check_kanban_mode()check the existing tools use, so they appear only for workers (HERMES_KANBAN_TASKset) or orchestrator profiles (kanbanintoolsets).kanban_unblockandkanban_assignshould respect_enforce_worker_task_ownership()for workers — those mutations on a foreign task ID don't make sense from a worker context, and orchestrators (noHERMES_KANBAN_TASK) bypass that check by design.A reasonable also-nice-to-have:
kanban_linkalready exists. Addkanban_unlink(parent_id, child_id)for symmetry — orchestrators sometimes need to break dependency chains (e.g., the circular-dep recovery I hit in this session: parent awaiting visual on child, child blocked by parent's visual ACs).Alternatives Considered
Just shell out via the terminal tool. This is what I'm doing today as a workaround. Two reasons it's not the right answer:
Backend portability. Workers running on Docker / Modal / Singularity terminal backends don't have
harnesson$PATHinside the container, so shelling fails. Tools run in the agent's own Python process and reachkanban.dbregardless of backend (this is exactly the rationale already documented in the kanban docs for why the existing 7 tools aren't shell wrappers).Toolset isolation. Granting
terminalto an orchestrator profile lets the agent run arbitrary shell commands. Orchestrator personas typically encode a "no code, only board ops" rule, but that's a soft guardrail. A nativekanban_listtool removes the temptation entirely.kanban_showagainst an "all" magic ID. Considered makingkanban_show()accept a status filter and return a list when no specific ID is given. Hacky overload, worse ergonomics. Better to add a siblingkanban_listthan overload_show.Shell out only to specific safe commands via a
kanban-clitoolset wrapper. A new toolset that wrapsharness kanban *subcommands as Python tools. More work than just registering 4 new tools inkanban_tools.pyand accomplishes the same thing.Feature Type
New tool
Scope
Small (single file, < 50 lines)
The handlers are 5-15 lines each — they call into
kanban_db.list_tasks(),kanban_db.unblock_task(), etc., serialize the result to JSON, and return. Thetools/kanban_tools.pymodule already has the patterns to copy from the existing 7 tools.Test coverage in
tests/cli/test_kanban_tools.py(or wherever the existing tool tests live) — one happy-path test per new tool, plus one orchestrator-vs-worker gating test.Contribution
I'd be happy to put up a PR for this if it sounds aligned with the project's direction. Following the same shape as #18968 (which fixed the visibility gate) — small, focused, with tests covering the orchestrator and worker paths.
Pinging this for direction first because the design choice matters:
kanban_list's schema (filter args, return shape) is the kind of thing where a quick "yes, looks right" or "no, do it like X" from a maintainer saves a wasted PR.Discovered while building an autonomous orchestrator profile against a real Kanban board (32 migrated tasks, 5 specialist worker profiles) on the v0.12.0 release.