본문으로 건너뛰기

Kanban - 멀티 에이전트 프로필 협업

처음부터 흐름을 따라가고 싶다면 Kanban 튜토리얼을 읽어 보세요. 개인 개발, 플릿 운영(fleet farming), 재시도를 포함한 역할 기반 파이프라인, 서킷 브레이커까지 네 가지 사용자 스토리를 대시보드 스크린샷과 함께 다룹니다. 이 페이지는 참조 문서이고, 튜토리얼은 실제 사용 흐름을 설명합니다.

Hermes Kanban은 모든 Hermes 프로필이 공유하는 내구성 작업 보드입니다. 프로세스 내부의 취약한 서브에이전트 군집에 의존하지 않고, 이름이 붙은 여러 에이전트가 같은 작업 흐름에서 협업할 수 있게 해 줍니다. 모든 작업은 ~/.hermes/kanban.db의 한 행이고, 모든 인계 기록은 누구나 읽고 쓸 수 있는 행이며, 모든 워커는 자기 정체성을 가진 완전한 OS 프로세스입니다.

두 가지 표면: 모델은 도구로, 사용자는 CLI로 대화합니다

보드에는 같은 ~/.hermes/kanban.db를 바라보는 두 개의 진입점이 있습니다.

  • 에이전트는 전용 kanban_* 툴셋으로 보드를 조작합니다. 여기에는 kanban_show, kanban_list, kanban_complete, kanban_block, kanban_heartbeat, kanban_comment, kanban_create, kanban_link, kanban_unblock이 포함됩니다. 디스패처는 각 워커를 시작할 때 이 도구들을 이미 스키마에 넣어 둡니다. 오케스트레이터 프로필도 toolsets 설정에서 kanban을 명시적으로 켤 수 있습니다. 모델은 hermes kanban을 셸로 실행하지 않고, 도구를 직접 호출해 작업을 읽고 라우팅합니다. 자세한 내용은 아래 워커가 보드와 상호작용하는 방식을 참고하세요.
  • 사용자, 스크립트, cron은 CLI의 hermes kanban, 슬래시 명령 /kanban, 또는 대시보드로 보드를 조작합니다. 이 표면들은 뒤에 tool-calling 모델이 없는 사람과 자동화 환경을 위한 인터페이스입니다.

두 표면은 모두 같은 kanban_db 계층을 통과합니다. 그래서 읽기는 일관된 상태를 보고, 쓰기는 서로 충돌하지 않는 방식으로 처리됩니다. 이 페이지에서는 복사해서 실행하기 쉬운 CLI 예제를 중심으로 보여 주지만, 모든 CLI 동작에는 모델이 사용하는 tool call 대응 동작이 있습니다.

Kanban은 delegate_task로 감당하기 어려운 다음 워크로드를 다룹니다.

  • Research triage - 병렬 연구자, 분석가, 작성자, 사람 검토자가 함께 움직이는 흐름
  • Scheduled ops - 몇 주에 걸쳐 저널을 쌓는 반복 일일 브리핑
  • Digital twins - 시간이 지나며 기억을 축적하는 이름 있는 어시스턴트, 예를 들어 inbox-triage, ops-review
  • Engineering pipelines - 작업 분해, 병렬 worktree 구현, 리뷰, 반복 수정, PR
  • Fleet work - 한 전문가가 여러 대상, 예를 들어 소셜 계정 50개나 모니터링 서비스 12개를 관리하는 흐름

전체 설계 배경, Cline Kanban / Paperclip / NanoClaw / Google Gemini Enterprise와의 비교, 여덟 가지 표준 협업 패턴은 저장소의 docs/hermes-kanban-v1-spec.pdf를 참고하세요.

Kanban vs. delegate_task

둘은 비슷해 보이지만 같은 primitive가 아닙니다.

delegate_taskKanban
형태RPC 호출(fork -> join)내구성 메시지 큐 + 상태 머신
부모자식이 반환할 때까지 blockingcreate 이후 fire-and-forget
자식 정체성익명 서브에이전트영속 메모리를 가진 이름 있는 프로필
재개 가능성없음. 실패하면 실패block -> unblock -> 재실행, crash -> reclaim
사람 개입지원하지 않음어느 시점이든 comment / unblock 가능
작업당 에이전트호출 1회 = 서브에이전트 1개작업 생애 동안 여러 에이전트 가능(retry, review, follow-up)
감사 추적context compression 때 사라짐SQLite 행으로 계속 보존
조율 방식계층형(caller -> callee)동료형(peer). 어떤 프로필이든 어떤 작업이든 읽고 쓸 수 있음

한 문장으로 구분하면, delegate_task는 함수 호출이고 Kanban은 모든 인계 기록이 프로필이나 사람이 보고 수정할 수 있는 행으로 남는 작업 큐입니다.

delegate_task를 쓰기 좋은 경우: 부모 에이전트가 계속 진행하기 전에 짧은 추론 답변이 필요하고, 사람이 개입하지 않으며, 결과가 부모 컨텍스트로 바로 돌아가면 되는 경우입니다.

Kanban을 쓰기 좋은 경우: 작업이 에이전트 경계를 넘고, 재시작 후에도 살아남아야 하며, 사람의 입력이 필요할 수 있고, 다른 역할이 이어받을 수 있으며, 나중에 검색 가능해야 하는 경우입니다.

둘은 함께 쓸 수 있습니다. Kanban 워커가 실행 중 내부적으로 delegate_task를 호출하는 것도 가능합니다.

핵심 개념

  • Board - 자체 SQLite DB, workspaces 디렉터리, 디스패처 루프를 가진 독립 작업 큐입니다. 하나의 설치에 여러 보드를 둘 수 있습니다. 예를 들어 프로젝트, 저장소, 도메인마다 하나씩 둘 수 있습니다. 자세한 내용은 아래 Boards (multi-project)를 참고하세요. 단일 프로젝트 사용자는 default 보드만 쓰면 되며, 이 문서 섹션 밖에서는 "보드"라는 개념을 의식할 필요가 거의 없습니다.
  • Task - 제목, 선택적 본문, 담당자(프로필 이름) 1명, 상태(triage | todo | ready | running | blocked | done | archived), 선택적 테넌트 네임스페이스, 선택적 멱등성 키(재시도되는 자동화를 중복 제거하기 위한 키)를 가진 행입니다.
  • Link - 부모와 자식 의존성을 기록하는 task_links 행입니다. 디스패처는 모든 부모가 done이 되면 todo -> ready로 승격합니다.
  • Comment - 에이전트 간 프로토콜입니다. 에이전트와 사람이 댓글을 추가합니다. 워커가 새로 시작되거나 다시 시작되면 전체 댓글 스레드를 컨텍스트 일부로 읽습니다.
  • Workspace - 워커가 작업하는 디렉터리입니다. 세 가지 종류가 있습니다.
    • scratch(기본값) - ~/.hermes/kanban/workspaces/<id>/ 아래 새 임시 디렉터리입니다. default가 아닌 보드에서는 ~/.hermes/kanban/boards/<slug>/workspaces/<id>/ 아래에 생깁니다.
    • dir:<path> - 이미 존재하는 공유 디렉터리입니다. Obsidian vault, 메일 운영 디렉터리, 계정별 폴더 등에 사용할 수 있습니다. 반드시 절대 경로여야 합니다. dir:../tenants/foo/ 같은 상대 경로는 dispatch 단계에서 거부됩니다. 상대 경로는 디스패처가 어떤 CWD에서 실행 중인지에 따라 달라져 모호하고, confused deputy 유형의 탈출 경로가 될 수 있기 때문입니다. 그 외에는 경로를 신뢰합니다. 사용자의 컴퓨터와 파일시스템이고, 워커는 사용자의 uid로 실행됩니다. Kanban은 단일 호스트를 전제로 한 신뢰된 로컬 사용자 위협 모델입니다.
    • worktree - 코딩 작업용 git worktree입니다. .worktrees/<id>/ 아래에 만들어지며, 워커 쪽의 git worktree add가 생성합니다.
  • Dispatcher - 오래 실행되는 루프입니다. N초마다(기본값 60초) 오래된 claim을 회수하고, 워커 프로세스가 사라졌지만 TTL이 아직 끝나지 않은 crash 상태를 회수하며, 준비된 작업을 승격하고, 원자적으로 claim한 뒤, 지정된 프로필을 시작합니다. 기본적으로 게이트웨이 안에서 실행됩니다(kanban.dispatch_in_gateway: true). 하나의 디스패처가 tick마다 모든 보드를 sweep합니다. 워커는 HERMES_KANBAN_BOARD가 고정된 상태로 시작되므로 다른 보드를 볼 수 없습니다. 같은 작업에서 kanban.failure_limit회 연속 spawn 실패가 나면(기본값 2), 디스패처가 마지막 오류를 reason으로 작업을 자동 block합니다. 존재하지 않는 프로필, mount할 수 없는 작업 공간 같은 문제로 무한 재시도되는 일을 막기 위한 장치입니다.
  • Tenant - 보드 안에서 쓰는 선택적 문자열 네임스페이스입니다. 하나의 전문가 플릿이 여러 사업체를 처리한다면 --tenant business-a처럼 테넌트를 붙이고, 작업 공간 경로와 메모리 키 접두사로 데이터를 분리할 수 있습니다. 테넌트는 소프트 필터이고, 보드가 하드 격리 경계입니다.

