docs: add Korean Kanban documentation

Salvages #21823 by @pochi-gio. Adds Korean (ko) Docusaurus locale and
translates Kanban documentation (kanban.md, kanban-tutorial.md) and the
two related skills (devops-kanban-orchestrator, devops-kanban-worker).

Purely additive — adds ko to the locales list in docusaurus.config.ts
and creates the website/i18n/ko/ tree.
This commit is contained in:
pochi-gio 2026-05-18 21:42:08 -07:00 committed by Teknium
parent dfcf48b476
commit c4c45f11fa
5 changed files with 1358 additions and 1 deletions

View file

@ -24,7 +24,7 @@ const config: Config = {
i18n: {
defaultLocale: 'en',
locales: ['en', 'zh-Hans'],
locales: ['en', 'zh-Hans', 'ko'],
localeConfigs: {
en: {
label: 'English',
@ -33,6 +33,10 @@ const config: Config = {
label: '简体中文',
htmlLang: 'zh-Hans',
},
ko: {
label: '한국어',
htmlLang: 'ko',
},
},
},

View file

@ -0,0 +1,310 @@
# Kanban 튜토리얼
브라우저에 dashboard를 띄운 상태에서, Hermes Kanban 시스템이 설계된 4가지 대표 사용 사례를 따라가는 walkthrough입니다. 아직 [Kanban 개요](./kanban)를 읽지 않았다면 먼저 그 문서부터 보세요. 이 튜토리얼은 task, run, assignee, dispatcher의 의미를 이미 안다고 가정합니다.
## 설정
```bash
hermes kanban init # 선택 사항; 첫 `hermes kanban <anything>` 호출 시 자동 초기화됨
hermes dashboard # 브라우저에서 http://127.0.0.1:9119 열기
# 왼쪽 네비게이션에서 Kanban 클릭
```
dashboard는 시스템을 지켜보는 **사람인 당신**에게 가장 편한 인터페이스입니다. dispatcher가 spawn하는 agent worker는 dashboard나 CLI를 직접 보지 않습니다. 이들은 전용 `kanban_*` [toolset](./kanban#how-workers-interact-with-the-board) (`kanban_show`, `kanban_complete`, `kanban_block`, `kanban_heartbeat`, `kanban_comment`, `kanban_create`, `kanban_link`)으로 보드를 다룹니다. dashboard, CLI, worker tool은 모두 같은 board별 SQLite DB(기본 board는 `~/.hermes/kanban.db`, 이후 만든 board는 `~/.hermes/kanban/boards/<slug>/kanban.db`)를 통하므로, 어느 쪽에서 바꿔도 보드 상태는 일관됩니다.
이 튜토리얼은 계속 `default` board를 사용합니다. 프로젝트/레포/도메인별로 여러 개의 격리된 queue를 원한다면 개요 문서의 [Boards (멀티 프로젝트)](./kanban#boards-multi-project)를 보세요. CLI / dashboard / worker 흐름은 똑같고, worker는 물리적으로 다른 board의 task를 볼 수 없습니다.
이 문서 전체에서 **`bash`로 표시된 code block은 사람이 직접 실행하는 명령**입니다. **`# worker tool calls`**로 표시된 블록은 spawn된 worker의 모델이 실제로 내보내는 tool call 예시입니다. end-to-end 루프를 보여주기 위해 넣은 것이지, 사용자가 직접 실행하라는 뜻은 아닙니다.
## 보드 한눈에 보기
![Kanban board overview](/img/kanban-tutorial/01-board-overview.png)
왼쪽부터 오른쪽으로 6개 컬럼이 있습니다.
- **Triage** — 아직 거친 아이디어 상태인 항목. specifier가 구체 스펙으로 다듬기 전의 주차 구역입니다.
- **Todo** — 만들어졌지만 dependency를 기다리거나, 아직 assign되지 않은 task입니다.
- **Ready** — assign되었고 dispatcher가 claim하기만 기다리는 상태입니다.
- **In progress** — worker가 현재 실행 중인 task입니다. 기본값인 `Lanes by profile`이 켜져 있으면 assignee별로 하위 그룹이 생겨, 각 worker가 무엇을 하는지 한눈에 볼 수 있습니다.
- **Blocked** — worker가 사람 입력을 요청했거나 circuit breaker가 발동한 상태입니다.
- **Done** — 완료된 task입니다.
상단 바에는 search, tenant, assignee filter가 있고, `Lanes by profile` 토글과 `Nudge dispatcher` 버튼이 있습니다. `Nudge dispatcher`는 daemon의 다음 주기를 기다리지 않고 **지금 바로** dispatch tick을 한 번 실행합니다. 카드를 클릭하면 오른쪽 drawer가 열립니다.
### Flat view
profile lane이 너무 복잡하게 느껴지면 `Lanes by profile`을 끄세요. 그러면 In Progress 컬럼이 claim 시각 순의 단일 평면 리스트로 접힙니다.
![Board with lanes by profile off](/img/kanban-tutorial/02-board-flat.png)
## Story 1 — 혼자 기능을 출하하는 개발자
기능 하나를 만든다고 해봅시다. 전형적인 흐름은 스키마 설계 → API 구현 → 테스트 작성입니다. 부모→자식 dependency를 가진 task 3개로 구성됩니다.
```bash
SCHEMA=$(hermes kanban create "Design auth schema" \
--assignee backend-dev --tenant auth-project --priority 2 \
--body "Design the user/session/token schema for the auth module." \
--json | jq -r .id)
API=$(hermes kanban create "Implement auth API endpoints" \
--assignee backend-dev --tenant auth-project --priority 2 \
--parent $SCHEMA \
--body "POST /register, POST /login, POST /refresh, POST /logout." \
--json | jq -r .id)
hermes kanban create "Write auth integration tests" \
--assignee qa-dev --tenant auth-project --priority 2 \
--parent $API \
--body "Cover happy path, wrong password, expired token, concurrent refresh."
```
`API``SCHEMA`를 부모로 가지며, `tests``API`를 부모로 가집니다. 그래서 처음에 `ready`로 시작하는 것은 `SCHEMA` 하나뿐입니다. 나머지 두 task는 부모가 끝나기 전까지 `todo`에 머뭅니다. 이것이 dependency promotion engine의 역할입니다. 아직 테스트할 API가 없는데 테스트 작성 worker가 먼저 집어가는 일은 생기지 않습니다.
다음 dispatcher tick(기본 60초, 또는 **Nudge dispatcher** 즉시 실행)에서 `backend-dev` profile이 `HERMES_KANBAN_TASK=$SCHEMA`를 가진 worker로 spawn됩니다. agent 내부에서 이 worker의 tool-call 루프는 대략 다음과 같습니다.
```python
# worker tool calls — 직접 실행하는 명령 아님
kanban_show()
# → title, body, worker_context, parents, prior attempts, comments를 반환
# (worker가 worker_context를 읽고 terminal/file tool로 스키마를 설계하고,
# migration을 작성하고, 자체 체크를 돌리고, commit하는 실제 작업이 여기서 일어남)
kanban_heartbeat(note="schema drafted, writing migrations now")
kanban_complete(
summary="users(id, email, pw_hash), sessions(id, user_id, jti, expires_at); "
"refresh tokens stored as sessions with type='refresh'",
metadata={
"changed_files": ["migrations/001_users.sql", "migrations/002_sessions.sql"],
"decisions": ["bcrypt for hashing", "JWT for session tokens",
"7-day refresh, 15-min access"],
},
)
```
`kanban_show`는 기본적으로 `task_id``$HERMES_KANBAN_TASK`에서 가져오므로 worker는 자기 id를 몰라도 됩니다. `kanban_complete`는 summary + metadata를 현재 `task_runs` row에 기록하고, run을 닫고, task를 `done`으로 바꾸는 일을 **한 번의 atomic hop**으로 처리합니다.
`SCHEMA``done`이 되면 dependency engine이 `API`를 자동으로 `ready`로 승격시킵니다. 이후 API worker가 `kanban_show()`를 호출하면, 부모 handoff에 붙은 `SCHEMA`의 summary와 metadata를 바로 보게 됩니다. 긴 설계 문서를 다시 읽지 않아도 스키마 결정을 이해할 수 있습니다.
보드에서 완료된 schema task를 클릭하면 drawer에 모든 것이 보입니다.
![Solo dev — completed schema task drawer](/img/kanban-tutorial/03-drawer-schema-task.png)
핵심은 하단의 **Run History** 섹션입니다. 한 번의 시도, outcome `completed`, worker `@backend-dev`, 소요 시간, 타임스탬프, 그리고 전체 handoff summary가 표시됩니다. metadata blob(`changed_files`, `decisions`)도 run에 함께 저장되어 이후 parent를 읽는 downstream worker에 전달됩니다.
같은 데이터는 언제든 터미널에서도 볼 수 있습니다. 아래 명령은 **사람인 당신**이 보드를 들여다보는 행위이지 worker 동작이 아닙니다.
```bash
hermes kanban show $SCHEMA
hermes kanban runs $SCHEMA
# # OUTCOME PROFILE ELAPSED STARTED
# 1 completed backend-dev 0s 2026-04-27 19:34
# → users(id, email, pw_hash), sessions(id, user_id, jti, expires_at); refresh tokens ...
```
## Story 2 — Fleet farming
세 명의 worker(번역가, 전사 담당자, 카피라이터)와 서로 독립적인 task 묶음이 있다고 해봅시다. 세 명이 병렬로 일하면서 가시적인 진척을 내길 원합니다. 이것이 Kanban의 가장 단순하고도 원래 설계가 최적화된 대표 use-case입니다.
작업을 생성합니다.
```bash
for lang in Spanish French German; do
hermes kanban create "Translate homepage to $lang" \
--assignee translator --tenant content-ops
done
for i in 1 2 3 4 5; do
hermes kanban create "Transcribe Q3 customer call #$i" \
--assignee transcriber --tenant content-ops
done
for sku in 1001 1002 1003 1004; do
hermes kanban create "Generate product description: SKU-$sku" \
--assignee copywriter --tenant content-ops
done
```
gateway를 시작하고 잠시 자리를 떠나도 됩니다. gateway 안의 embedded dispatcher가 세 specialist profile의 task를 같은 `kanban.db`에서 동시에 끌어갑니다.
```bash
hermes gateway start
```
이제 보드를 `content-ops`로 filter하거나, "Transcribe"로 검색해보면 다음과 같은 화면이 나옵니다.
![Fleet view filtered to transcribe tasks](/img/kanban-tutorial/07-fleet-transcribes.png)
두 개의 전사 task는 완료, 하나는 실행 중, 둘은 다음 dispatcher tick을 기다리며 `ready` 상태입니다. In Progress 컬럼은 기본값인 profile lane으로 묶여 있기 때문에, mixed list를 훑지 않아도 각 worker의 현재 작업을 볼 수 있습니다. 현재 작업 하나가 끝나면 dispatcher가 다음 ready task를 즉시 running으로 승격합니다. 세 daemon이 세 개의 assignee pool을 병렬로 처리하므로, 사람 손을 더 대지 않아도 전체 content queue가 빠르게 소진됩니다.
**Story 1에서 본 structured handoff는 여기에도 그대로 적용됩니다.** 번역 worker가 task를 끝내며 `kanban_complete(summary="translated 4 pages, style matched existing marketing voice", metadata={"duration_seconds": 720, "tokens_used": 2100})`처럼 남기면, 분석과 후속 task, 대시보드 통계에 바로 쓸 수 있습니다.
## Story 3 — 재시도가 포함된 역할 파이프라인
여기서 Kanban은 평평한 TODO 리스트보다 확실한 장점을 보여줍니다. PM이 spec을 쓰고, 엔지니어가 구현하고, reviewer가 첫 시도를 reject하고, 엔지니어가 수정 후 재시도하고, reviewer가 승인합니다.
`auth-project`로 filter된 dashboard 화면:
![Pipeline view for a multi-role feature](/img/kanban-tutorial/08-pipeline-auth.png)
세 단계 체인이 동시에 보입니다. `Spec: password reset flow` (DONE, pm), `Implement password reset flow` (DONE, backend-dev), `Review password reset PR` (READY, reviewer). 각 task는 하단에 녹색 parent link와 child dependency를 보여줍니다.
가장 흥미로운 것은 구현 task입니다. 한 번 blocked되었다가 retry되었기 때문입니다. 아래는 세 agent가 어떻게 맞물리는지 worker tool call 기준으로 보여주는 예시입니다.
```python
# --- PM worker가 $SPEC에 spawn되어 acceptance criteria를 작성 ---
# worker tool calls
kanban_show()
kanban_complete(
summary="spec approved; POST /forgot-password sends email, "
"GET /reset/:token renders form, POST /reset applies new password",
metadata={"acceptance": [
"expired token returns 410",
"reused last-3 password returns 400 with message",
"successful reset invalidates all active sessions",
]},
)
# → $SPEC is done; $IMPL auto-promotes from todo to ready
# --- Engineer worker가 $IMPL에 spawn (첫 시도) ---
# worker tool calls
kanban_show() # worker_context에서 $SPEC의 summary + acceptance metadata를 읽음
# (engineer가 코드를 작성하고, 테스트를 돌리고, PR을 엶)
# Reviewer feedback arrives — engineer decides the concerns are valid and blocks
kanban_block(
reason="Review: password strength check missing, reset link isn't "
"single-use (can be replayed within 30min)",
)
# → $IMPL transitions to blocked; run 1 closes with outcome='blocked'
```
이제 **사람인 당신**(혹은 별도 reviewer profile)이 block reason을 읽고, 수정 방향이 명확하다고 판단해 dashboard의 "Unblock" 버튼을 누르거나, CLI / slash command로 unblock합니다.
```bash
hermes kanban unblock $IMPL
# 또는 chat에서: /kanban unblock $IMPL
```
dispatcher는 `$IMPL`을 다시 `ready`로 돌려놓고, 다음 tick에 `backend-dev` worker를 다시 spawn합니다. 이 두 번째 spawn은 **같은 task에 대한 새로운 run**입니다.
```python
# --- Engineer worker가 $IMPL에 다시 spawn (두 번째 시도) ---
# worker tool calls
kanban_show()
# → 이제 worker_context에 run 1의 block reason이 포함되어 있으므로,
# worker는 스펙 전체를 다시 읽기보다 어떤 두 가지를 고쳐야 하는지 바로 안다.
# (engineer가 zxcvbn check 추가, reset token을 single-use로 만들고, 테스트 재실행)
kanban_complete(
summary="added zxcvbn strength check, reset tokens are now single-use "
"(stored + deleted on success)",
metadata={
"changed_files": [
"auth/reset.py",
"auth/tests/test_reset.py",
"migrations/003_single_use_reset_tokens.sql",
],
"tests_run": 11,
"review_iteration": 2,
},
)
```
구현 task를 클릭하면 drawer에 **두 번의 시도**가 보입니다.
![Implementation task with two runs — blocked then completed](/img/kanban-tutorial/04b-drawer-retry-history-scrolled.png)
- **Run 1**`@backend-dev``blocked`. review feedback이 outcome 아래에 그대로 남아 있습니다: "password strength check missing, reset link isn't single-use (can be replayed within 30min)".
- **Run 2**`@backend-dev``completed`. 새로운 summary와 metadata를 가집니다.
각 run은 `task_runs`의 독립 row이며, 고유한 outcome, summary, metadata를 가집니다. retry history는 단지 최신 상태 task 위에 얹힌 부가 기능이 아니라, 시스템의 **1차 표현 방식**입니다. 재시도 worker가 task를 열면 `build_worker_context`가 이전 시도를 보여주므로, 두 번째 worker는 첫 번째 시도가 왜 막혔는지 알고 **같은 실수를 반복하지 않습니다**.
이제 reviewer 차례입니다. `Review password reset PR`을 열면 다음을 보게 됩니다.
![Reviewer's drawer view of the pipeline](/img/kanban-tutorial/09-drawer-pipeline-review.png)
부모 link는 완료된 구현 task를 가리킵니다. reviewer worker가 `Review password reset PR`에 spawn되어 `kanban_show()`를 호출하면, `worker_context`는 부모의 **가장 최근 completed run의 summary + metadata**를 포함합니다. 그래서 diff를 보기 전부터 "added zxcvbn strength check, reset tokens are now single-use"라는 요약과 changed file 목록을 손에 쥐고 시작할 수 있습니다.
## Story 4 — Circuit breaker와 crash recovery
현실의 worker는 실패합니다. credential 누락, OOM kill, 일시적 네트워크 오류가 생깁니다. dispatcher는 이에 대해 두 겹의 방어선을 가집니다.
- **circuit breaker** — 연속 N회 실패 시 자동 block하여 보드가 영원히 thrash하지 않도록 함
- **crash detection** — TTL이 만료되기 전에 worker PID가 사라진 task를 reclaim함
### Circuit breaker — 영구 장애처럼 보이는 실패
예를 들어 `AWS_ACCESS_KEY_ID`가 profile 환경에 없는 deploy task:
```bash
hermes kanban create "Deploy to staging (missing creds)" \
--assignee deploy-bot --tenant ops
```
dispatcher가 worker spawn을 시도하지만 `RuntimeError: AWS_ACCESS_KEY_ID not set`로 실패합니다. dispatcher는 claim을 release하고 failure counter를 증가시키며 다음 tick에 다시 시도합니다. 기본 `failure_limit`인 3회 연속 실패 후 circuit이 열리면 task는 outcome `gave_up`과 함께 `blocked`가 됩니다. 사람이 unblock하기 전까지는 더 이상 재시도하지 않습니다.
blocked task를 클릭하면:
![Circuit breaker — 2 spawn_failed + 1 gave_up](/img/kanban-tutorial/11-drawer-gave-up.png)
같은 error가 적힌 세 개의 run이 보입니다. 앞의 두 개는 `spawn_failed`(재시도 가능), 세 번째는 `gave_up`(종결 상태)입니다. 위쪽 event log는 `created → claimed → spawn_failed → claimed → spawn_failed → claimed → gave_up`의 전체 순서를 보여줍니다.
터미널에서는:
```bash
hermes kanban runs t_ef5d
# # OUTCOME PROFILE ELAPSED STARTED
# 1 spawn_failed deploy-bot 0s 2026-04-27 19:34
# ! AWS_ACCESS_KEY_ID not set in deploy-bot env
# 2 spawn_failed deploy-bot 0s 2026-04-27 19:34
# ! AWS_ACCESS_KEY_ID not set in deploy-bot env
# 3 gave_up deploy-bot 0s 2026-04-27 19:34
# ! AWS_ACCESS_KEY_ID not set in deploy-bot env
```
Telegram / Discord / Slack이 연결되어 있다면 `gave_up` 이벤트에 대해 gateway notification이 발송되므로, 보드를 수동으로 확인하지 않아도 장애를 알 수 있습니다.
### Crash recovery — worker가 실행 중 중간에 죽는 경우
spawn은 성공했지만 이후 worker 프로세스가 죽는 경우도 있습니다. segfault, OOM, `systemctl stop` 등이 여기에 해당합니다. dispatcher는 `kill(pid, 0)` polling으로 죽은 pid를 감지하고, claim을 release하고, task를 `ready`로 되돌린 뒤 다음 tick에 새 worker에게 넘깁니다.
seed data 예시에서는 migration이 메모리를 다 써버리는 상황입니다.
```bash
# Worker claims, starts scanning 2.4M rows, OOM kills it at ~2.3M
# Dispatcher detects dead pid, releases claim, increments attempt counter
# Retry with a chunked strategy succeeds
```
drawer는 두 번의 시도 전체를 보여줍니다.
![Crash and recovery — 1 crashed + 1 completed](/img/kanban-tutorial/06-drawer-crash-recovery.png)
Run 1은 `crashed`, error는 `OOM kill at row 2.3M (process 99999 gone)`입니다. Run 2는 `completed`, metadata에는 `"strategy": "chunked with LIMIT + WHERE id > last_id"`가 들어 있습니다. retrying worker는 run 1의 crash를 컨텍스트에서 보고 더 안전한 전략을 선택했습니다. metadata는 나중에 보는 사람이나 postmortem 작성자에게 **무엇이 바뀌었는지**를 즉시 보여줍니다.
## Structured handoff — 왜 `summary``metadata`가 중요한가
위의 모든 story에서 worker는 마지막에 `kanban_complete(summary=..., metadata=...)`를 호출했습니다. 이것은 장식이 아니라, workflow 단계 사이를 잇는 **주요 handoff 채널**입니다.
task B의 worker가 spawn되어 `kanban_show()`를 호출하면 `worker_context`에는 다음이 포함됩니다.
- B 자신의 **이전 시도들** (outcome, summary, error, metadata) — retry worker가 실패한 경로를 반복하지 않도록 함
- **부모 task 결과** — 각 부모에 대해 가장 최근 completed run의 summary와 metadata — downstream worker가 upstream 작업의 이유와 방법을 이해하도록 함
이 구조는 flat kanban 시스템에서 흔한 "comment와 결과물을 뒤져서 맥락을 복원하는 일"을 대체합니다. PM이 spec metadata에 acceptance criteria를 쓰면 engineer worker는 부모 handoff에서 이를 구조적으로 읽습니다. engineer가 어떤 테스트를 돌렸고 몇 개가 통과했는지 기록하면, reviewer worker는 diff를 열기 전부터 그 목록을 손에 쥐게 됩니다.
bulk-close guard가 존재하는 이유도 이것이 **run별 데이터**이기 때문입니다. `hermes kanban complete a b c --summary X`는 CLI에서 거부됩니다. 같은 summary를 세 task에 복붙하는 일은 거의 항상 잘못이기 때문입니다. handoff flag 없이 bulk close하는 기능은 "행정성 task 여러 개를 한꺼번에 끝냈다" 같은 일반적인 경우를 위해 여전히 남아 있습니다. 반면 tool 표면에는 bulk variant 자체가 없습니다. 같은 이유로 `kanban_complete`는 언제나 single-task 단위입니다.
## 현재 실행 중인 task 들여다보기
완전성을 위해, 아직 끝나지 않은 in-flight task의 drawer도 보겠습니다. 아래는 Story 1의 API 구현 task가 `backend-dev`에 claim되어 실행 중이지만 아직 완료되지 않은 상태입니다.
![Claimed, in-flight task](/img/kanban-tutorial/10-drawer-in-flight.png)
상태는 `Running`입니다. 활성 run은 Run History 섹션에 outcome `active`, `ended_at` 없음으로 표시됩니다. 만약 이 worker가 죽거나 timeout되면, dispatcher는 이 run을 적절한 outcome으로 닫고 다음 claim에서 새 run을 엽니다. **시도 row는 사라지지 않습니다.**
## 다음 단계
- [Kanban 개요](./kanban) — 전체 데이터 모델, event vocabulary, CLI reference
- `hermes kanban --help` — 모든 subcommand와 flag
- `hermes kanban watch --kinds completed,gave_up,timed_out` — 보드 전체 terminal event 실시간 스트림
- `hermes kanban notify-subscribe <task> --platform telegram --chat-id <id>` — 특정 task가 끝날 때 gateway 알림 받기

View file

@ -0,0 +1,721 @@
---
sidebar_position: 12
title: "Kanban (멀티 에이전트 보드)"
description: "여러 Hermes 프로필을 조율하기 위한, 지속형 SQLite 기반 작업 보드"
sidebar_label: "Kanban"
---
# Kanban — 멀티 에이전트 프로필 협업
> **전체 흐름을 먼저 보고 싶다면?** [Kanban 튜토리얼](./kanban-tutorial)을 읽어보세요. 이 문서는 레퍼런스이고, 튜토리얼은 사용자 시나리오 중심 설명입니다.
Hermes Kanban은 모든 Hermes 프로필이 함께 쓰는 **지속형 작업 보드**입니다. 취약한 in-process 서브에이전트 무리 대신, 이름 있는 여러 에이전트가 같은 작업을 협업할 수 있게 해줍니다. 모든 task는 `~/.hermes/kanban.db`의 한 row이고, 모든 handoff도 누구나 읽고 쓸 수 있는 row이며, 모든 worker는 자기 정체성을 가진 **독립 OS 프로세스**입니다.
### 두 개의 표면: 모델은 tool로 말하고, 사용자는 CLI로 다룹니다
보드에는 두 개의 진입점이 있고, 둘 다 같은 `~/.hermes/kanban.db`를 사용합니다.
- **에이전트는 전용 `kanban_*` toolset으로 보드를 다룹니다.** `kanban_show`, `kanban_complete`, `kanban_block`, `kanban_heartbeat`, `kanban_comment`, `kanban_create`, `kanban_link`가 여기에 포함됩니다. dispatcher는 worker를 띄울 때 이 tool들을 스키마에 넣어주며, 모델은 `hermes kanban` CLI를 shell로 호출하지 않고 **직접 tool call**로 task를 읽고 넘깁니다. 아래의 [작업자는 보드와 어떻게 상호작용하나](#how-workers-interact-with-the-board)를 참고하세요.
- **사람(그리고 스크립트, cron)은 `hermes kanban …` CLI, `/kanban …` 슬래시 명령, 혹은 dashboard로 보드를 다룹니다.** 이 표면은 tool-calling 모델이 없는 인간/자동화를 위한 인터페이스입니다.
두 표면 모두 같은 `kanban_db` 계층을 통하기 때문에, 읽기 결과는 일관되고 쓰기 결과가 어긋나지 않습니다. 이 문서는 복사해 쓰기 쉬운 CLI 예시를 중심으로 설명하지만, 여기 등장하는 CLI 동작은 전부 모델이 쓰는 tool-call 대응물이 있습니다.
이 구조는 `delegate_task`로는 커버하기 어려운 작업에 적합합니다.
- **리서치 분업** — 병렬 조사자 + 분석가 + 작성자, 그리고 human-in-the-loop
- **스케줄 기반 운영** — 주/월 단위로 누적되는 recurring 브리프
- **디지털 트윈** — 시간이 지나며 메모리를 축적하는 named assistant (`inbox-triage`, `ops-review`)
- **엔지니어링 파이프라인** — 분해 → 병렬 구현(worktree) → 리뷰 → 반복 → PR
- **플릿 작업** — 한 specialist가 N개의 대상(예: 50개 소셜 계정, 12개 서비스)을 관리
설계 배경, 비교 분석(Cline Kanban / Paperclip / NanoClaw / Google Gemini Enterprise), 8개의 정형 협업 패턴은 레포의 `docs/hermes-kanban-v1-spec.pdf`를 참고하세요.
## Kanban vs. `delegate_task`
겉보기엔 비슷하지만, 같은 primitive가 아닙니다.
| | `delegate_task` | Kanban |
|---|---|---|
| 형태 | RPC 호출 (fork → join) | 지속형 메시지 큐 + 상태 머신 |
| 부모 | 자식이 끝날 때까지 block | `create` 후 fire-and-forget |
| 자식 정체성 | 익명 subagent | persistent memory를 가진 named profile |
| 재개 가능성 | 없음 — 실패하면 끝 | block → unblock → 재실행, crash → reclaim |
| Human in the loop | 지원 안 함 | 언제든 comment / unblock 가능 |
| task당 agent 수 | 한 호출 = 한 subagent | task 수명 동안 N명의 agent 가능 |
| 감사 이력 | 컨텍스트 압축 시 사라짐 | SQLite row로 영구 보존 |
| 조율 구조 | 계층형 (caller → callee) | 동료형 — 어떤 profile이든 task를 읽고 수정 가능 |
**한 줄 차이:** `delegate_task`는 함수 호출이고, Kanban은 어떤 profile이든 보고 수정할 수 있는 handoff row를 가진 작업 큐입니다.
**`delegate_task`를 써야 할 때**
- 부모 agent가 이어서 생각하기 전에 짧은 reasoning 결과가 필요할 때
- 사람이 끼지 않을 때
- 결과가 다시 부모 컨텍스트 안으로 바로 돌아가야 할 때
**Kanban을 써야 할 때**
- 작업이 agent 경계를 넘을 때
- 재시작 이후에도 살아남아야 할 때
- 중간에 사람 입력이 필요할 수 있을 때
- 다른 role이 이어받을 수 있어야 할 때
- 사후에 추적 가능해야 할 때
두 기능은 함께 쓸 수 있습니다. kanban worker가 자기 task 수행 중 내부적으로 `delegate_task`를 호출하는 것도 가능합니다.
## 핵심 개념
- **Board** — 자체 SQLite DB, workspace 디렉터리, dispatcher loop를 가진 독립 queue. 하나의 설치에 여러 board를 둘 수 있습니다. 자세한 내용은 아래의 [Boards (멀티 프로젝트)](#boards-multi-project).
- **Task** — 제목, 선택적 본문, 단일 assignee(profile 이름), 상태(`triage | todo | ready | running | blocked | done | archived`), 선택적 tenant namespace, 선택적 idempotency key를 가진 row.
- **Link** — 부모 → 자식 의존성을 기록하는 `task_links` row. 부모가 모두 `done`이면 dispatcher가 `todo → ready`로 승격시킵니다.
- **Comment** — 에이전트 간 프로토콜. agent와 사람이 comment를 붙이고, worker가 (재)실행될 때 전체 thread를 컨텍스트로 읽습니다.
- **Workspace** — worker가 실제 작업을 수행하는 디렉터리.
- `scratch` (기본값) — `~/.hermes/kanban/workspaces/<id>/` 아래의 새 tmp 디렉터리 (non-default board는 board 경로 아래)
- `dir:<path>` — 기존 공유 디렉터리. **절대경로만 허용**됩니다.
- `worktree` — 코딩 task를 위한 git worktree (`.worktrees/<id>/`)
- **Dispatcher** — 주기적으로 stale claim 회수, crashed worker 정리, ready task 승격, atomic claim, assigned profile spawn을 수행하는 장기 실행 루프. 기본적으로 gateway 내부(`kanban.dispatch_in_gateway: true`)에서 동작합니다.
- **Tenant** — board 내부의 선택적 namespace. 예를 들어 하나의 specialist fleet가 여러 고객사를 처리할 때 `--tenant business-a`처럼 사용합니다. tenant는 soft filter이고, board가 hard isolation boundary입니다.
## Boards (멀티 프로젝트) {#boards-multi-project}
board를 쓰면 서로 무관한 작업 흐름을 프로젝트/레포/도메인별로 완전히 분리할 수 있습니다. 새 설치에는 `default` board 하나만 존재하며, DB는 하위 호환 때문에 `~/.hermes/kanban.db`에 놓입니다. 작업 흐름이 하나뿐인 사용자는 board 개념을 몰라도 됩니다.
board 단위 격리는 다음을 의미합니다.
- board별 별도 SQLite DB (`~/.hermes/kanban/boards/<slug>/kanban.db`)
- 별도 `workspaces/``logs/`
- worker는 자기 board task만 볼 수 있음 (`HERMES_KANBAN_BOARD` 고정)
- board 간 task link는 불가
### CLI에서 board 관리
```bash
# 현재 디스크에 있는 board 확인
hermes kanban boards list
# 새 board 생성
hermes kanban boards create atm10-server \
--name "ATM10 Server" \
--description "Minecraft modded server ops" \
--icon 🎮 \
--switch
# switch 없이 특정 board만 대상으로 실행
hermes kanban --board atm10-server list
hermes kanban --board atm10-server create "Restart ATM server" --assignee ops
# 현재 board 바꾸기
hermes kanban boards switch atm10-server
hermes kanban boards show
# 표시 이름 변경 (slug는 디렉터리 이름이라 immutable)
hermes kanban boards rename atm10-server "ATM10 (Prod)"
# 아카이브(기본): dir을 boards/_archived/<slug>-<ts>/ 로 이동
hermes kanban boards rm atm10-server
# 영구 삭제
hermes kanban boards rm atm10-server --delete
```
board 해석 우선순위는 다음과 같습니다.
1. 명시적 `--board <slug>`
2. `HERMES_KANBAN_BOARD` 환경변수
3. `~/.hermes/kanban/current`
4. `default`
slug는 소문자 영숫자 + `-` + `_`, 길이 164로 제한되며, 대문자 입력은 자동 소문자화됩니다.
### Dashboard에서 board 관리
`hermes dashboard`의 Kanban 탭은 board가 2개 이상이거나 task가 존재하면 상단에 board switcher를 표시합니다.
- **Board dropdown** — 활성 board 선택. 브라우저 `localStorage`에 저장되므로 새로고침 후에도 유지됩니다.
- **+ New board** — slug, display name, description, icon 입력 modal
- **Archive** — non-`default` board에서만 표시
모든 dashboard API endpoint는 `?board=<slug>`를 받고, 이벤트 WebSocket도 연결 시점에 특정 board로 고정됩니다.
## 빠른 시작
아래 명령은 **사람인 당신**이 board를 만들고 task를 등록하는 단계입니다. task가 assign된 뒤부터는 dispatcher가 해당 profile을 worker로 띄우고, 그 이후에는 **모델이 CLI가 아니라 `kanban_*` tool call**로 task를 진행합니다.
```bash
# 1. board 생성
hermes kanban init
# 2. gateway 시작 (내장 dispatcher 포함)
hermes gateway start
# 3. task 생성
hermes kanban create "research AI funding landscape" --assignee researcher
# 4. 실시간 확인
hermes kanban watch
# 5. board 상태 보기
hermes kanban list
hermes kanban stats
```
dispatcher가 `t_abcd`를 집어 `researcher` profile을 worker로 띄우면, 그 worker가 제일 먼저 하는 일은 `kanban_show()` 호출입니다. `hermes kanban show t_abcd`를 shell로 실행하지 않습니다.
### Gateway 내장 dispatcher (기본값)
dispatcher는 gateway 프로세스 안에서 돌기 때문에 별도 서비스가 필요 없습니다. gateway만 살아 있으면 ready task는 다음 tick(기본 60초)에 처리됩니다.
```yaml
kanban:
dispatch_in_gateway: true
dispatch_interval_seconds: 60
```
디버깅용으로만 `HERMES_KANBAN_DISPATCH_IN_GATEWAY=0`으로 끌 수 있습니다. `hermes kanban daemon` 단독 실행 방식은 **deprecated**이며, 가능하면 gateway를 쓰는 것이 권장됩니다.
### Idempotent create (자동화 / webhook용)
```bash
hermes kanban create "nightly ops review" \
--assignee ops \
--idempotency-key "nightly-ops-$(date -u +%Y-%m-%d)" \
--json
```
같은 key로 재호출하면 중복 task 대신 기존 task id를 돌려줍니다.
### Bulk CLI verbs
```bash
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
```
## 작업자는 보드와 어떻게 상호작용하나 {#how-workers-interact-with-the-board}
**Worker는 `hermes kanban`을 shell로 호출하지 않습니다.** dispatcher는 worker spawn 시 `HERMES_KANBAN_TASK=t_abcd`를 child env에 넣고, 그 환경변수가 모델 스키마에서 전용 **kanban toolset**을 활성화합니다. 이 7개 tool은 CLI와 동일하게 Python `kanban_db` 계층을 직접 호출합니다.
| Tool | 목적 | 필수 파라미터 |
|---|---|---|
| `kanban_show` | 현재 task 읽기 (제목, 본문, 시도 이력, 부모 handoff, comment, `worker_context`) | — |
| `kanban_complete` | `summary` + `metadata`로 완료 | `summary` 또는 `result` 중 최소 하나 |
| `kanban_block` | 사람 입력이 필요할 때 block | `reason` |
| `kanban_heartbeat` | 장기 작업 중 살아있음을 표시 | — |
| `kanban_comment` | task thread에 note 추가 | `task_id`, `body` |
| `kanban_create` | (orchestrator) child task fan-out | `title`, `assignee` |
| `kanban_link` | (orchestrator) 부모-자식 dependency 추가 | `parent_id`, `child_id` |
전형적인 worker 흐름은 아래와 같습니다.
```
kanban_show()
# (model이 worker_context를 읽고 terminal/file tool로 실제 작업 수행)
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},
)
```
orchestrator라면 이런 식으로 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",
)
kanban_create(title="research ICP funding — EU angle", assignee="researcher-b", body="…")
kanban_create(
title="synthesize findings into launch brief",
assignee="writer",
parents=["t_r1", "t_r2"],
body="one-pager, 300 words, neutral tone",
)
kanban_complete(summary="decomposed into 2 research tasks + 1 writer; linked dependencies")
```
`kanban_create`, `kanban_link`, 다른 task에 대한 `kanban_comment`는 모든 worker에게 기술적으로 열려 있지만, **worker profile은 fan-out하지 않고 orchestrator profile은 직접 실행하지 않는다**는 운영 규칙을 `kanban-orchestrator` skill이 강제하는 것이 권장됩니다.
### 왜 `hermes kanban` shell 호출 대신 tool인가
1. **백엔드 이식성** — terminal backend가 Docker / Modal / Singularity / SSH여도, kanban tool은 agent 자신의 Python 프로세스에서 돌아가므로 항상 `~/.hermes/kanban.db`에 도달합니다.
2. **shell quoting 취약성 제거**`--metadata '{"files": [...]}'` 같은 문자열 인자 문제를 피합니다.
3. **더 좋은 오류 처리** — stderr 파싱이 아니라 structured JSON 결과를 모델이 바로 읽습니다.
**일반 세션에는 schema footprint가 0입니다.** 평범한 `hermes chat` 세션에는 `kanban_*` tool이 나타나지 않습니다. `HERMES_KANBAN_TASK`가 있을 때만 `check_fn`이 True가 되기 때문입니다.
### 추천 handoff evidence
`kanban_complete(summary=..., metadata={...})`의 의도는 명확합니다.
- `summary` — 사람이 읽는 closeout
- `metadata` — 다음 agent / reviewer / dashboard가 재사용할 수 있는 machine-readable handoff
엔지니어링/리뷰 task라면 보통 이런 `metadata` 형태를 권장합니다.
```json
{
"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"]
}
```
이 키들은 강제 스키마가 아니라 **관례**입니다. 중요한 건 다음 4가지를 빠르게 알 수 있게 하는 것입니다.
1. 무엇이 바뀌었나?
2. 어떻게 검증했나?
3. 실패했을 때 무엇이 unblock / retry를 가능하게 하나?
4. 어떤 risk가 의도적으로 남아 있나?
`metadata`에는 secret, raw log, token, OAuth material, 무관한 transcript를 넣지 말고, 요약과 pointer만 넣는 게 좋습니다.
### Worker skill
kanban task를 처리할 수 있는 profile은 `kanban-worker` skill을 로드해야 합니다. 이 skill은 CLI가 아니라 **tool call 기준 lifecycle**을 가르칩니다.
1. spawn되면 `kanban_show()` 호출
2. terminal tool로 `cd $HERMES_KANBAN_WORKSPACE`
3. 장기 작업 중 `kanban_heartbeat(note="...")`
4. 끝나면 `kanban_complete(...)`, 막히면 `kanban_block(...)`
설치 예시는 다음과 같습니다.
```bash
hermes skills install devops/kanban-worker
```
dispatcher는 worker를 띄울 때 자동으로 `--skills kanban-worker`도 함께 넘기므로, profile 기본 skill 설정에 없더라도 실행 시점에는 항상 패턴 라이브러리를 갖게 됩니다.
### 특정 task에 skill 추가로 pin하기
어떤 task는 assignee profile 기본 skill만으로는 부족할 수 있습니다. 예를 들어 번역 task에는 `translation`, 리뷰 task에는 `github-code-review`, 보안 감사에는 `security-pr-audit`가 필요할 수 있습니다. 그럴 때 profile 자체를 매번 수정하지 말고 task에 직접 skill을 붙이면 됩니다.
**orchestrator agent에서**
```
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 / slash command에서**
```bash
hermes kanban create "translate README to Japanese" \
--assignee linguist \
--skill translation
hermes kanban create "audit auth flow" \
--assignee reviewer \
--skill security-pr-audit \
--skill github-code-review
```
**dashboard에서는** inline create form의 **skills** 필드에 comma-separated로 넣으면 됩니다.
이 skill들은 기본 `kanban-worker`**추가(additive)** 됩니다. dispatcher는 각 skill마다 `--skills <name>` 플래그를 하나씩 넣어 worker를 띄웁니다.
### Orchestrator skill
**잘 행동하는 orchestrator는 일을 직접 하지 않습니다.** 사용자의 목표를 task로 분해하고, link를 만들고, specialist에게 assign한 뒤 물러납니다. `kanban-orchestrator` skill은 이 규칙을 `kanban_create` / `kanban_link` / `kanban_comment` 패턴으로 정리해 둡니다.
대표적인 orchestrator turn 예시:
```
# 사용자 목표: "draft a launch post on the ICP funding landscape"
kanban_create(title="research ICP funding, NA angle", assignee="researcher-a", body="…")
kanban_create(title="research ICP funding, EU angle", assignee="researcher-b", body="…")
kanban_create(
title="synthesize ICP funding research into launch post draft",
assignee="writer",
parents=["t_r1", "t_r2"],
body="one-pager, neutral tone, cite sources inline",
)
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",
)
```
설치:
```bash
hermes skills install devops/kanban-orchestrator
```
가장 깔끔한 운용은 orchestrator profile의 toolset을 board operation 위주(`kanban`, `gateway`, `memory`)로 제한해, 구현 작업을 **물리적으로 직접 실행할 수 없게** 만드는 것입니다.
## Dashboard (GUI)
`/kanban` CLI와 slash command만으로도 headless 운영은 가능하지만, triage, cross-profile supervision, comment thread 읽기, 카드 drag/drop 같은 작업은 사람이 보기엔 시각 보드가 더 편합니다. Hermes는 이를 core 기능이 아니라 `plugins/kanban/`의 **bundled dashboard plugin**으로 제공합니다.
열기:
```bash
hermes kanban init
hermes dashboard
```
### Plugin이 제공하는 것
- `triage`, `todo`, `ready`, `running`, `blocked`, `done` 컬럼(토글 시 `archived` 포함)
- 카드에 task id, title, priority badge, tenant tag, assignee, comment/link 수, progress pill, 생성 시간 표시
- **Running 컬럼의 profile별 lane**
- **WebSocket 기반 실시간 업데이트**
- 컬럼 간 **drag-drop 상태 전환**
- **Inline create**
- **Multi-select + bulk action**
- 카드 클릭 시 side drawer:
- 제목/assignee/priority 수정
- markdown description 편집
- dependency editor
- 상태 전환 버튼
- result section, comment thread, 최근 20개 이벤트
- 상단 toolbar filter:
- free-text search
- tenant dropdown
- assignee dropdown
- archived toggle
- lanes by profile toggle
- **Nudge dispatcher** 버튼
시각적으로는 Linear / Fusion 스타일의 dark theme 보드를 지향합니다.
### 아키텍처
GUI는 철저히 **DB 읽기 + `kanban_db` 쓰기** 레이어입니다.
```
┌────────────────────────┐ 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 표면
모든 route는 `/api/plugins/kanban/` 아래에 있으며 dashboard의 ephemeral session token으로 보호됩니다.
| Method | Path | 목적 |
|---|---|---|
| `GET` | `/board?tenant=<name>&include_archived=…` | 상태 컬럼별 전체 board + filter용 tenants/assignees |
| `GET` | `/tasks/:id` | task + comments + events + links |
| `POST` | `/tasks` | 생성 |
| `PATCH` | `/tasks/:id` | 상태 / assignee / priority / title / body / result 수정 |
| `POST` | `/tasks/bulk` | 여러 id에 동일 patch 적용 |
| `POST` | `/tasks/:id/comments` | comment 추가 |
| `POST` | `/links` | dependency 추가 |
| `DELETE` | `/links?parent_id=…&child_id=…` | dependency 제거 |
| `POST` | `/dispatch?max=…&dry_run=…` | dispatcher 즉시 1회 실행 |
| `GET` | `/config` | `dashboard.kanban` 설정 읽기 |
| `WS` | `/events?since=<event_id>` | `task_events` 실시간 스트림 |
handler는 전부 얇은 wrapper이고, 실제 비즈니스 로직은 `kanban_db`에 있습니다.
### Dashboard 설정
`~/.hermes/config.yaml``dashboard.kanban` 아래 키로 기본 동작을 바꿀 수 있습니다.
```yaml
dashboard:
kanban:
default_tenant: acme
lane_by_profile: true
include_archived_by_default: false
render_markdown: true
```
### 보안 모델
dashboard는 기본적으로 localhost에 bind되므로 plugin route들은 별도 인증 없이 열려 있습니다. 즉 **호스트 내부 프로세스**는 kanban REST 표면에 접근할 수 있습니다.
WebSocket은 브라우저 upgrade 요청 특성상 `Authorization` 헤더를 못 쓰기 때문에 `?token=…` query parameter로 dashboard session token을 요구합니다.
`hermes dashboard --host 0.0.0.0`로 띄우면 모든 plugin route가 네트워크에 노출됩니다. **공유 호스트에서는 권장되지 않습니다.** task body, comment, workspace path 등 협업 surface 전체가 노출될 수 있습니다.
### Live updates
`task_events`는 monotonic `id`를 가진 append-only SQLite table입니다. WebSocket endpoint는 클라이언트별 last-seen event id를 들고 있다가 새 row를 push합니다. 이벤트 burst가 와도 frontend는 board endpoint를 한 번만 재로딩해 상태를 맞춥니다.
### 확장
plugin은 표준 Hermes dashboard plugin contract를 사용합니다. 추가 컬럼, 커스텀 카드 UI, tenant-filtered layout, 전체 `tab.override` 교체도 plugin fork 없이 표현 가능합니다.
비활성화만 하고 싶다면 `config.yaml`에 다음을 추가하면 됩니다.
```yaml
dashboard:
plugins:
kanban:
enabled: false
```
### 범위 경계
GUI는 의도적으로 얇습니다. auto-assignment, budget, governance gate, org-chart view 같은 것은 user-space 영역입니다.
## CLI 명령 레퍼런스
이 표면은 **사람, 스크립트, cron, dashboard**가 보드를 조작할 때 씁니다. dispatcher 내부 worker는 동일 작업을 `kanban_*` [tool 표면](#how-workers-interact-with-the-board)으로 수행합니다.
```
hermes kanban init
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>
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]
hermes kanban complete <id>... [--result "..."]
hermes kanban block <id> "<reason>" [--ids <id>...]
hermes kanban unblock <id>...
hermes kanban archive <id>...
hermes kanban tail <id>
hermes kanban watch [--assignee P] [--tenant T] [--kinds completed,blocked,…] [--interval SECS]
hermes kanban heartbeat <id> [--note "..."]
hermes kanban runs <id> [--json]
hermes kanban assignees [--json]
hermes kanban dispatch [--dry-run] [--max N] [--failure-limit N] [--json]
hermes kanban daemon --force
hermes kanban stats [--json]
hermes kanban log <id> [--tail BYTES]
hermes kanban notify-subscribe <id> --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>
hermes kanban gc [--event-retention-days N] [--log-retention-days N]
```
모든 명령은 interactive CLI와 messaging gateway에서도 `/kanban` slash command로 쓸 수 있습니다.
## `/kanban` 슬래시 명령 {#kanban-slash-command}
모든 `hermes kanban <action>``/kanban <action>`으로도 호출할 수 있습니다. interactive `hermes chat` 세션과 Telegram/Discord/Slack/WhatsApp/Signal/Matrix/Mattermost/email/SMS 등 gateway 플랫폼에서 모두 동작합니다.
```
/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
```
여러 단어 인자는 shell처럼 quote하면 됩니다. 내부적으로 `shlex.split`을 사용합니다.
### 실행 중 사용: `/kanban`은 running-agent guard를 우회합니다
일반적으로 gateway는 agent가 아직 응답 중이면 slash command와 user message를 queue에 쌓습니다. 그러나 **`/kanban`은 예외입니다.** board는 `~/.hermes/kanban.db`에 있고 실행 중인 agent의 내부 state에 묶여 있지 않기 때문입니다.
예:
- worker가 peer를 기다리며 block됨 → 휴대폰에서 `/kanban unblock t_abcd`
- 사람이 context를 더 넣어야 함 → `/kanban comment t_xyz "use the 2026 schema, not 2025"`
- orchestrator를 멈추지 않고 플릿 상태를 보고 싶음 → `/kanban list --mine`, `/kanban stats`
### `/kanban create` 시 자동 구독 (gateway 전용)
gateway에서 `/kanban create "…"`로 task를 만들면, 원래 chat이 해당 task의 terminal event(`completed`, `blocked`, `gave_up`, `crashed`, `timed_out`)에 자동 구독됩니다.
```
you> /kanban create "transcribe today's podcast" --assignee transcriber
bot> Created t_9fc1a3 (ready, assignee=transcriber)
(subscribed — you'll be notified when t_9fc1a3 completes or blocks)
… ~8 minutes later …
bot> ✓ t_9fc1a3 completed by transcriber
transcribed 42 minutes, saved to podcast/2026-05-04.md
```
`--json`을 써서 machine output으로 create하면 auto-subscribe는 생략됩니다.
### 메시징 출력 잘림
gateway 플랫폼은 메시지 길이 제한이 있어서 `/kanban list`, `/kanban show`, `/kanban tail` 결과가 약 3800자를 넘으면 잘려서 반환됩니다. 전체 출력은 터미널의 `hermes kanban …`를 쓰면 됩니다.
### 자동완성
interactive CLI에서 `/kanban ` 뒤 Tab을 누르면 built-in subcommand hint가 순환됩니다.
## 협업 패턴
새 primitive를 추가하지 않고도 다음 패턴을 지원합니다.
| Pattern | 형태 | 예시 |
|---|---|---|
| **P1 Fan-out** | 같은 role의 sibling N개 | "5개 각도를 병렬 조사" |
| **P2 Pipeline** | scout → editor → writer 체인 | daily brief 조립 |
| **P3 Voting / quorum** | sibling N개 + 1 aggregator | 3명 조사 → 1명 reviewer 결정 |
| **P4 Long-running journal** | 같은 profile + shared dir + cron | Obsidian vault |
| **P5 Human-in-the-loop** | worker block → user comment → unblock | 애매한 의사결정 |
| **P6 `@mention`** | prose 안의 inline routing | `@reviewer look at this` |
| **P7 Thread-scoped workspace** | thread 내부 `/kanban here` | 프로젝트별 gateway thread |
| **P8 Fleet farming** | 한 profile, N subjects | 50개 소셜 계정 |
| **P9 Triage specifier** | rough idea → `triage` → specifier 확장 → `todo` | 한 줄 아이디어를 spec로 승격 |
실전 예시는 `docs/hermes-kanban-v1-spec.pdf` 참고.
## 멀티 테넌트 사용
하나의 specialist fleet가 여러 비즈니스를 담당한다면 task에 tenant를 붙입니다.
```bash
hermes kanban create "monthly report" \
--assignee researcher \
--tenant business-a \
--workspace dir:~/tenants/business-a/data/
```
worker는 `$HERMES_TENANT`를 받고 memory write를 prefix namespace로 분리합니다. board, dispatcher, profile 정의는 공유하고 데이터만 scope됩니다.
## Gateway 알림
gateway에서 `/kanban create …`를 실행하면 원래 chat이 새 task에 자동 구독됩니다. gateway의 background notifier는 몇 초마다 `task_events`를 poll하고 terminal event마다 메시지를 한 번씩 보냅니다. 완료된 task는 worker `--result`의 첫 줄도 함께 보내줍니다.
명시적으로 CLI에서 구독을 관리할 수도 있습니다.
```bash
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
```
task가 `done` 또는 `archived`가 되면 구독은 자동 제거됩니다.
## Runs — 시도 1회당 row 1개
task는 논리적 작업 단위이고, **run**은 그 작업을 실행한 한 번의 시도입니다. dispatcher가 ready task를 claim하면 `task_runs`에 row를 만들고 `tasks.current_run_id`가 그 row를 가리킵니다. 시도가 완료/차단/crash/timeout/spawn-failed/reclaimed로 끝나면 run row는 `outcome`과 함께 닫히고 pointer는 비워집니다.
task와 run을 분리하는 이유:
- 실제 postmortem에 필요한 **전체 시도 이력** 보존
- 어떤 파일이 바뀌었는지, 어떤 테스트를 돌렸는지, reviewer가 무엇을 지적했는지 같은 **시도별 metadata** 저장
run은 structured handoff가 놓이는 곳이기도 합니다.
- `summary` / `--summary` — 사람이 읽는 handoff
- `metadata` / `--metadata` — 자유 형식 JSON dict
- `result` / `--result` — task row에 남는 짧은 log line
예:
```
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로 직접 닫을 수도 있습니다.
```bash
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}'
hermes kanban runs t_abcd
```
주의 사항:
- **Bulk close + `--summary`/`--metadata`는 거부**됩니다. handoff는 run마다 달라야 하기 때문입니다.
- dashboard에서 running task를 다른 상태로 drag하면 in-flight run은 orphan 대신 `reclaimed`로 닫힙니다.
- 한 번도 claim되지 않은 task를 사람이 완료/차단하면 summary/handoff를 잃지 않도록 zero-duration synthetic run이 생성됩니다.
### Forward compatibility
`tasks`의 nullable column 두 개는 v2 workflow routing용으로 예약되어 있습니다.
- `workflow_template_id`
- `current_step_key`
v1 kernel은 routing에는 쓰지 않지만, client가 기록하는 것은 허용합니다.
## Event 레퍼런스
모든 상태 전환은 `task_events`에 row를 append합니다. 각 row는 선택적으로 `run_id`를 포함하므로 UI가 시도 단위로 묶을 수 있습니다.
### Lifecycle
| Kind | Payload | 시점 |
|---|---|---|
| `created` | `{assignee, status, parents, tenant}` | task 생성 |
| `promoted` | — | 부모가 모두 `done`이 되어 `todo → ready` |
| `claimed` | `{lock, expires, run_id}` | dispatcher가 `ready` task를 atomic claim |
| `completed` | `{result_len, summary?}` | worker가 `done`으로 종료 |
| `blocked` | `{reason}` | worker 또는 사람이 `blocked`로 전환 |
| `unblocked` | — | `blocked → ready` |
| `archived` | — | 기본 보드에서 숨김 |
### Edits
| Kind | Payload | 시점 |
|---|---|---|
| `assigned` | `{assignee}` | assignee 변경 |
| `edited` | `{fields}` | title/body 수정 |
| `reprioritized` | `{priority}` | priority 수정 |
| `status` | `{status}` | dashboard drag-drop 등으로 직접 status 변경 |
### Worker telemetry
| Kind | Payload | 시점 |
|---|---|---|
| `spawned` | `{pid}` | worker 프로세스 시작 성공 |
| `heartbeat` | `{note?}` | 장기 작업 중 liveness signal |
| `reclaimed` | `{stale_lock}` | claim TTL 만료, task가 `ready`로 복귀 |
| `crashed` | `{pid, claimer}` | worker PID가 사라짐 |
| `timed_out` | `{pid, elapsed_seconds, limit_seconds, sigkill}` | `max_runtime_seconds` 초과 |
| `spawn_failed` | `{error, failures}` | spawn 시도 1회 실패 |
| `gave_up` | `{failures, error}` | circuit breaker 발동 후 auto-block |
개별 task 이벤트는 `hermes kanban tail <id>`, 보드 전체 이벤트는 `hermes kanban watch`로 볼 수 있습니다.
## 범위 밖
Kanban은 의도적으로 **single-host** 설계입니다. `~/.hermes/kanban.db`는 로컬 SQLite 파일이고, dispatcher는 같은 머신에서 worker를 spawn합니다. 두 호스트가 하나의 board를 공유하는 구조는 지원하지 않습니다.
멀티 호스트가 필요하다면 호스트별 독립 board를 두고, 그 사이를 `delegate_task`나 별도 message queue로 연결해야 합니다.
## 설계 문서
아키텍처, 동시성 정합성, 타 시스템 비교, 구현 계획, 리스크, open question을 포함한 전체 설계 문서는 `docs/hermes-kanban-v1-spec.pdf`에 있습니다. 동작 변경 PR을 넣기 전에는 이 문서를 먼저 읽는 것이 좋습니다.

View file

@ -0,0 +1,170 @@
---
title: "Kanban Orchestrator"
sidebar_label: "Kanban Orchestrator"
description: "Kanban을 통해 작업을 라우팅하는 orchestrator profile을 위한 작업 분해 playbook, specialist roster 관례, anti-temptation 규칙"
---
{/* This page is auto-generated from the skill's SKILL.md by website/scripts/generate-skill-docs.py. Edit the source SKILL.md, not this page. */}
# Kanban Orchestrator
Kanban을 통해 작업을 라우팅하는 orchestrator profile을 위한 작업 분해 playbook, specialist roster 관례, anti-temptation 규칙입니다. "직접 하지 말고 라우팅하라"는 규칙과 기본 lifecycle은 모든 kanban worker의 system prompt에 자동 주입되며, 이 skill은 **특히 orchestrator 역할을 수행할 때** 필요한 더 깊은 운영 지침을 담고 있습니다.
## Skill metadata
| | |
|---|---|
| Source | Bundled (기본 설치) |
| Path | `skills/devops/kanban-orchestrator` |
| Version | `2.0.0` |
| Tags | `kanban`, `multi-agent`, `orchestration`, `routing` |
| Related skills | [`kanban-worker`](./devops-kanban-worker) |
## Reference: full SKILL.md
:::info
아래 내용은 이 skill이 트리거될 때 Hermes가 실제로 로드하는 **전체 skill 정의**입니다. 즉, skill이 활성화되었을 때 agent가 실제 지침으로 보는 텍스트입니다.
:::
# Kanban Orchestrator — 작업 분해 playbook
> **핵심 worker lifecycle**(여기에는 `kanban_create` fan-out 패턴과 "분해만 하고 실행은 하지 말라"는 규칙 포함)은 `KANBAN_GUIDANCE` system-prompt block을 통해 모든 kanban process에 자동 주입됩니다. 이 skill은 **작업 라우팅만을 담당하는 orchestrator profile**일 때 참고하는 심화 playbook입니다.
## 언제 보드를 써야 하는가 (vs. 그냥 직접 해버리는가)
다음 중 하나라도 해당하면 Kanban task를 만드세요.
1. **여러 specialist가 필요할 때** — research + analysis + writing은 서로 다른 3개 profile입니다.
2. **작업이 crash나 restart 이후에도 살아남아야 할 때** — 장기 작업, 반복 작업, 중요한 작업.
3. **사용자가 중간에 끼어들 수 있어야 할 때** — 어느 단계에서든 human-in-the-loop가 필요함.
4. **여러 subtask를 병렬로 돌릴 수 있을 때** — fan-out으로 속도 개선.
5. **review / iteration이 예상될 때** — reviewer profile이 drafter 출력에 대해 반복 루프를 돌 것.
6. **감사 이력이 중요할 때** — board row는 SQLite에 영구 보존됨.
이 중 **하나도 해당하지 않고**, 단순한 one-shot reasoning task라면 Kanban 대신 `delegate_task`를 쓰거나 직접 답변하면 됩니다.
## Anti-temptation 규칙
당신의 직무 설명은 "execute가 아니라 route"입니다. 이를 강제하는 규칙은 다음과 같습니다.
- **직접 일을 실행하지 마세요.** 보통 restricted toolset에는 구현용 terminal/file/code/web조차 포함되지 않습니다. "이건 내가 빨리 고치면 되겠는데"라는 생각이 들면 멈추고, 올바른 specialist에게 task를 만드세요.
- **구체적인 작업이 생기면 무조건 Kanban task를 만들고 assign하세요.** 매번 예외 없이.
- **맞는 specialist가 없다면 어떤 profile을 새로 만들지 사용자에게 물으세요.** "대충 비슷하니까 내가 해도 되겠지"로 넘어가지 마세요.
- **분해하고, 라우팅하고, 요약하는 것 — 그게 전부입니다.**
## 표준 specialist roster (관례)
사용자 환경에서 별도로 profile을 커스터마이즈하지 않았다면, 다음 profile들이 있다고 가정합니다. 실제 환경이 다르면 그에 맞게 조정하고, 확신이 없으면 물으세요.
| Profile | 하는 일 | Typical workspace |
|---|---|---|
| `researcher` | 자료를 읽고, 사실을 수집하고, findings를 정리 | `scratch` |
| `analyst` | 종합, 랭킹, 중복 제거. 여러 `researcher` 출력물을 소비 | `scratch` |
| `writer` | 사용자의 문체에 맞춰 prose 초안 작성 | `scratch` 또는 Obsidian vault의 `dir:` |
| `reviewer` | 결과를 읽고, findings를 남기고, 승인 여부를 게이트 | `scratch` |
| `backend-eng` | 서버 사이드 코드 작성 | `worktree` |
| `frontend-eng` | 클라이언트 사이드 코드 작성 | `worktree` |
| `ops` | 스크립트 실행, 서비스 관리, 배포 처리 | ops scripts repo의 `dir:` |
| `pm` | spec, acceptance criteria 작성 | `scratch` |
## 작업 분해 playbook
### Step 1 — 목표 이해하기
목표가 애매하면 clarifying question을 하세요. 잘못된 fleet를 띄우는 비용이 질문 한 번보다 훨씬 큽니다.
### Step 2 — task graph를 먼저 스케치하기
무엇이든 생성하기 전에, 먼저 사용자에게 graph를 말로 스케치해서 보여주세요. 예를 들어 "Postgres로 마이그레이션할지 분석해줘"라면:
```
T1 researcher research: Postgres cost vs current
T2 researcher research: Postgres performance vs current
T3 analyst synthesize migration recommendation parents: T1, T2
T4 writer draft decision memo parents: T3
```
이 초안을 사용자에게 보여주고, 실제 task를 만들기 전에 수정할 기회를 주세요.
### Step 3 — task 생성 및 link 연결
```python
t1 = kanban_create(
title="research: Postgres cost vs current",
assignee="researcher",
body="Compare estimated infrastructure costs, migration costs, and ongoing ops costs over a 3-year window. Sources: AWS/GCP pricing, team time estimates, current Postgres bills from peers.",
tenant=os.environ.get("HERMES_TENANT"),
)["task_id"]
t2 = kanban_create(
title="research: Postgres performance vs current",
assignee="researcher",
body="Compare query latency, throughput, and scaling characteristics at our expected data volume (~500GB, 10k QPS peak). Sources: benchmark papers, public case studies, pgbench results if easy.",
)["task_id"]
t3 = kanban_create(
title="synthesize migration recommendation",
assignee="analyst",
body="Read the findings from T1 (cost) and T2 (performance). Produce a 1-page recommendation with explicit trade-offs and a go/no-go call.",
parents=[t1, t2],
)["task_id"]
t4 = kanban_create(
title="draft decision memo",
assignee="writer",
body="Turn the analyst's recommendation into a 2-page memo for the CTO. Match the tone of previous decision memos in the team's knowledge base.",
parents=[t3],
)["task_id"]
```
`parents=[...]`는 promotion을 gate합니다. 자식 task는 모든 부모가 `done`이 될 때까지 `todo`에 머물다가, 이후 자동으로 `ready`로 승격됩니다. 수동 조율은 필요 없고 dispatcher와 dependency engine이 처리합니다.
### Step 4 — 자기 자신의 task 완료 처리
만약 당신 자신도 하나의 task로 spawn된 상태였다면(예: `planner` profile이 `T0: "investigate Postgres migration"`을 assign받은 경우), 자신이 만든 task graph를 요약해서 완료 처리하세요.
```python
kanban_complete(
summary="decomposed into T1-T4: 2 researchers parallel, 1 analyst on their outputs, 1 writer on the recommendation",
metadata={
"task_graph": {
"T1": {"assignee": "researcher", "parents": []},
"T2": {"assignee": "researcher", "parents": []},
"T3": {"assignee": "analyst", "parents": ["T1", "T2"]},
"T4": {"assignee": "writer", "parents": ["T3"]},
},
},
)
```
### Step 5 — 사용자에게 보고하기
무엇을 만들었는지 평문으로 설명하세요.
> I've queued 4 tasks:
> - **T1** (researcher): cost comparison
> - **T2** (researcher): performance comparison, in parallel with T1
> - **T3** (analyst): synthesizes T1 + T2 into a recommendation
> - **T4** (writer): turns T3 into a CTO memo
>
> The dispatcher will pick up T1 and T2 now. T3 starts when both finish. You'll get a gateway ping when T4 completes. Use the dashboard or `hermes kanban tail <id>` to follow along.
## 흔한 패턴
**Fan-out + fan-in (research → synthesize):** parent 없는 `researcher` task N개와, 그것들을 모두 parent로 가진 `analyst` task 1개.
**게이트가 있는 pipeline:** `pm → backend-eng → reviewer`. 각 단계는 `parents=[previous_task]`로 연결. reviewer는 block 또는 complete를 수행하고, reviewer가 block하면 operator가 feedback과 함께 unblock해서 다시 spawn합니다.
**동일 profile queue:** 예를 들어 task 50개가 모두 `translator`에게 assign되고 dependency가 없다면, dispatcher가 이를 직렬화합니다. translator는 priority 순서대로 처리하면서 자기 memory에 경험을 축적합니다.
**Human-in-the-loop:** 어떤 task든 `kanban_block()`으로 입력 대기 상태가 될 수 있습니다. `/unblock` 이후 dispatcher가 다시 spawn합니다. comment thread가 전체 컨텍스트를 운반합니다.
## Pitfalls
**재할당 vs. 새 task 생성.** reviewer가 "needs changes"로 block했다면, reviewer task에서 이어지는 **새 task를 만들어야지**, 같은 task를 다시 엄하게 쳐다보며 재실행하면 안 됩니다. 새 task는 원래 구현자 profile에게 assign하세요.
**link 인자 순서.** `kanban_link(parent_id=..., child_id=...)` — parent가 먼저입니다. 순서를 뒤집으면 엉뚱한 task가 `todo`로 내려갈 수 있습니다.
**중간 결과에 따라 graph 모양이 달라질 수 있다면 전체 graph를 미리 만들지 마세요.** T3 구조가 T1/T2 findings에 따라 달라진다면, T3를 "synthesize findings" task로만 두고 그 task의 첫 단계에서 부모 handoff를 읽어 후속 계획을 짜게 하면 됩니다. orchestrator는 또 다른 orchestrator를 spawn할 수 있습니다.
**Tenant 상속.** env에 `HERMES_TENANT`가 설정되어 있다면, 모든 `kanban_create` 호출에 `tenant=os.environ.get("HERMES_TENANT")`를 넣어 child task도 같은 namespace에 머물게 하세요.

View file

@ -0,0 +1,152 @@
---
title: "Kanban Worker — Hermes Kanban worker를 위한 pitfalls, examples, edge cases"
sidebar_label: "Kanban Worker"
description: "Hermes Kanban worker를 위한 pitfalls, examples, edge cases"
---
{/* This page is auto-generated from the skill's SKILL.md by website/scripts/generate-skill-docs.py. Edit the source SKILL.md, not this page. */}
# Kanban Worker
Hermes Kanban worker를 위한 pitfalls, examples, edge cases 문서입니다. lifecycle 자체는 `KANBAN_GUIDANCE`로 모든 worker의 system prompt에 자동 주입되며(`agent/prompt_builder.py`), 이 skill은 **특정 시나리오에서 더 깊은 상세 지침이 필요할 때** 로드하는 자료입니다.
## Skill metadata
| | |
|---|---|
| Source | Bundled (기본 설치) |
| Path | `skills/devops/kanban-worker` |
| Version | `2.0.0` |
| Tags | `kanban`, `multi-agent`, `collaboration`, `workflow`, `pitfalls` |
| Related skills | [`kanban-orchestrator`](./devops-kanban-orchestrator) |
## Reference: full SKILL.md
:::info
아래는 이 skill이 트리거될 때 Hermes가 실제로 로드하는 **전체 skill 정의**입니다. 즉, skill이 활성화되었을 때 agent가 실제 instruction으로 보는 내용입니다.
:::
# Kanban Worker — Pitfalls and Examples
> 이 skill이 보이는 이유는 Hermes Kanban dispatcher가 당신을 `--skills kanban-worker`와 함께 worker로 spawn했기 때문입니다. dispatched worker마다 자동으로 로드됩니다. **lifecycle**(6단계: orient → work → heartbeat → block/complete)은 system prompt에 자동 주입되는 `KANBAN_GUIDANCE` block에도 들어 있습니다. 이 skill은 그보다 더 구체적인 심화 설명입니다: 좋은 handoff 형태, retry 진단, edge case 등.
## Workspace handling
workspace 종류에 따라 `$HERMES_KANBAN_WORKSPACE` 안에서의 행동 방식이 달라집니다.
| Kind | 의미 | 작업 방식 |
|---|---|---|
| `scratch` | 새 tmp 디렉터리, 오직 당신만 사용 | 자유롭게 read/write 가능; task가 archived되면 GC 대상 |
| `dir:<path>` | 공유되는 persistent directory | 다른 run이 당신이 쓴 내용을 읽게 됨. 장기 상태처럼 다뤄야 함. path는 항상 절대경로임 (kernel이 상대경로 거부) |
| `worktree` | 해당 경로의 Git worktree | `.git`이 없다면 먼저 main repo에서 `git worktree add <path> <branch>`를 실행한 뒤 cd하여 작업. 여기서 commit 수행 |
## Tenant isolation
`$HERMES_TENANT`가 설정되어 있으면 이 task는 특정 tenant namespace에 속합니다. persistent memory를 읽거나 쓸 때는 tenant prefix를 붙여서 context가 다른 tenant로 새지 않게 하세요.
- Good: `business-a: Acme is our biggest customer`
- Bad (leaks): `Acme is our biggest customer`
## 좋은 summary + metadata 형태
`kanban_complete(summary=..., metadata=...)` handoff는 downstream worker가 당신의 작업을 읽는 기본 채널입니다. 잘 작동하는 패턴은 다음과 같습니다.
**코딩 task:**
```python
kanban_complete(
summary="shipped rate limiter — token bucket, keys on user_id with IP fallback, 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 requests"],
},
)
```
**리서치 task:**
```python
kanban_complete(
summary="3 competing libraries reviewed; vLLM wins on throughput, SGLang on latency, Tensorrt-LLM on memory efficiency",
metadata={
"sources_read": 12,
"recommendation": "vLLM",
"benchmarks": {"vllm": 1.0, "sglang": 0.87, "trtllm": 0.72},
},
)
```
**리뷰 task:**
```python
kanban_complete(
summary="reviewed PR #123; 2 blocking issues found (SQL injection in /search, missing CSRF on /settings)",
metadata={
"pr_number": 123,
"findings": [
{"severity": "critical", "file": "api/search.py", "line": 42, "issue": "raw SQL concat"},
{"severity": "high", "file": "api/settings.py", "issue": "missing CSRF middleware"},
],
"approved": False,
},
)
```
`metadata`는 downstream parser(reviewer, aggregator, scheduler)가 prose를 다시 읽지 않고도 사용할 수 있는 형태로 구성하세요.
## 빨리 답을 받을 수 있는 block reason
나쁜 예: `"stuck"` — 사람은 무슨 일이 막혔는지 알 수 없습니다.
좋은 예: **어떤 결정을 내려야 하는지 한 문장으로 특정**하고, 긴 배경 설명은 comment로 남기세요.
```python
kanban_comment(
task_id=os.environ["HERMES_KANBAN_TASK"],
body="Full context: I have user IPs from Cloudflare headers but some users are behind NATs with thousands of peers. Keying on IP alone causes false positives.",
)
kanban_block(reason="Rate limit key choice: IP (simple, NAT-unsafe) or user_id (requires auth, skips anonymous endpoints)?")
```
block message는 dashboard / gateway notifier에 그대로 나타나는 짧은 문구이고, comment는 사람이 task를 열었을 때 읽는 깊은 배경 설명입니다.
## 보낼 가치가 있는 heartbeat
좋은 heartbeat는 진척을 이름 붙여서 말합니다. 예: `"epoch 12/50, loss 0.31"`, `"scanned 1.2M/2.4M rows"`, `"uploaded 47/120 videos"`.
나쁜 heartbeat는 `"still working"`, 빈 note, 초단위 남발입니다. 몇 분에 한 번이면 충분하고, 2분 이하 작업이면 아예 보내지 않아도 됩니다.
## Retry 시나리오
`kanban_show` 결과의 `runs: [...]`에 닫힌 run이 하나 이상 있다면 당신은 retry worker입니다. 이전 run의 `outcome` / `summary` / `error`가 무엇이 잘 안 됐는지 알려줍니다. **같은 경로를 반복하지 마세요.** 전형적인 retry 진단은 아래와 같습니다.
- `outcome: "timed_out"` — 이전 시도가 `max_runtime_seconds`에 걸렸습니다. 작업을 chunk로 나누거나 더 짧게 만들어야 할 수 있습니다.
- `outcome: "crashed"` — OOM 또는 segfault. 메모리 사용량을 줄이세요.
- `outcome: "spawn_failed"` + `error: "..."` — 대개 profile 설정 문제(credential 누락, PATH 불량). 무작정 재시도하지 말고 `kanban_block`으로 사람에게 물으세요.
- `outcome: "reclaimed"` + `summary: "task archived..."` — operator가 이전 run 도중 task를 archive했습니다. 아마 지금 실행되면 안 되는 상태일 수 있으니 status를 먼저 확인하세요.
- `outcome: "blocked"` — 이전 시도가 block 상태였고, unblock comment가 thread에 달려 있을 가능성이 큽니다.
## Do NOT
- `kanban_create` 대신 `delegate_task`를 cross-agent handoff로 쓰지 마세요. `delegate_task`는 **당신 자신의 run 내부**에서 쓰는 짧은 reasoning subtask용이고, `kanban_create`는 API loop를 넘어서 살아남는 cross-agent handoff용입니다.
- task body에 명시되지 않았다면 `$HERMES_KANBAN_WORKSPACE` 밖의 파일을 수정하지 마세요.
- follow-up task를 자기 자신에게 assign하지 마세요. 올바른 specialist에게 assign하세요.
- 실제로 끝내지 않은 task를 completed로 처리하지 마세요. 그 대신 block하세요.
## Pitfalls
**dispatch와 worker startup 사이에 task 상태가 바뀔 수 있습니다.** dispatcher가 claim한 뒤 실제 프로세스가 부팅되기 전까지 task가 blocked, reassigned, archived 되었을 수 있습니다. 항상 먼저 `kanban_show`를 호출하세요. 결과가 `blocked` 또는 `archived`라면 중단해야 합니다. 지금 실행되면 안 되는 상태입니다.
**workspace에 stale artifact가 남아 있을 수 있습니다.** 특히 `dir:``worktree` workspace는 이전 run의 파일이 남아 있을 수 있습니다. comment thread를 읽으세요. 대개 왜 다시 실행되는지, 현재 workspace 상태가 어떤지를 설명하고 있습니다.
**guidance가 있는데 CLI에 의존하지 마세요.** `kanban_*` tool은 모든 terminal backend(Docker, Modal, SSH)에서 동작합니다. 반면 terminal tool 안에서 `hermes kanban <verb>`를 실행하면, containerized backend에서는 CLI가 설치돼 있지 않아 실패할 수 있습니다. 확신이 없을 때는 tool을 쓰세요.
## CLI fallback (스크립팅용)
각 tool에는 사람/스크립트를 위한 CLI 대응물이 있습니다.
- `kanban_show``hermes kanban show <id> --json`
- `kanban_complete``hermes kanban complete <id> --summary "..." --metadata '{...}'`
- `kanban_block``hermes kanban block <id> "reason"`
- `kanban_create``hermes kanban create "title" --assignee <profile> [--parent <id>]`
- 등등
agent 내부에서는 tool을 쓰고, CLI는 터미널 앞의 인간을 위한 인터페이스라고 생각하면 됩니다.