Boards (multi-project)

보드는 서로 관련 없는 작업 흐름을 분리합니다. 프로젝트, 저장소, 도메인마다 하나의 독립 큐를 둘 수 있습니다. 새 설치에는 default라는 보드가 정확히 하나 있습니다. 하위 호환성을 위해 이 보드의 DB는 ~/.hermes/kanban.db에 있습니다. 작업 흐름이 하나뿐인 사용자는 보드 기능을 몰라도 됩니다. 이 기능은 opt-in입니다.

보드별 격리는 명확합니다.

  • 보드마다 별도의 SQLite DB를 사용합니다(~/.hermes/kanban/boards/<slug>/kanban.db).
  • 보드마다 별도의 workspaces/logs/ 디렉터리를 사용합니다.
  • 어떤 작업을 위해 시작된 워커는 자신의 보드에 있는 작업만 봅니다. 디스패처가 자식 환경 변수에 HERMES_KANBAN_BOARD를 설정하고, 워커가 접근하는 모든 kanban_* 도구가 이 값을 읽습니다.
  • 보드를 넘나드는 작업 링크는 허용하지 않습니다. 스키마를 단순하게 유지하기 위한 결정입니다. 정말 프로젝트 간 참조가 필요하다면 자유 텍스트 mention을 쓰고 id를 직접 찾아보세요.

CLI에서 보드 관리하기

# 디스크에 있는 보드를 확인합니다. 새 설치에는 "default"만 보입니다.
hermes kanban boards list

# 새 보드를 만듭니다.
hermes kanban boards create atm10-server \
--name "ATM10 Server" \
--description "Minecraft modded server ops" \
--icon server \
--switch # 선택: 새 보드를 active board로 전환

# active board를 바꾸지 않고 특정 보드에서 동작합니다.
hermes kanban --board atm10-server list
hermes kanban --board atm10-server create "Restart ATM server" --assignee ops

# 이후 호출에 사용할 "current" board를 변경합니다.
hermes kanban boards switch atm10-server
hermes kanban boards show # 지금 active board 확인

# 표시 이름을 바꿉니다. slug는 디렉터리 이름이므로 변경할 수 없습니다.
hermes kanban boards rename atm10-server "ATM10 (Prod)"

# Archive(기본값): 보드 디렉터리를 boards/_archived/<slug>-<ts>/로 이동합니다.
# 디렉터리를 다시 옮기면 복구할 수 있습니다.
hermes kanban boards rm atm10-server

# Hard delete: 보드 디렉터리를 `rm -rf`로 삭제합니다. 복구할 수 없습니다.
hermes kanban boards rm atm10-server --delete

보드를 결정하는 순서는 다음과 같습니다. 위에 있을수록 우선순위가 높습니다.

  1. CLI 호출의 명시적 --board &lt;slug&gt;
  2. HERMES_KANBAN_BOARD 환경 변수. 디스패처가 워커를 시작할 때 설정하므로 워커가 다른 보드를 볼 수 없습니다.
  3. hermes kanban boards switch가 저장한 slug인 ~/.hermes/kanban/current
  4. default

Slug는 검증됩니다. 소문자 영숫자, 하이픈, 밑줄만 허용되고, 길이는 1-64자이며, 영숫자로 시작해야 합니다. 대문자 입력은 자동으로 소문자로 바뀝니다. 슬래시, 공백, 점, .. 같은 다른 값은 CLI 계층에서 거부되어 path traversal 방식으로 보드 이름을 조작할 수 없습니다.

대시보드에서 보드 관리하기

hermes dashboard의 Kanban 탭은 보드가 둘 이상 존재하거나 어떤 보드에 작업이 하나라도 있으면 상단에 보드 전환기를 표시합니다. 보드가 하나뿐인 사용자는 작은 + New board 버튼만 보며, 필요해질 때까지 전환기는 숨겨집니다.

  • Board dropdown - active board를 선택합니다. 선택값은 브라우저 localStorage에 저장되므로 새로고침해도 유지됩니다. 이 값은 터미널에서 열린 CLI의 current 포인터를 몰래 바꾸지 않습니다.
  • + New board - slug, 표시 이름, 설명, 아이콘을 입력하는 모달을 엽니다. 새 보드로 자동 전환할지 선택할 수 있습니다.
  • Archive - default가 아닌 보드에서만 보입니다. 확인 후 보드 디렉터리를 boards/_archived/로 이동합니다.

모든 대시보드 API 엔드포인트는 보드 범위 지정을 위해 ?board=&lt;slug&gt;를 받습니다. Events WebSocket은 연결 시점의 보드에 고정됩니다. UI에서 보드를 바꾸면 새 보드를 향해 새 WS 연결을 엽니다.

빠른 시작

아래 명령은 사용자가 보드를 준비하고 작업을 만드는 과정입니다. 작업에 담당자가 지정되면 디스패처가 해당 프로필을 워커로 시작합니다. 그 뒤부터는 모델이 CLI 명령이 아니라 kanban_* 도구 호출로 작업을 진행합니다. 자세한 내용은 워커가 보드와 상호작용하는 방식을 참고하세요.

# 1. 보드를 만듭니다(사용자)
hermes kanban init

# 2. 게이트웨이를 시작합니다. 게이트웨이 안에 내장 디스패처가 있습니다.
hermes gateway start

# 3. 작업을 만듭니다(사용자 또는 kanban_create를 쓰는 오케스트레이터 에이전트)
hermes kanban create "research AI funding landscape" --assignee researcher

# 4. 활동을 실시간으로 봅니다(사용자)
hermes kanban watch

# 5. 보드 상태를 확인합니다(사용자)
hermes kanban list
hermes kanban stats

디스패처가 t_abcd를 잡고 researcher 프로필을 시작하면, 그 워커의 모델이 가장 먼저 하는 일은 kanban_show()를 호출해 자신의 작업을 읽는 것입니다. hermes kanban show t_abcd를 실행하지 않습니다.

게이트웨이에 내장된 디스패처(기본값)

디스패처는 게이트웨이 프로세스 안에서 실행됩니다. 따로 설치하거나 별도 서비스를 관리할 필요가 없습니다. 게이트웨이가 떠 있으면 ready 작업은 다음 tick에서 잡힙니다. 기본 tick은 60초입니다.

# config.yaml
kanban:
dispatch_in_gateway: true # default
dispatch_interval_seconds: 60 # default

디버깅할 때는 HERMES_KANBAN_DISPATCH_IN_GATEWAY=0 환경 변수로 이 설정을 런타임에 override할 수 있습니다. 일반 게이트웨이 supervision이 그대로 적용됩니다. hermes gateway start를 직접 실행하거나, 게이트웨이 문서에 나온 것처럼 systemd user unit으로 연결하세요. 게이트웨이가 실행 중이 아니면 ready 작업은 게이트웨이가 뜰 때까지 그대로 남아 있습니다. hermes kanban create는 생성 시 이 상황을 경고합니다.

별도 프로세스로 hermes kanban daemon을 실행하는 방식은 deprecated입니다. 게이트웨이를 사용하세요. Headless host 정책상 장기 실행 서비스를 돌릴 수 없는 등 정말 게이트웨이를 쓸 수 없는 경우를 위해 --force escape hatch가 한 릴리스 주기 동안 기존 standalone daemon을 유지합니다. 하지만 같은 kanban.db를 향해 게이트웨이 내장 디스패처와 standalone daemon을 동시에 돌리면 claim race가 생기므로 지원하지 않습니다.

Idempotent create(자동화/웹훅용)

# 첫 호출은 작업을 만듭니다. 이후 같은 key로 다시 호출하면
# 중복 생성하지 않고 기존 작업 id를 반환합니다.
hermes kanban create "nightly ops review" \
--assignee ops \
--idempotency-key "nightly-ops-$(date -u +%Y-%m-%d)" \
--json

Bulk CLI verbs

모든 lifecycle verb는 여러 id를 받을 수 있으므로 한 번에 batch를 정리할 수 있습니다.

hermes kanban complete t_abc t_def t_hij --result "batch wrap"
hermes kanban archive t_abc t_def t_hij
hermes kanban unblock t_abc t_def
hermes kanban block t_abc "need input" --ids t_def t_hij

워커가 보드와 상호작용하는 방식

워커는 hermes kanban을 셸로 실행하지 않습니다. 디스패처가 워커를 시작할 때 자식 환경 변수에 HERMES_KANBAN_TASK=t_abcd를 설정하고, 이 환경 변수가 모델 스키마에 전용 kanban 툴셋을 켭니다. 같은 툴셋은 toolsets 설정에서 kanban을 켠 오케스트레이터 프로필에도 제공됩니다. 이 도구들은 CLI와 마찬가지로 Python kanban_db 계층을 통해 보드를 직접 읽고 수정합니다. 실행 중인 워커는 다른 도구처럼 이 도구들을 호출하며, hermes kanban CLI를 볼 필요도 없습니다.

Tool목적필수 params
kanban_show현재 작업을 읽습니다. title, body, 이전 시도, 부모 작업의 인계 기록, 댓글, 미리 포맷된 전체 worker_context를 포함합니다. 기본값은 환경 변수의 작업 id입니다.없음
kanban_listassignee, status, tenant, archived visibility, limit filter로 작업 요약을 나열합니다. 오케스트레이터가 보드의 작업을 찾을 때 사용합니다.없음
kanban_completesummary + metadata 형태의 구조화된 인계 기록으로 작업을 끝냅니다.summary / result 중 하나 이상
kanban_block사람의 입력이 필요하다고 에스컬레이션하고 reason을 남깁니다.reason
kanban_heartbeat긴 작업 중 살아 있음을 알립니다. 순수한 부수 효과만 있습니다.없음
kanban_comment작업 스레드에 오래 보존되는 메모를 추가합니다.task_id, body
kanban_create오케스트레이터가 assignee, 선택적 parents, skills 등을 가진 자식 작업으로 fan-out합니다.title, assignee
kanban_link오케스트레이터가 나중에 parent_id -> child_id 의존성 edge를 추가합니다.parent_id, child_id
kanban_unblock오케스트레이터가 막힌 작업을 ready로 되돌립니다.task_id

일반적인 워커 turn은 다음과 같습니다.

# 모델의 tool call 순서:
kanban_show() # args 없음. HERMES_KANBAN_TASK 사용
# 모델은 반환된 worker_context를 읽고 terminal/file 도구로 실제 작업을 수행합니다.
kanban_heartbeat(note="halfway through - 4 of 8 files transformed")
# 추가 작업
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},
)

오케스트레이터 워커는 대신 작업을 fan-out합니다.

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"], # 둘 다 complete되면 ready로 승격
body="one-pager, 300 words, neutral tone",
)
kanban_complete(summary="decomposed into 2 research tasks + 1 writer; linked dependencies")

kanban_list, kanban_create, kanban_link, kanban_unblock, 다른 작업에 대한 kanban_comment 같은 "(Orchestrators)" 도구도 같은 툴셋으로 제공됩니다. 이 관례는 kanban-orchestrator 스킬이 강제합니다. 워커 프로필은 fan-out하거나 관련 없는 작업을 라우팅하지 않고, 오케스트레이터 프로필은 구현 작업을 직접 수행하지 않습니다. 디스패처가 시작한 워커는 destructive lifecycle operation에 대해서는 여전히 작업 범위로 제한되며, 관련 없는 작업을 mutate할 수 없습니다.

hermes kanban 셸 실행 대신 도구를 쓰는가

이유는 세 가지입니다.

  1. 백엔드 이식성. Terminal 도구가 Docker, Modal, Singularity, SSH 같은 원격 백엔드를 가리키는 워커는 컨테이너 안에서 hermes kanban complete를 실행하게 됩니다. 그곳에는 hermes가 설치되어 있지 않거나 ~/.hermes/kanban.db가 mount되어 있지 않을 수 있습니다. Kanban 도구는 에이전트 자신의 Python 프로세스에서 실행되므로 terminal backend와 관계없이 항상 ~/.hermes/kanban.db에 접근합니다.
  2. 셸 quoting 취약성 제거. --metadata '{"files": [...]}'를 shlex와 argparse를 거쳐 넘기는 방식은 언제든 발목을 잡을 수 있습니다. 구조화된 tool args는 이 문제를 통째로 피합니다.
  3. 더 나은 오류. Tool result는 모델이 추론할 수 있는 구조화 JSON입니다. stderr 문자열을 파싱할 필요가 없습니다.

일반 세션에는 schema footprint가 없습니다. 보통의 hermes chat 세션은 스키마에 kanban_* 도구가 하나도 없습니다. 각 도구의 check_fnHERMES_KANBAN_TASK가 설정된 경우에만 True를 반환합니다. 이 값은 디스패처가 프로세스를 시작했을 때만 생깁니다. Kanban을 쓰지 않는 사용자에게 tool bloat를 만들지 않습니다.

kanban-workerkanban-orchestrator 스킬은 모델에게 어떤 도구를 어떤 순서로 호출해야 하는지 알려 줍니다.

kanban_complete(summary=..., metadata={...})는 의도적으로 유연합니다. summary는 사람이 읽는 마무리 요약이고, metadata는 downstream agent, reviewer, dashboard가 prose를 긁어내지 않고 재사용할 수 있는 machine-readable handoff입니다.

엔지니어링과 리뷰 작업에서는 다음 선택적 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"]
}

이 key들은 관례일 뿐 schema 요구사항은 아닙니다. 중요한 점은 모든 워커가 다음 독자가 네 가지 질문에 빠르게 답할 수 있을 만큼의 근거를 남기는 것입니다.

  1. 무엇이 바뀌었는가?
  2. 어떻게 검증했는가?
  3. 실패하면 unblock이나 retry에 무엇이 필요한가?
  4. 어떤 risk가 의도적으로 남아 있는가?

Secret, raw log, token, OAuth 자료, 관련 없는 transcript는 metadata에 넣지 마세요. pointer와 summary를 저장하세요. 파일이나 테스트가 없는 작업이라면 summary에 그 사실을 명시하고, source URL, issue id, manual review step처럼 실제로 존재하는 evidence를 metadata에 넣으세요.

워커 스킬

Kanban 작업을 처리할 수 있어야 하는 프로필은 kanban-worker 스킬을 load해야 합니다. 이 스킬은 CLI 명령이 아니라 tool call로 전체 lifecycle을 수행하는 법을 워커에게 알려 줍니다.

  1. Spawn되면 kanban_show()를 호출해 title, body, 부모 작업의 인계 기록, 이전 시도, 전체 댓글 스레드를 읽습니다.
  2. Terminal 도구로 cd $HERMES_KANBAN_WORKSPACE한 뒤 그곳에서 작업합니다.
  3. 긴 작업 중에는 몇 분마다 kanban_heartbeat(note="...")를 호출합니다.
  4. 끝나면 kanban_complete(summary="...", metadata={...})로 완료합니다. 막히면 kanban_block(reason="...")을 호출합니다.

kanban-worker는 bundled skill입니다. 설치와 업데이트 때 모든 프로필에 동기화되므로 별도의 Skills Hub 설치 단계가 없습니다. Kanban 워커로 쓸 프로필, 예를 들어 researcher, writer, ops에 이 스킬이 있는지 확인하세요.

hermes -p <your-worker-profile> skills list | grep kanban-worker

Bundled copy가 빠져 있다면 해당 프로필에 복원합니다.

hermes -p <your-worker-profile> skills reset kanban-worker --restore

디스패처는 모든 워커를 시작할 때 --skills kanban-worker도 자동으로 넘깁니다. 그래서 프로필의 기본 skills config에 포함되어 있지 않아도 워커는 항상 이 pattern library를 사용할 수 있습니다.

특정 작업에 extra skill 고정하기

어떤 작업 하나만 담당자 프로필의 기본 컨텍스트에 없는 specialist context가 필요할 때가 있습니다. 예를 들어 번역 작업에는 translation 스킬이 필요하고, 리뷰 작업에는 github-code-review, 보안 audit에는 security-pr-audit이 필요할 수 있습니다. 매번 담당자 프로필을 수정하지 말고, 작업에 스킬을 직접 붙이세요.

오케스트레이터 에이전트에서 다른 에이전트로 작업을 라우팅하는 일반적인 경우에는 kanban_create 도구의 skills 배열을 사용합니다.

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"],
)

사람이 CLI나 슬래시 명령으로 만들 때는 스킬마다 --skill을 반복합니다.

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

대시보드에서는 inline create form의 skills 필드에 쉼표로 구분해 입력합니다.

이 스킬들은 built-in kanban-worker추가됩니다. 디스패처는 built-in을 포함해 각 스킬마다 --skills &lt;name&gt; 플래그를 하나씩 emit하므로 워커는 모두 load된 상태로 시작합니다. 스킬 이름은 담당자 프로필에 실제로 설치된 스킬과 일치해야 합니다. hermes skills list로 사용 가능한 스킬을 확인하세요. 런타임 설치는 하지 않습니다.

오케스트레이터 스킬

잘 동작하는 오케스트레이터는 일을 직접 하지 않습니다. 사용자의 목표를 작업으로 분해하고, 링크를 걸고, 준비해 둔 프로필 중 하나에 각 작업을 배정한 뒤 물러납니다. kanban-orchestrator 스킬은 이를 tool-call pattern으로 인코딩합니다. 직접 하고 싶은 유혹을 막는 규칙, Step-0 프로필 discovery prompt, kanban_create / kanban_link / kanban_comment 중심의 decomposition playbook이 포함됩니다. 디스패처는 알 수 없는 assignee 이름에서 조용히 실패하므로, 오케스트레이터는 모든 카드를 실제로 사용자의 컴퓨터에 존재하는 프로필에 배정해야 합니다.

표준 오케스트레이터 turn은 다음과 같습니다. 두 researcher가 병렬로 조사하고 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"], # 두 researcher가 complete되면 'ready'로 승격
body="one-pager, neutral tone, cite sources inline",
) # -> t_w1
# 나중에 cross-cutting dependency를 발견하면 작업을 다시 만들지 않고 link만 추가합니다.
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",
)

kanban-orchestrator도 bundled skill입니다. 설치와 업데이트 때 각 프로필에 동기화되므로 별도 Skills Hub 설치 단계가 없습니다. 오케스트레이터 프로필에 존재하는지 확인하세요.

hermes -p orchestrator skills list | grep kanban-orchestrator

Bundled copy가 빠져 있다면 해당 프로필에 복원합니다.

hermes -p orchestrator skills reset kanban-orchestrator --restore

가장 좋은 결과를 얻으려면 오케스트레이터 프로필의 toolsets를 보드 운영(kanban, gateway, memory)으로 제한하세요. 그러면 오케스트레이터가 실수로 구현 작업을 직접 수행하려 해도 실제로 수행할 수 없습니다.

대시보드(GUI)

/kanban CLI와 슬래시 명령만으로도 headless하게 보드를 운영할 수 있습니다. 하지만 사람이 중간에 개입하는 흐름에는 시각적 보드가 더 적합한 경우가 많습니다. Triage, 프로필 간 감독, 댓글 스레드 읽기, column 간 card 이동 같은 작업이 그렇습니다. Hermes는 이를 plugins/kanban/에 있는 bundled dashboard plugin으로 제공합니다. Core feature도 아니고 별도 service도 아닙니다. 구조는 Dashboard 확장에 설명된 모델을 따릅니다.

다음처럼 엽니다.

hermes kanban init      # 최초 1회: kanban.db가 없으면 생성
hermes dashboard # nav에서 "Skills" 다음에 "Kanban" 탭이 나타납니다.

플러그인이 제공하는 것

  • Kanban 탭은 status마다 하나의 column을 보여 줍니다. 기본 column은 triage, todo, ready, running, blocked, done이며, toggle을 켜면 archived도 표시합니다.
    • triage는 rough idea를 잠시 세워 두는 column입니다. Specifier가 이 idea를 구체화할 것으로 기대합니다. hermes kanban create --triage로 만들거나 Triage column inline create에서 만든 작업은 여기에 들어가며, 사람이 todo / ready로 승격하거나 specifier가 처리할 때까지 디스패처는 건드리지 않습니다. hermes kanban specify &lt;id&gt;를 실행하면 auxiliary LLM이 triage 작업을 목표, 접근 방식, acceptance criteria가 담긴 구체적 spec(title + body)으로 확장하고 한 번에 todo로 승격합니다. --all은 모든 triage 작업을 한 번에 sweep합니다. 사용할 specifier 모델은 config.yamlauxiliary.triage_specifier에서 설정합니다.
  • Card에는 작업 id, title, priority badge, tenant tag, assigned profile, comment/link count가 표시됩니다. 자식 작업이 있으면 완료된 자식 수를 N/M 형식으로 보여 주는 progress pill도 표시되고, 생성 시각은 "created N ago"로 나타납니다. Card별 checkbox로 multi-select할 수 있습니다.
  • Running 내부의 profile별 lane - toolbar checkbox를 켜면 Running column을 assignee별 subgroup으로 나누어 볼 수 있습니다.
  • WebSocket live updates - plugin은 append-only task_events table을 짧은 poll interval로 tail합니다. 어떤 프로필, CLI, 게이트웨이, 다른 dashboard tab에서 변경하더라도 보드가 즉시 반영합니다. Event가 burst로 들어와도 reload는 debounce되어 한 번의 refetch로 처리됩니다.
  • Drag-drop - card를 column 사이로 옮겨 status를 바꿉니다. Drop은 PATCH /api/plugins/kanban/tasks/:id를 보내며, CLI와 같은 kanban_db code path를 통과합니다. 그래서 세 표면은 서로 다른 상태가 될 수 없습니다. done, archived, blocked처럼 destructive한 status로 옮길 때는 confirmation prompt가 뜹니다. Touch device에서는 pointer 기반 fallback을 사용하므로 tablet에서도 쓸 수 있습니다.
  • Inline create - column header의 +를 눌러 title, assignee, priority, 선택적 parent task를 입력합니다. Parent task는 기존 task dropdown에서 고릅니다. Triage column에서 만들면 새 작업이 자동으로 triage에 놓입니다.
  • Multi-select와 bulk actions - card를 shift/ctrl-click하거나 checkbox를 tick해 selection에 추가합니다. 상단에 bulk action bar가 나타나며 batch status transition, archive, reassign을 할 수 있습니다. Reassign은 profile dropdown 또는 "(unassign)"으로 처리합니다. Destructive batch는 먼저 확인합니다. 특정 id만 실패하면 나머지를 중단하지 않고 per-id partial failure를 보고합니다.
  • Card 클릭 - shift/ctrl 없이 card를 클릭하면 side drawer가 열립니다. Escape나 바깥 클릭으로 닫을 수 있습니다.
    • Editable title - heading을 클릭해 이름을 바꿉니다.
    • Editable assignee / priority - meta row를 클릭해 수정합니다.
    • Editable description - 기본적으로 markdown-rendered로 보입니다. Heading, bold, italic, inline code, fenced code, http(s) / mailto: link, bullet list를 지원합니다. "edit" 버튼을 누르면 textarea로 바뀝니다. Markdown renderer는 작고 XSS-safe합니다. 모든 substitution은 HTML-escaped input 위에서 실행되고, link는 http(s) / mailto:만 통과하며, 항상 target="_blank"rel="noopener noreferrer"를 설정합니다.
    • Dependency editor - parent와 child를 chip list로 보여 주고, 각 chip의 x로 unlink할 수 있습니다. 모든 다른 task를 대상으로 하는 dropdown으로 새 parent나 child를 추가합니다. Cycle attempt는 서버에서 명확한 메시지와 함께 거부됩니다.
    • Status action row - triage, ready, running, block, unblock, complete, archive action을 제공합니다. Destructive transition은 confirm prompt를 거칩니다. Triage column의 card에는 Specify 버튼도 나타납니다. 이 버튼은 config.yamlauxiliary.triage_specifier auxiliary LLM을 호출해 one-liner를 목표, 접근 방식, acceptance criteria가 있는 구체적 spec으로 확장하고 todo로 승격합니다. 같은 동작은 CLI(hermes kanban specify &lt;id&gt; / --all), gateway platform(/kanban specify &lt;id&gt;), REST API(POST /api/plugins/kanban/tasks/:id/specify)에서도 가능합니다.
    • Result section도 markdown-rendered입니다. Comment thread는 Enter-to-submit을 지원하고, 마지막 20개 event를 보여 줍니다.
  • Toolbar filters - free-text search, tenant dropdown(config.yamldashboard.kanban.default_tenant가 기본값), assignee dropdown, "show archived" toggle, "lanes by profile" toggle, 다음 60초 tick을 기다리지 않게 해 주는 Nudge dispatcher 버튼이 있습니다.

시각적 목표는 Linear / Fusion에 익숙한 레이아웃입니다. Dark theme, count가 있는 column header, colored status dot, priority와 tenant용 pill chip을 사용합니다. Plugin은 --color-*, --radius, --font-mono 같은 theme CSS var만 읽으므로 활성 dashboard theme에 맞춰 자동으로 reskin됩니다.

Architecture

GUI는 자체 domain logic을 갖지 않는 read-through-the-DB + write-through-kanban_db 계층입니다.

+------------------------+        WebSocket tails task_events
| React SPA (plugin) |<-------------------------------------+
| HTML5 drag-and-drop | |
+-----------+------------+ |
| REST over fetchJSON |
v |
+------------------------+ writes call kanban_db.* |
| FastAPI router | directly - same code path |
| plugins/kanban/ | used by CLI /kanban verbs |
| dashboard/plugin_api.py| |
+-----------+------------+ |
| |
v |
+------------------------+ append task_events ------------+
| ~/.hermes/kanban.db |
| (WAL, shared) |
+------------------------+

REST 표면

모든 라우트는 /api/plugins/kanban/ 아래에 mount되고, 대시보드의 일회성 세션 토큰으로 보호됩니다.

MethodPathPurpose
GET/board?tenant=&lt;name&gt;&include_archived=...Status column별로 묶은 전체 보드와 filter dropdown용 tenant, assignee 목록
GET/tasks/:id작업, 댓글, 이벤트, 링크
POST/tasks생성. kanban_db.create_task를 감싸며 triage: bool, parents: [id, ...]를 받습니다.
PATCH/tasks/:idStatus / assignee / priority / title / body / result 수정
POST/tasks/bulkids의 모든 작업에 같은 patch(status / archive / assignee / priority)를 적용합니다. id별 실패는 다른 형제 작업을 중단하지 않고 보고됩니다.
POST/tasks/:id/comments댓글 추가
POST/tasks/:id/specifyTriage specifier 실행. Auxiliary LLM이 작업 본문을 구체화하고 triage에서 todo로 승격합니다. {ok, task_id, reason, new_title}를 반환합니다. "not in triage" / no aux client / LLM error에서는 사람이 읽을 수 있는 reason과 함께 ok=false를 반환하지만 4xx가 아니라 200입니다.
POST/links의존성 추가(parent_id -> child_id)
DELETE/links?parent_id=...&child_id=...의존성 제거
POST/dispatch?max=...&dry_run=...디스패처를 nudge해 60초 wait를 건너뜁니다.
GET/configconfig.yamldashboard.kanban preference를 읽습니다. default_tenant, lane_by_profile, include_archived_by_default, render_markdown이 포함됩니다.
WS/events?since=&lt;event_id&gt;task_events 행 live stream

모든 handler는 얇은 wrapper입니다. Plugin은 Python 약 700줄(router, WebSocket tail, bulk batcher, config reader)이며 새 business logic을 추가하지 않습니다. 작은 _conn() helper가 모든 read/write 때 kanban.db를 자동 초기화하므로, 사용자가 대시보드를 먼저 열든 REST API를 직접 호출하든 hermes kanban init을 먼저 실행하든 새 설치에서 바로 동작합니다.

대시보드 설정

~/.hermes/config.yamldashboard.kanban 아래에서 다음 key를 설정하면 tab의 기본값이 바뀝니다. Plugin은 load 시 GET /config로 이 값을 읽습니다.

dashboard:
kanban:
default_tenant: acme # tenant filter를 미리 선택
lane_by_profile: true # "lanes by profile" toggle 기본값
include_archived_by_default: false
render_markdown: true # false면 plain <pre> 렌더링

각 key는 선택 사항이며, 없으면 예시에 보인 기본값으로 fallback합니다.

보안 모델

대시보드의 HTTP auth middleware는 명시적으로 /api/plugins/를 건너뜁니다. Plugin route는 대시보드가 기본적으로 localhost에 bind된다는 전제로 설계상 unauthenticated입니다. 즉 Kanban REST 표면은 같은 host의 모든 process에서 접근할 수 있습니다.

WebSocket은 한 단계를 더 수행합니다. Browser는 upgrade request에 Authorization을 설정할 수 없으므로 대시보드의 일회성 세션 토큰을 ?token=... query parameter로 요구합니다. 이는 browser 안 PTY bridge가 쓰는 pattern과 같습니다.

hermes dashboard --host 0.0.0.0으로 실행하면 Kanban을 포함한 모든 plugin route가 network에서 접근 가능해집니다. 공유 host에서는 그렇게 하지 마세요. 보드에는 작업 본문, 댓글, 작업 공간 경로가 들어 있습니다. 공격자가 이 route에 접근하면 협업 표면 전체를 읽을 수 있고, 작업을 생성, 재배정, archive할 수도 있습니다.

~/.hermes/kanban.db의 작업은 의도적으로 프로필에 독립적입니다. 이것이 조율 primitive입니다. hermes -p &lt;profile&gt; dashboard로 대시보드를 열어도 host의 다른 프로필이 만든 작업이 보입니다. 같은 OS 사용자가 모든 프로필을 소유한다는 전제지만, 여러 persona가 공존한다면 알고 있어야 합니다.

Live updates

task_events는 단조 증가하는 id를 가진 append-only SQLite table입니다. WebSocket endpoint는 각 client의 last-seen event id를 들고 있다가 새 row가 들어오면 push합니다. Event burst가 도착하면 frontend는 매우 저렴한 board endpoint를 reload합니다. 모든 event kind를 local state patch로 처리하려고 하는 것보다 단순하고 정확합니다. WAL mode 덕분에 read loop는 디스패처의 BEGIN IMMEDIATE claim transaction을 막지 않습니다.

확장하기

Plugin은 표준 Hermes dashboard plugin contract를 사용합니다. 전체 manifest reference, shell slot, page-scoped slot, Plugin SDK는 Dashboard 확장을 참고하세요. Extra column, custom card chrome, tenant-filtered layout, 전체 tab.override replacement까지 이 plugin을 fork하지 않고 표현할 수 있습니다.

제거하지 않고 비활성화하려면 config.yamldashboard.plugins.kanban.enabled: false를 추가하거나 plugins/kanban/dashboard/manifest.json을 삭제하세요.

범위 경계

GUI는 의도적으로 얇습니다. Plugin이 하는 모든 일은 CLI에서도 가능합니다. Plugin은 사람이 쓰기 편하게 만드는 역할만 합니다. Auto-assignment, budget, governance gate, org-chart view는 user-space에 남아 있습니다. Router profile, 다른 plugin, tools/approval.py 재사용처럼 design spec의 out-of-scope 섹션에 나열된 방식으로 처리하세요.

CLI 명령 참조

이 표면은 사용자, script, cron, 대시보드가 보드를 조작할 때 사용합니다. 디스패처 안에서 실행 중인 워커는 같은 작업에 Kanban 도구 표면을 사용합니다. 여기의 CLI와 저기의 도구는 모두 kanban_db를 통과하므로 구조적으로 같은 상태를 봅니다.

hermes kanban init                                     # kanban.db 생성 + daemon hint 출력
hermes 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> # 'none'이면 unassign
hermes 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 - 여러 id를 받습니다.
hermes kanban complete <id>... [--result "..."]
hermes kanban block <id> "<reason>" [--ids <id>...]
hermes kanban unblock <id>...
hermes kanban archive <id>...

hermes kanban tail <id> # 단일 작업 event stream follow
hermes kanban watch [--assignee P] [--tenant T] # 모든 event를 terminal로 live stream
[--kinds completed,blocked,...] [--interval SECS]
hermes kanban heartbeat <id> [--note "..."] # 긴 작업 중 worker liveness signal
hermes kanban runs <id> [--json] # attempt history(시도당 한 row)
hermes kanban assignees [--json] # disk의 profile + assignee별 task count
hermes kanban dispatch [--dry-run] [--max N] # one-shot pass
[--failure-limit N] [--json]
hermes kanban daemon --force # DEPRECATED - standalone dispatcher. `hermes gateway start` 사용
[--failure-limit N] [--pidfile PATH] [-v]
hermes kanban stats [--json] # status별 + assignee별 count
hermes kanban log <id> [--tail BYTES] # ~/.hermes/kanban/logs/의 worker log
hermes kanban notify-subscribe <id> # gateway bridge hook(/kanban이 사용)
--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> # worker가 보는 context
hermes kanban specify [<id> | --all] [--tenant T] # triage one-liner를 full spec으로 확장
[--author NAME] [--json] # 한 뒤 todo로 승격
hermes kanban gc [--event-retention-days N] # workspaces + old events + old logs
[--log-retention-days N]

모든 명령은 interactive CLI와 messaging gateway에서 슬래시 명령으로도 사용할 수 있습니다. 아래 /kanban 슬래시 명령을 참고하세요.

/kanban 슬래시 명령

모든 hermes kanban &lt;action&gt; verb는 interactive hermes chat 세션과 gateway platform(Telegram, Discord, Slack, WhatsApp, Signal, Matrix, Mattermost, email, SMS)에서 /kanban &lt;action&gt;으로도 사용할 수 있습니다. 두 표면은 같은 hermes_cli.kanban.run_slash() entry point를 호출하며, 이 함수는 hermes kanban argparse tree를 재사용합니다. 그래서 argument surface, flag, output format이 CLI, /kanban, hermes kanban에서 동일합니다. Chat을 떠나지 않고 보드를 조작할 수 있습니다.

/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 # triage one-liner를 실제 spec으로 확장
/kanban specify --all --tenant engineering # 한 tenant의 모든 triage 작업 sweep

여러 단어로 된 argument는 shell에서처럼 quote합니다. run_slash가 나머지 줄을 shlex.split으로 parse하므로 "..."'...'가 모두 동작합니다.

Mid-run usage: /kanban은 running-agent guard를 우회합니다

Gateway는 보통 agent가 아직 생각 중일 때 slash command와 사용자 메시지를 queue합니다. 첫 번째 turn이 끝나기 전에 두 번째 turn을 실수로 시작하지 않게 하기 위한 guard입니다. /kanban은 이 guard에서 명시적으로 제외됩니다. 보드는 실행 중인 agent state가 아니라 ~/.hermes/kanban.db에 있기 때문입니다. 그래서 읽기(list, show, context, tail, watch, stats, runs)와 쓰기(comment, unblock, block, assign, archive, create, link 등)가 mid-turn에도 즉시 통과합니다.

이 분리가 핵심입니다.

  • 워커가 peer를 기다리며 blocked된 경우, 휴대폰에서 /kanban unblock t_abcd를 보내면 디스패처가 다음 tick에서 peer를 잡습니다. Blocked worker 자체는 interrupt되지 않고, 단지 blocked 상태가 풀립니다.
  • 사람이 필요한 컨텍스트를 발견하면 /kanban comment t_xyz "use the 2026 schema, not 2025"를 보내 작업 스레드에 남길 수 있습니다. 그 작업의 다음 run은 kanban_show()에서 이 comment를 읽습니다.
  • 오케스트레이터를 멈추지 않고 fleet 상태를 보고 싶다면 /kanban list --mine 또는 /kanban stats로 main conversation을 건드리지 않고 보드를 검사할 수 있습니다.

/kanban create의 자동 구독(gateway only)

Gateway에서 /kanban create "..."로 작업을 만들면, 그 작업의 terminal event(completed, blocked, gave_up, crashed, timed_out)에 원래 chat(platform + chat id + thread id)이 자동으로 subscribe됩니다. Polling하거나 작업 id를 기억하지 않아도 terminal event마다 한 메시지를 받습니다. completed에서는 워커 result summary의 첫 줄도 함께 옵니다.

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분 뒤

bot> t_9fc1a3 completed by transcriber
transcribed 42 minutes, saved to podcast/2026-05-04.md

작업이 done 또는 archived에 도달하면 subscription은 자동으로 제거됩니다. --json으로 create를 script하면(machine output) auto-subscribe는 건너뜁니다. Script caller는 /kanban notify-subscribe로 subscription을 명시적으로 관리한다고 보기 때문입니다.

Messaging output truncation

Gateway platform에는 실제 메시지 길이 제한이 있습니다. /kanban list, /kanban show, /kanban tail 출력이 약 3800자를 넘으면 응답은 다음 footer와 함께 잘립니다.

(truncated; use `hermes kanban ...` in your terminal for full output)

CLI 표면에는 이런 제한이 없습니다.

Autocomplete

Interactive CLI에서 /kanban 을 입력하고 Tab을 누르면 built-in subcommand list를 순환합니다. 현재 hint list에는 list, ls, show, create, assign, link, unlink, claim, comment, complete, block, unblock, archive, tail, dispatch, context, init, gc가 있습니다. 위 CLI reference의 나머지 verb(watch, stats, runs, log, assignees, heartbeat, notify-subscribe, notify-list, notify-unsubscribe, daemon)도 동작하지만 아직 autocomplete hint list에는 없습니다.

협업 패턴

보드는 새 primitive 없이 다음 아홉 가지 패턴을 지원합니다.

PatternShapeExample
P1 Fan-out같은 역할의 N개 sibling"research 5 angles in parallel"
P2 Pipeline역할 chain: scout -> editor -> writerdaily brief assembly
P3 Voting / quorumN개 sibling + aggregator 1개3 researchers -> 1 reviewer picks
P4 Long-running journal같은 profile + shared dir + cronObsidian vault
P5 Human-in-the-loopworker blocks -> user comments -> unblockambiguous decisions
P6 @mentionprose 안의 inline routing@reviewer look at this
P7 Thread-scoped workspacethread 안에서 /kanban hereper-project gateway threads
P8 Fleet farming한 profile, N개 subject50 social accounts
P9 Triage specifierrough idea -> triage -> hermes kanban specify가 body 확장 -> todo"turn this one-liner into a spec'd task"

각 패턴의 worked example은 docs/hermes-kanban-v1-spec.pdf를 참고하세요.

Multi-tenant usage

하나의 specialist fleet가 여러 business를 처리한다면 작업마다 tenant를 붙입니다.

hermes kanban create "monthly report" \
--assignee researcher \
--tenant business-a \
--workspace dir:~/tenants/business-a/data/

워커는 $HERMES_TENANT를 받고, memory write를 prefix로 namespace합니다. 보드, 디스패처, 프로필 정의는 공유됩니다. 범위가 나뉘는 것은 데이터뿐입니다.

Gateway notifications

Gateway(Telegram, Discord, Slack 등)에서 /kanban create ...를 실행하면 원래 chat이 새 작업에 자동 subscribe됩니다. Gateway의 background notifier는 몇 초마다 task_events를 poll하고 terminal event(completed, blocked, gave_up, crashed, timed_out)마다 그 chat에 메시지를 보냅니다. Completed task는 워커의 --result 첫 줄도 보내므로 /kanban show를 하지 않고 결과를 볼 수 있습니다.

Script나 cron job이 자신이 시작하지 않은 chat에 알림을 보내고 싶을 때는 CLI에서 subscription을 명시적으로 관리할 수 있습니다.

hermes kanban notify-subscribe t_abcd \
--platform telegram --chat-id 12345678 --thread-id 7
hermes kanban notify-list
hermes kanban notify-unsubscribe t_abcd \
--platform telegram --chat-id 12345678 --thread-id 7

작업이 done 또는 archived에 도달하면 subscription은 자동으로 제거됩니다. 별도 cleanup은 필요 없습니다.

Runs - 시도당 한 행

Task는 논리적 작업 단위이고, run은 그 작업을 실행하려는 한 번의 시도입니다. 디스패처가 ready 작업을 claim하면 task_runs에 행을 만들고 tasks.current_run_id가 그 행을 가리키게 합니다. Attempt가 completed, blocked, crashed, timed_out, spawn-failed, reclaimed 중 하나로 끝나면 run row가 outcome과 함께 닫히고 작업의 pointer가 지워집니다. 세 번 시도된 작업에는 task_runs 행이 세 개 있습니다.

Task row 하나만 계속 mutate하지 않고 두 table을 쓰는 이유는 두 가지입니다. 첫째, 실제 postmortem에는 전체 attempt history가 필요합니다. 예를 들어 "두 번째 reviewer attempt는 approve까지 갔고, 세 번째 attempt가 merge했다" 같은 정보를 잃으면 안 됩니다. 둘째, per-attempt metadata를 걸 깨끗한 장소가 필요합니다. 어떤 file이 바뀌었는지, 어떤 test를 실행했는지, reviewer가 어떤 finding을 남겼는지는 task fact가 아니라 run fact입니다.

Run은 구조화된 인계 기록이 사는 곳이기도 합니다. 워커가 kanban_complete(...)로 작업을 완료할 때 다음 값을 넘길 수 있습니다.

  • summary(tool param) / --summary(CLI) - 사람이 읽는 인계 기록입니다. Run에 저장되고 downstream child는 build_worker_context에서 이를 봅니다.
  • metadata(tool param) / --metadata(CLI) - run에 저장되는 free-form JSON dict입니다. Child는 summary 옆에 직렬화된 형태로 봅니다.
  • result(tool param) / --result(CLI) - task row에 들어가는 짧은 log line입니다. 하위 호환성을 위해 유지되는 legacy field입니다.

Downstream child는 각 parent의 최신 completed run에 있는 summary + metadata를 읽습니다. Retry worker는 자기 작업의 이전 attempt(outcome, summary, error)를 읽어 이미 실패한 경로를 반복하지 않습니다.

# Worker가 실제로 하는 일 - agent loop 안의 tool call:
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",
)

워커가 마무리할 수 없는 작업을 사람이 닫아야 할 때도 같은 인계 기록을 CLI에서 사용할 수 있습니다. 예를 들어 abandoned task나 대시보드에서 수동으로 done 처리한 작업이 그렇습니다.

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}'

# 재시도된 작업의 attempt history 확인:
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

Run은 대시보드의 drawer에 Run History section으로 노출됩니다. Attempt마다 색이 있는 행 하나로 표시됩니다. REST API에서도 GET /api/plugins/kanban/tasks/:idruns[] 배열을 반환합니다. {status: "done", summary, metadata}를 담아 PATCH /api/plugins/kanban/tasks/:id를 호출하면 두 값 모두 kernel로 전달되므로 대시보드의 "mark done" 버튼은 CLI와 동등합니다. task_events 행은 자신이 속한 run_id를 carries하므로 UI가 attempt별로 event를 group할 수 있고, completed event는 payload에 첫 줄 summary를 포함합니다(400자 cap). 덕분에 gateway notifier는 두 번째 SQL round-trip 없이 structured handoff를 렌더링할 수 있습니다.

Bulk close caveat. hermes kanban complete a b c --summary X는 거부됩니다. Structured handoff는 run별 정보이므로 같은 summary를 N개 작업에 복사하는 것은 거의 항상 잘못입니다. --summary / --metadata 없이 bulk close하는 것은 "관리 작업 묶음을 끝냈다" 같은 일반적인 경우를 위해 여전히 동작합니다.

Status change로 reclaimed된 run. 대시보드에서 running task를 running 밖으로 끌어내려 readytodo로 되돌리거나, 아직 running인 작업을 archive하면 in-flight run은 orphan되지 않고 outcome='reclaimed'로 닫힙니다. tasks.current_run_idNULL이면 task_runs row는 항상 terminal state이고, 그 반대도 성립합니다. 이 invariant는 CLI, 대시보드, 디스패처, notifier 전부에서 유지됩니다.

Claim된 적 없는 completion을 위한 synthetic run. 한 번도 claim되지 않은 작업을 complete하거나 block하면 handoff가 사라질 수 있습니다. 예를 들어 사람이 대시보드에서 ready 작업을 summary와 함께 닫거나 CLI 사용자가 hermes kanban complete &lt;ready-task&gt; --summary X를 실행하는 경우입니다. Kernel은 대신 zero-duration run row(started_at == ended_at)를 삽입해 summary / metadata / reason을 보존합니다. Attempt history가 완전하게 유지되고, completed / blocked event의 run_id는 이 row를 가리킵니다.

Live drawer refresh. 대시보드의 WebSocket event stream이 사용자가 현재 보고 있는 작업에 대한 새 event를 보고하면 drawer는 스스로 reload합니다. 이를 위해 per-task event counter가 useEffect dependency list에 들어갑니다. 새 row나 업데이트된 outcome을 보기 위해 drawer를 닫았다가 다시 열 필요가 없습니다.

Forward compatibility

tasks의 nullable column 두 개는 v2 workflow routing을 위해 예약되어 있습니다. workflow_template_id는 이 작업이 어떤 template에 속하는지, current_step_key는 그 template에서 어떤 step이 active인지 나타냅니다. v1 kernel은 이 값들을 routing에 사용하지 않지만 client가 쓸 수 있게 허용합니다. 그래서 v2 release는 추가 schema migration 없이 routing machinery를 넣을 수 있습니다.

Event reference

모든 transition은 task_events에 행을 추가합니다. 각 행에는 optional run_id가 있어 UI가 attempt별로 event를 group할 수 있습니다. Kind는 filtering하기 쉽도록 세 cluster로 나뉩니다. 예를 들어 hermes kanban watch --kinds completed,gave_up,timed_out처럼 사용할 수 있습니다.

Lifecycle - 논리적 작업에 생긴 변화입니다.

KindPayloadWhen
created{assignee, status, parents, tenant}작업이 삽입됨. run_idNULL입니다.
promoted없음모든 parent가 done이 되어 todo -> ready. run_idNULL입니다.
claimed{lock, expires, run_id}디스패처가 spawn을 위해 ready 작업을 원자적으로 claim함.
completed{result_len, summary?}워커가 --result / --summary를 쓰고 작업이 done이 됨. summary는 첫 줄 handoff이며 400자로 제한됩니다. 전체 내용은 run row에 있습니다. Handoff field와 함께 never-claimed task에 complete_task가 호출되면 zero-duration run이 합성되어 run_id가 여전히 무언가를 가리킵니다.
blocked{reason}워커나 사람이 작업을 blocked로 전환함. Never-claimed task에 --reason과 함께 호출되면 zero-duration run을 합성합니다.
unblocked없음수동 또는 /unblock으로 blocked -> ready. run_idNULL입니다.
archived없음기본 보드에서 숨김. 작업이 아직 running이었다면 side effect로 reclaimed된 run의 run_id를 carries합니다.

Edits - transition이 아닌 사람 주도의 변경입니다.

KindPayloadWhen
assigned{assignee}Assignee가 바뀜. Unassignment도 포함합니다.
edited{fields}Title 또는 body가 수정됨.
reprioritized{priority}Priority가 바뀜.
status{status}Dashboard drag-drop이 status를 직접 씀. 예를 들어 todo -> ready. running에서 다른 곳으로 끌어내릴 때 reclaimed된 run의 run_id를 carries하고, 그 외에는 run_idNULL입니다.

Worker telemetry - 논리적 작업이 아니라 execution process에 관한 event입니다.

KindPayloadWhen
spawned{pid}디스패처가 worker process 시작에 성공함.
heartbeat{note?}워커가 긴 작업 중 liveness를 알리기 위해 hermes kanban heartbeat $TASK를 호출함.
reclaimed{stale_lock}Completion 없이 claim TTL이 만료되어 작업이 ready로 돌아감.
crashed{pid, claimer}Worker PID가 더 이상 살아 있지 않지만 TTL이 아직 만료되지 않았음.
timed_out{pid, elapsed_seconds, limit_seconds, sigkill}max_runtime_seconds를 초과함. 디스패처가 SIGTERM 후 5초 grace 뒤 SIGKILL하고 re-queue함.
spawn_failed{error, failures}한 번의 spawn attempt가 실패함. 예를 들어 PATH 누락, mount할 수 없는 workspace 등이 있습니다. Counter가 증가하고 작업은 retry를 위해 ready로 돌아갑니다.
gave_up{failures, error}N회 연속 spawn_failed 뒤 circuit breaker가 동작함. 작업은 마지막 error와 함께 자동 block됩니다. 기본 N은 5이며 --failure-limit로 override합니다.

hermes kanban tail &lt;id&gt;는 단일 작업에 대해 이 event들을 보여 줍니다. hermes kanban watch는 보드 전체 event를 stream합니다.

Out of scope

Kanban은 의도적으로 single-host입니다. ~/.hermes/kanban.db는 local SQLite file이고 디스패처는 같은 machine에서 worker를 spawn합니다. 두 host가 하나의 보드를 공유하는 방식은 지원하지 않습니다. "host A의 worker X, host B의 worker Y"를 조율할 primitive가 없고, crash detection path는 PID가 host-local이라는 전제에 의존합니다. Multi-host가 필요하면 host마다 독립 보드를 돌리고 delegate_task나 message queue로 bridge하세요.

Design spec

전체 설계, architecture, concurrency correctness, 다른 시스템과의 비교, implementation plan, risk, open question은 docs/hermes-kanban-v1-spec.pdf에 있습니다. 동작 변경 PR을 열기 전에 이 문서를 먼저 읽어 보세요.