Перейти к содержанию

Kanban

На этой странице

Хотите пошаговое руководство? Прочитайте Учебник по Kanban — четыре пользовательские истории (одиночный разработчик, управление фермой, пайплайн ролей с повторами и автоматический выключатель) со скриншотами панели управления для каждой. Эта страница — справочная; учебник — повествовательный.

Hermes Kanban — это надёжная доска задач, общая для всех ваших профилей Hermes, которая позволяет нескольким именованным агентам совместно работать без хрупких внутрипроцессных роёв подагентов. Каждая задача — это строка в ~/.hermes/kanban.db; каждая передача — это строка, которую любой может прочитать и записать; каждый рабочий — это полноценный OS-процесс с собственной идентичностью.

Два интерфейса: модель общается через инструменты, вы — через CLI

У доски есть два входа, оба работают с одной и той же ~/.hermes/kanban.db:

  • Агенты управляют доской через специальный набор инструментов kanban_*kanban_show, kanban_complete, kanban_block, kanban_heartbeat, kanban_comment, kanban_create, kanban_link. Диспетчер порождает каждого рабочего с уже включёнными в его схему этими инструментами; модель читает свою задачу и передаёт работу, вызывая их напрямую, а не через вызов hermes kanban в оболочке. См. раздел Как рабочие взаимодействуют с доской ниже.
  • Вы (и скрипты, и cron) управляете доской через hermes kanban … в CLI, через /kanban … как слэш-команду или через панель управления. Они предназначены для людей и автоматизации — для мест, за которыми не стоит модель, вызывающая инструменты.

Оба интерфейса проходят через один и тот же слой kanban_db, поэтому чтение всегда показывает согласованное представление, а записи не расходятся. В остальной части этой страницы приведены примеры для CLI, потому что их легко копировать, но у каждой CLI-команды есть эквивалентный вызов инструмента, который использует модель.

Это форма, которая охватывает рабочие нагрузки, с которыми delegate_task не справляется:

  • Триаж исследований — параллельные исследователи + аналитик + писатель, человек в цикле.
  • Плановые операции — повторяющиеся ежедневные брифинги, которые накапливают журнал за недели.
  • Цифровые двойники — постоянные именованные ассистенты (inbox-triage, ops-review), накапливающие память со временем.
  • Инженерные пайплайны — декомпозиция → реализация в параллельных рабочих деревьях → ревью → итерация → PR.
  • Фермерская работа — один специалист, управляющий N субъектами (50 соцсетей, 12 отслеживаемых сервисов).

Полное обоснование дизайна, сравнительный анализ с Cline Kanban / Paperclip / NanoClaw / Google Gemini Enterprise и восемь канонических шаблонов совместной работы см. в docs/hermes-kanban-v1-spec.pdf в репозитории.

Kanban vs delegate_task

Они выглядят похоже, но это не один и тот же примитив.

delegate_task Kanban
Форма RPC-вызов (fork → join) Надёжная очередь сообщений + конечный автомат
Родитель Блокируется до возврата потомка Создал и забыл после create
Идентичность потомка Анонимный подагент Именованный профиль с постоянной памятью
Возобновляемость Нет — не удалось = не удалось Блок → разблокировать → перезапуск; сбой → возврат
Человек в цикле Не поддерживается Комментарий / разблокировка в любой момент
Агентов на задачу Один вызов = один подагент N агентов за время жизни задачи (повтор, ревью, доработка)
Аудит-след Теряется при сжатии контекста Постоянные строки в SQLite навсегда
Координация Иерархическая (вызывающий → вызываемый) Равноправная — любой профиль читает/пишет любую задачу

Одно предложение для различия: delegate_task — это вызов функции; Kanban — это очередь работы, где каждая передача — это строка, которую любой профиль (или человек) может увидеть и отредактировать.

Используйте delegate_task, когда родительскому агенту нужен короткий обдуманный ответ перед продолжением, без участия людей, результат возвращается в контекст родителя.

Используйте Kanban, когда работа пересекает границы агентов, должна пережить перезапуски, может потребовать ввода человека, может быть подхвачена другой ролью или должна быть обнаружима постфактум.

Они сосуществуют: рабочий kanban может внутренне вызывать delegate_task во время своего выполнения.

Основные понятия

  • Доска (Board) — самостоятельная очередь задач с собственной БД SQLite, директорией рабочих областей и циклом диспетчера. Одна установка может иметь много досок (например, по одной на проект, репозиторий или домен); см. раздел Доски (мультипроект) ниже. Пользователи с одним проектом работают на доске default и никогда не видят слово «доска» за пределами этого раздела документации.
  • Задача (Task) — строка с заголовком, необязательным телом, одним исполнителем (имя профиля), статусом (triage | todo | ready | running | blocked | done | archived), необязательным пространством имён арендатора и необязательным ключом идемпотентности (дедупликация для повторяющейся автоматизации).
  • Связь (Link) — строка task_links, записывающая зависимость родитель → потомок. Диспетчер переводит todo → ready, когда все родители переходят в done.
  • Комментарий (Comment) — протокол меж-агентного взаимодействия. Агенты и люди добавляют комментарии; когда рабочий (повторно) запускается, он читает всю ветку комментариев как часть своего контекста.
  • Рабочая область (Workspace) — директория, в которой работает рабочий. Три вида:
  • scratch (по умолчанию) — свежая временная директория в ~/.hermes/kanban/workspaces/<id>/ (или ~/.hermes/kanban/boards/<slug>/workspaces/<id>/ для нестандартных досок).
  • dir:<path> — существующая общая директория (хранилище Obsidian, директория почтовых операций, папка для каждой учётной записи). Должен быть абсолютным путём. Относительные пути, такие как dir:../tenants/foo/, отклоняются при диспетчеризации, потому что они разрешались бы относительно текущей рабочей директории диспетчера, что двусмысленно и представляет собой вектор побега для запутанного посредника. В остальном путь считается доверенным — это ваша машина, ваша файловая система, рабочий работает с вашим uid. Это модель угроз доверенного локального пользователя; kanban по замыслу однопользовательский.
  • worktree — git worktree в .worktrees/<id>/ для задач программирования. Создаётся через git worktree add на стороне рабочего.
  • Диспетчер (Dispatcher) — долгоживущий цикл, который каждые N секунд (по умолчанию 60): возвращает устаревшие захваты, возвращает упавшие рабочие (PID исчез, но TTL ещё не истёк), переводит готовые задачи, атомарно захватывает, порождает назначенные профили. Работает внутри шлюза по умолчанию (kanban.dispatch_in_gateway: true). Один диспетчер обрабатывает все доски за один тик; рабочие запускаются с установленной переменной HERMES_KANBAN_BOARD, чтобы не видеть другие доски. После ~5 последовательных ошибок запуска одной и той же задачи диспетчер автоматически блокирует её с последней ошибкой в качестве причины — предотвращает бесконечные попытки для задач, профиль которых не существует, рабочая область не монтируется и т.д.
  • Арендатор (Tenant) — необязательное строковое пространство имён внутри доски. Один флот специалистов может обслуживать несколько бизнесов (--tenant business-a) с изоляцией данных через путь рабочей области и префикс ключа памяти. Арендаторы — это мягкий фильтр; доски — это жёсткая граница изоляции.

Доски (мультипроект)

Доски позволяют разделять несвязанные потоки работы — по одному на проект, репозиторий или домен — в изолированные очереди. Новая установка имеет ровно одну доску с именем default (БД в ~/.hermes/kanban.db для обратной совместимости). Пользователи, которым нужен только один поток работы, никогда не должны знать о досках; эта функция опциональна.

Изоляция на доску абсолютна:

  • Отдельная БД SQLite на доску (~/.hermes/kanban/boards/<slug>/kanban.db).
  • Отдельные директории workspaces/ и logs/.
  • Рабочие, порождённые для задачи, видят только задачи своей доски — диспетчер устанавливает HERMES_KANBAN_BOARD в окружение потомка, и каждый инструмент kanban_*, доступный рабочему, читает её.
  • Связывание задач между досками не разрешено (чтобы схема оставалась простой; если вам действительно нужны межпроектные ссылки, используйте упоминания в свободном тексте и ищите их по id вручную).

Управление досками через CLI

[code] # See what's on disk. Fresh installs show only "default". hermes kanban boards list

# Create a new board.
hermes kanban boards create atm10-server \
    --name "ATM10 Server" \
    --description "Minecraft modded server ops" \
    --icon 🎮 \
    --switch                   # optional: make it the active board

# Operate on a specific board without switching.
hermes kanban --board atm10-server list
hermes kanban --board atm10-server create "Restart ATM server" --assignee ops

# Change which board is "current" for subsequent calls.
hermes kanban boards switch atm10-server
hermes kanban boards show             # who's active right now?

# Rename the display name (the slug is immutable — it's the directory name).
hermes kanban boards rename atm10-server "ATM10 (Prod)"

# Archive (default) — moves the board's dir to boards/_archived/<slug>-<ts>/.
# Recoverable by moving the dir back.
hermes kanban boards rm atm10-server

# Hard delete — `rm -rf` the board dir. No recovery.
hermes kanban boards rm atm10-server --delete

[/code]

Порядок разрешения доски (наивысший приоритет первый):

  1. Явный --board <slug> при вызове CLI.
  2. Переменная окружения HERMES_KANBAN_BOARD (устанавливается диспетчером при запуске рабочего, чтобы рабочие не видели другие доски).
  3. ~/.hermes/kanban/current — slug, сохранённый командой hermes kanban boards switch.
  4. default.

Slug валидируются: строчные буквы латиницы + цифры + дефисы + подчёркивания, 1-64 символа, должны начинаться с буквы или цифры. Ввод в верхнем регистре автоматически приводится к нижнему. Всё остальное (слеши, пробелы, точки, ..) отклоняется на уровне CLI, чтобы трюки с обходом пути не могли назвать доску.

Управление досками через панель управления

hermes dashboard → вкладка Kanban показывает переключатель досок вверху, как только появляется более одной доски (или любая доска имеет задачи). Пользователи с одной доской видят только небольшую кнопку + New board; переключатель скрыт, пока не потребуется.

  • Board dropdown — выберите активную доску. Ваш выбор сохраняется в localStorage браузера, так что он переживает перезагрузки, не сдвигая указатель current CLI из-под терминала, который вы оставили открытым.
  • + New board — открывает модальное окно с запросом slug, отображаемого имени, описания и иконки. Опция автоматического переключения на новую доску.
  • Archive — показывается только для не-default досок. Подтверждает, затем перемещает директорию доски в boards/_archived/.

Все конечные точки API панели управления принимают ?board=<slug> для привязки к доске. WebSocket событий привязывается к доске при соединении; переключение в интерфейсе открывает новый WebSocket для новой доски.

Быстрый старт

Команды ниже — вы (человек) настраиваете доску и создаёте задачи. После назначения задачи диспетчер запускает назначенный профиль как рабочего, и оттуда модель управляет задачей через вызовы инструментов kanban_*, а не через CLI-команды — см. Как рабочие взаимодействуют с доской.

[code] # 1. Create the board (you) hermes kanban init

# 2. Start the gateway (hosts the embedded dispatcher)
hermes gateway start

# 3. Create a task (you — or an orchestrator agent via kanban_create)
hermes kanban create "research AI funding landscape" --assignee researcher

# 4. Watch activity live (you)
hermes kanban watch

# 5. See the board (you)
hermes kanban list
hermes kanban stats

[/code]

Когда диспетчер подхватывает t_abcd и запускает профиль researcher, первое, что делает модель этого рабочего — вызывает kanban_show(), чтобы прочитать свою задачу. Она не запускает hermes kanban show t_abcd.

Диспетчер, встроенный в шлюз (по умолчанию)

Диспетчер работает внутри процесса шлюза. Ничего устанавливать не нужно, никакого отдельного сервиса для управления — если шлюз работает, готовые задачи будут подхвачены на следующем тике (60 с по умолчанию).

[code] # config.yaml kanban: dispatch_in_gateway: true # default dispatch_interval_seconds: 60 # default [/code]

Переопределите флаг конфигурации во время выполнения через HERMES_KANBAN_DISPATCH_IN_GATEWAY=0 для отладки. Применяется стандартный надзор шлюза: запустите hermes gateway start напрямую или подключите шлюз как systemd-юнит пользователя (см. документацию шлюза). Без работающего шлюза задачи со статусом ready остаются на месте, пока шлюз не появится — hermes kanban create предупреждает об этом при создании.

Запуск hermes kanban daemon как отдельного процесса устарел; используйте шлюз. Если вы действительно не можете запустить шлюз (политика хоста безголового режима запрещает долгоживущие сервисы и т.п.), запасной вариант --force сохраняет старый отдельный демон на один цикл релиза, но одновременный запуск встроенного в шлюз диспетчера И отдельного демона для одной и той же kanban.db вызывает гонки захватов и не поддерживается.

Идемпотентное создание (для автоматизации / вебхуков)

[code] # First call creates the task. Any subsequent call with the same key # returns the existing task id instead of duplicating. hermes kanban create "nightly ops review" \ --assignee ops \ --idempotency-key "nightly-ops-$(date -u +%Y-%m-%d)" \ --json [/code]

Массовые CLI-команды

Все команды жизненного цикла принимают несколько id, чтобы вы могли обработать партию одной командой:

[code] 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 [/code]

Как рабочие взаимодействуют с доской

Рабочие не запускают hermes kanban в оболочке. Когда диспетчер запускает рабочего, он устанавливает HERMES_KANBAN_TASK=t_abcd в окружение потомка, и эта переменная окружения активирует специальный набор kanban-инструментов в схеме модели — семь инструментов, которые читают и изменяют доску напрямую через Python-слой kanban_db, точно так же, как это делает CLI. Работающий рабочий вызывает их как любые другие инструменты; он никогда не видит CLI hermes kanban и не нуждается в нём.

Инструмент Назначение Обязательные параметры
kanban_show Прочитать текущую задачу (заголовок, тело, предыдущие попытки, передачи от родителя, комментарии, полный предварительно отформатированный worker_context). По умолчанию использует id задачи из окружения.
kanban_complete Завершить со структурированной передачей summary + metadata. хотя бы один из summary / result
kanban_block Эскалировать для ввода человека с указанием reason. reason
kanban_heartbeat Сигнализировать о жизнеспособности во время долгих операций. Чистый побочный эффект.
kanban_comment Добавить постоянную заметку в ветку задачи. task_id, body
kanban_create (Оркестраторы) разветвляться на дочерние задачи с указанием assignee, опционально parents, skills и т.д. title, assignee
kanban_link (Оркестраторы) добавить ребро зависимости parent_id → child_id постфактум. parent_id, child_id

Типичный оборот рабочего выглядит так:

[code] # Model's tool calls, in order: kanban_show() # no args — uses HERMES_KANBAN_TASK # (model reads the returned worker_context, does the work via terminal/file tools) kanban_heartbeat(note="halfway through — 4 of 8 files transformed") # (more work) kanban_complete( summary="migrated limiter.py to token-bucket; added 14 tests, all pass", metadata={"changed_files": ["limiter.py", "tests/test_limiter.py"], "tests_run": 14}, ) [/code]

Оркестратор вместо этого разветвляется:

[code] kanban_show() kanban_create( title="research ICP funding 2024-2026", assignee="researcher-a", body="focus on seed + series A, North America, AI-adjacent", ) # → returns {"task_id": "t_r1", ...} kanban_create(title="research ICP funding — EU angle", assignee="researcher-b", body="…") # → returns {"task_id": "t_r2", ...} kanban_create( title="synthesize findings into launch brief", assignee="writer", parents=["t_r1", "t_r2"], # promotes to ready when both complete body="one-pager, 300 words, neutral tone", ) kanban_complete(summary="decomposed into 2 research tasks + 1 writer; linked dependencies") [/code]

Три инструмента «(Оркестраторы)» — kanban_create, kanban_link и kanban_comment для чужих задач — доступны каждому рабочему; соглашение (подкреплённое навыком kanban-orchestrator) гласит, что профили рабочих не разветвляются, а профили оркестраторов не выполняют.

Почему инструменты вместо вызова hermes kanban в оболочке

Три причины:

  1. Переносимость бэкенда. Рабочие, чей инструмент терминала указывает на удалённый бэкенд (Docker / Modal / Singularity / SSH), запускали бы hermes kanban complete внутри контейнера, где hermes не установлен, а ~/.hermes/kanban.db не смонтирован. Инструменты kanban работают в собственном Python-процессе агента и всегда достигают ~/.hermes/kanban.db независимо от бэкенда терминала.
  2. Нет хрупкости экранирования оболочки. Передача --metadata '{"files": [...]}' через shlex + argparse — это скрытая грабля. Структурированные аргументы инструмента полностью её обходят.
  3. Лучшие ошибки. Результаты инструментов — это структурированный JSON, с которым модель может работать, а не строки stderr, которые нужно парсить.

Нулевой след в схеме для обычных сессий. Обычная сессия hermes chat не имеет ни одного инструмента kanban_* в своей схеме. check_fn каждого инструмента возвращает True только когда установлена HERMES_KANBAN_TASK, что происходит только когда диспетчер запустил этот процесс. Никакого раздувания инструментов для пользователей, которые никогда не касаются kanban.

Навыки kanban-worker и kanban-orchestrator учат модель, какой инструмент вызывать когда и в каком порядке.

Рекомендуемые свидетельства передачи

kanban_complete(summary=..., metadata={...}) намеренно гибок: summary — это читаемое человеком закрытие, а metadata — это машиночитаемая передача, которую downstream-агенты, рецензенты или панели управления могут использовать без сканирования прозы.

Для инженерных задач и задач ревью предпочтительна следующая опциональная форма метаданных:

[code] { "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"] } [/code]

Эти ключи — соглашение, а не требование схемы. Полезное свойство в том, что каждый рабочий оставляет достаточно свидетельств, чтобы следующий читатель мог быстро ответить на четыре вопроса:

  1. Что изменилось?
  2. Как это было проверено?
  3. Что может разблокировать или повторить это, если не удастся?
  4. Какой риск сознательно остаётся открытым?

Храните секреты, сырые логи, токены, OAuth-материалы и несвязанные транскрипты вне metadata. Вместо этого сохраняйте указатели и сводки. Если у задачи нет файлов или тестов, явно укажите это в summary и используйте metadata для тех свидетельств, которые существуют, такие как URL источников, id тикетов или шаги ручного ревью.

Навык рабочего

Любой профиль, который должен иметь возможность работать с задачами kanban, должен загрузить навык kanban-worker. Он учит рабочего полному жизненному циклу в вызовах инструментов, а не в CLI-командах:

  1. При запуске вызвать kanban_show(), чтобы прочитать заголовок + тело + передачи от родителя + предыдущие попытки + полную ветку комментариев.
  2. cd $HERMES_KANBAN_WORKSPACE (через инструмент терминала) и выполнять работу там.
  3. Вызывать kanban_heartbeat(note="...") каждые несколько минут во время долгих операций.
  4. Завершить с помощью kanban_complete(summary="...", metadata={...}) или kanban_block(reason="..."), если застряли.

Загрузите его (это вы, устанавливаете в профиль — не вызов инструмента):

[code] hermes skills install devops/kanban-worker [/code]

Диспетчер также автоматически передаёт --skills kanban-worker при запуске каждого рабочего, так что у рабочего всегда есть библиотека паттернов, даже если конфигурация навыков профиля по умолчанию не включает его.

Прикрепление дополнительных навыков к конкретной задаче

Иногда одной задаче требуется специализированный контекст, который профиль исполнителя не носит по умолчанию — задача перевода, требующая навык translation, задача ревью, требующая github-code-review, аудит безопасности, требующий security-pr-audit. Вместо редактирования профиля исполнителя каждый раз прикрепите навыки непосредственно к задаче.

От агента-оркестратора (обычный случай — один агент направляет работу другому), используйте массив skills инструмента kanban_create:

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

[/code]

От человека (CLI / слэш-команда), повторяйте --skill для каждого:

[code] 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

[/code]

Из панели управления, введите навыки через запятую в поле skills встроенной формы создания.

Эти навыки дополняют встроенный kanban-worker — диспетчер передаёт по одному флагу --skills <name> для каждого (и для встроенного), так что рабочий запускается со всеми загруженными. Имена навыков должны соответствовать навыкам, которые действительно установлены в профиле исполнителя (выполните hermes skills list, чтобы увидеть доступные); установка на лету не производится.

Навык оркестратора

Воспитанный оркестратор не делает работу сам. Он декомпозирует цель пользователя на задачи, связывает их, назначает каждую специалисту и отступает. Навык kanban-orchestrator кодирует это как паттерны вызовов инструментов: правила анти-соблазна, стандартный список специалистов (researcher, writer, analyst, backend-eng, reviewer, ops) и плейбук декомпозиции, основанный на kanban_create / kanban_link / kanban_comment.

Канонический оборот оркестратора (два параллельных исследователя, передающих писателю):

[code] # 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"], # promoted to 'ready' when both researchers complete body="one-pager, neutral tone, cite sources inline", ) # → t_w1 # Optional: add cross-cutting deps discovered later without re-creating tasks 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", ) [/code]

Загрузите его в свой профиль оркестратора:

[code] hermes skills install devops/kanban-orchestrator [/code]

Для лучшего результата объедините его с профилем, чьи наборы инструментов ограничены операциями с доской (kanban, gateway, memory), чтобы оркестратор буквально не мог выполнять задачи реализации, даже если попытается.

Панель управления (GUI)

CLI-команда /kanban и слэш-команда достаточны для безголового управления доской, но визуальная доска часто является правильным интерфейсом для людей-в-цикле: триаж, межпрофильный надзор, чтение веток комментариев и перетаскивание карточек между колонками. Hermes поставляет это как встроенный плагин панели управления в plugins/kanban/ — не основная функция, не отдельный сервис — следуя модели, описанной в Расширение панели управления.

Откройте его с помощью:

[code] hermes kanban init # one-time: create kanban.db if not already present hermes dashboard # "Kanban" tab appears in the nav, after "Skills" [/code]

Что даёт плагин

  • Вкладка Kanban, показывающая одну колонку на статус: triage, todo, ready, running, blocked, done (плюс archived, когда переключатель включён).
  • triage — это парковочная колонка для сырых идей, которые спецификатор должен проработать. Задачи, созданные с hermes kanban create --triage (или через встроенное создание в колонке Triage), попадают сюда, и диспетчер не трогает их, пока человек или спецификатор не переведёт их в todo / ready.
  • Карточки показывают id задачи, заголовок, значок приоритета, тег арендатора, назначенный профиль, количество комментариев/связей, индикатор прогресса (N/M дочерних задач выполнено, когда у задачи есть зависимые) и «создано N назад». Флажок на каждой карточке включает множественный выбор.
  • Полосы по профилям внутри Running — флажок на панели инструментов включает подгруппировку колонки Running по исполнителю.
  • Живые обновления через WebSocket — плагин отслеживает таблицу task_events (только добавление) с коротким интервалом опроса; доска отражает изменения в тот момент, когда любой профиль (CLI, шлюз или другая вкладка панели управления) действует. Перезагрузки дебаунсятся, чтобы всплеск событий вызывал единую повторную выборку.
  • Перетаскивание карточек между колонками для изменения статуса. Сброс отправляет PATCH /api/plugins/kanban/tasks/:id, который проходит через тот же код kanban_db, что и CLI — три интерфейса никогда не могут разойтись. Перемещения в деструктивные статусы (done, archived, blocked) запрашивают подтверждение. Сенсорные устройства используют запасной вариант на основе указателя, так что доской можно пользоваться с планшета.
  • Встроенное создание — нажмите + в заголовке любой колонки, чтобы ввести заголовок, исполнителя, приоритет и (опционально) родительскую задачу из выпадающего списка всех существующих задач. Создание из колонки Triage автоматически помещает новую задачу в триаж.
  • Множественный выбор с массовыми действиями — shift/ctrl-клик по карточке или установка её флажка добавляет её в выделение. Панель массовых действий появляется вверху с пакетными переходами статусов, архивированием и переназначением (через выпадающий список профилей или «(снять назначение)»). Деструктивные пакеты сначала запрашивают подтверждение. Частичные ошибки по каждому id сообщаются без прерывания остальных.
  • Клик по карточке (без shift/ctrl) открывает боковую панель (Escape или клик вне закрывает) с:
  • Редактируемым заголовком — нажмите на заголовок, чтобы переименовать.
  • Редактируемым исполнителем / приоритетом — нажмите на строку метаданных, чтобы изменить.
  • Редактируемым описанием — по умолчанию отображается в markdown (заголовки, жирный, курсив, строчный код, блоки кода, ссылки http(s) / mailto:, маркированные списки), с кнопкой «редактировать», которая заменяет на textarea. Markdown-рендеринг — это крошечный, безопасный от XSS рендерер — каждая подстановка выполняется над HTML-экранированным вводом, пропускаются только ссылки http(s) / mailto:, и всегда устанавливаются target="_blank" + rel="noopener noreferrer".
  • Редактором зависимостей — список чипов родителей и потомков, каждый с × для открепления, плюс выпадающие списки всех остальных задач для добавления нового родителя или потомка. Попытки создать цикл отклоняются на серверной стороне с чётким сообщением.
  • Строкой действий со статусом (→ triage / → ready / → running / block / unblock / complete / archive) с запросами подтверждения для деструктивных переходов.
  • Разделом результата (также отображается в markdown), веткой комментариев с отправкой по Enter, последними 20 событиями.
  • Фильтры панели инструментов — полнотекстовый поиск, выпадающий список арендаторов (по умолчанию dashboard.kanban.default_tenant из config.yaml), выпадающий список исполнителей, переключатель «показать архивные», переключатель «полосы по профилям» и кнопка Nudge dispatcher, чтобы не ждать следующего тика в 60 с.

Визуально цель — знакомый макет Linear / Fusion: тёмная тема, заголовки колонок со счётчиками, цветные точки статуса, чип-таблетки для приоритета и арендатора. Плагин читает только CSS-переменные темы (--color-*, --radius, --font-mono, ...), поэтому он автоматически перекрашивается под любую активную тему панели управления.

Архитектура

GUI — это строго слой чтения-через-БД + записи-через-kanban_db без собственной доменной логики:

[code] ┌────────────────────────┐ 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) │ └────────────────────────┘ [/code]

REST-поверхность

Все маршруты смонтированы в /api/plugins/kanban/ и защищены эфемерным токеном сессии панели управления:

Метод Путь Назначение
GET /board?tenant=<name>&include_archived=… Полная доска, сгруппированная по колонкам статуса, плюс арендаторы и исполнители для фильтров
GET /tasks/:id Задача + комментарии + события + связи
POST /tasks Создать (обёртка над kanban_db.create_task, принимает triage: bool и parents: [id, …])
PATCH /tasks/:id Статус / исполнитель / приоритет / заголовок / тело / результат
POST /tasks/bulk Применить тот же патч (статус / архив / исполнитель / приоритет) ко всем id в ids. Частичные ошибки сообщаются без прерывания остальных
POST /tasks/:id/comments Добавить комментарий
POST /links Добавить зависимость (parent_idchild_id)
DELETE /links?parent_id=…&child_id=… Удалить зависимость
POST /dispatch?max=…&dry_run=… Подтолкнуть диспетчера — пропустить ожидание в 60 с
GET /config Прочитать настройки dashboard.kanban из config.yamldefault_tenant, lane_by_profile, include_archived_by_default, render_markdown
WS /events?since=<event_id> Живой поток строк task_events

Каждый обработчик — это тонкая обёртка — плагин составляет ~700 строк Python (роутер + WebSocket-отслеживание + пакетный обработчик + читатель конфигурации) и не добавляет новой бизнес-логики. Маленький хелпер _conn() автоматически инициализирует kanban.db при каждом чтении и записи, так что свежая установка работает, открыл ли пользователь сначала панель управления, обратился напрямую к REST API или выполнил hermes kanban init.

Конфигурация панели управления

Любой из этих ключей в разделе dashboard.kanban в ~/.hermes/config.yaml изменяет настройки по умолчанию для вкладки — плагин читает их при загрузке через GET /config:

[code] dashboard: kanban: default_tenant: acme # preselects the tenant filter lane_by_profile: true # default for the "lanes by profile" toggle include_archived_by_default: false render_markdown: true # set false for plain

 rendering
[/code]

Каждый ключ опционален и возвращается к показанному значению по умолчанию.

Модель безопасности

Промежуточное ПО HTTP-аутентификации панели управления явно пропускает /api/plugins/ — маршруты плагинов неаутентифицированы по замыслу, потому что панель управления по умолчанию привязывается к localhost. Это означает, что REST-поверхность kanban доступна с любого процесса на хосте.

WebSocket делает один дополнительный шаг: он требует эфемерный токен сессии панели управления как параметр запроса ?token=… (браузеры не могут установить Authorization на запросе обновления), следуя паттерну, используемому встроенным PTY-мостом в браузере.

Если вы запустите hermes dashboard --host 0.0.0.0, каждый маршрут плагина — включая kanban — становится доступен из сети. Не делайте этого на общем хосте. Доска содержит тела задач, комментарии и пути к рабочим областям; атакующий, получивший доступ к этим маршрутам, получает доступ на чтение ко всей вашей поверхности совместной работы, а также может создавать, переназначать и архивировать задачи.

Задачи в ~/.hermes/kanban.db намеренно агностичны к профилям (в этом и заключается координационный примитив). Если вы откроете панель управления с помощью hermes -p <profile> dashboard, доска всё равно покажет задачи, созданные любым другим профилем на хосте. Один и тот же пользователь владеет всеми профилями, но об этом стоит знать, если сосуществуют несколько персон.

Живые обновления

task_events — это таблица SQLite только на добавление с монотонным id. Конечная точка WebSocket хранит последний просмотренный id события каждого клиента и отправляет новые строки по мере их появления. Когда прибывает всплеск событий, фронтенд перезагружает (очень дешёвую) конечную точку доски — проще и правильнее, чем пытаться обновлять локальное состояние из каждого типа событий. Режим WAL означает, что цикл чтения никогда не блокирует транзакции захвата BEGIN IMMEDIATE диспетчера.

Расширение

Плагин использует стандартный контракт плагинов панели управления Hermes — см. Расширение панели управления для полного справочника манифеста, слотов оболочки, слотов с областью видимости страницы и SDK плагинов. Дополнительные колонки, пользовательский хром карточек, макеты с фильтрацией по арендатору или полные замены tab.override могут быть выражены без форка этого плагина.

Чтобы отключить без удаления: добавьте dashboard.plugins.kanban.enabled: false в config.yaml (или удалите plugins/kanban/dashboard/manifest.json).

Граница области ответственности

GUI намеренно тонкий. Всё, что делает плагин, доступно из CLI; плагин просто делает это удобным для людей. Автоназначение, бюджеты, шлюзы управления и представления оргструктуры остаются в пользовательском пространстве — профиль-роутер, другой плагин или повторное использование tools/approval.py — именно как перечислено в разделе «вне области ответственности» спецификации дизайна.

Справочник CLI-команд

Это поверхность, которую вы (или скрипты, cron, панель управления) используете для управления доской. Рабочие, запущенные внутри диспетчера, используют поверхность инструментов kanban_* для тех же операций — CLI здесь и инструменты там проходят через kanban_db, поэтому две поверхности согласованы по построению.

[code] hermes kanban init # create kanban.db + print daemon hint hermes kanban create "" [--body ...] [--assignee <profile>] [--parent <id>]... [--tenant <name>] [--workspace scratch|worktree|dir:<path>] [--priority N] [--triage] [--idempotency-key KEY] [--max-runtime 30m|2h|1d|<seconds>] [--skill <name>]... [--json] hermes kanban list [--mine] [--assignee P] [--status S] [--tenant T] [--archived] [--json] hermes kanban show <id> [--json] hermes kanban assign <id> <profile> # or 'none' to 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]</p> <div class="highlight"><pre><span></span><code># Bulk verbs — accept multiple ids: hermes kanban complete <id>... [--result "..."] hermes kanban block <id> "<reason>" [--ids <id>...] hermes kanban unblock <id>... hermes kanban archive <id>... hermes kanban tail <id> # follow a single task's event stream hermes kanban watch [--assignee P] [--tenant T] # live stream ALL events to the terminal [--kinds completed,blocked,…] [--interval SECS] hermes kanban heartbeat <id> [--note "..."] # worker liveness signal for long ops hermes kanban runs <id> [--json] # attempt history (one row per run) hermes kanban assignees [--json] # profiles on disk + per-assignee task counts hermes kanban dispatch [--dry-run] [--max N] # one-shot pass [--failure-limit N] [--json] hermes kanban daemon --force # DEPRECATED — standalone dispatcher (use `hermes gateway start` instead) [--failure-limit N] [--pidfile PATH] [-v] hermes kanban stats [--json] # per-status + per-assignee counts hermes kanban log <id> [--tail BYTES] # worker log from ~/.hermes/kanban/logs/ hermes kanban notify-subscribe <id> # gateway bridge hook (used by /kanban in the gateway) --platform <name> --chat-id <id> [--thread-id <id>] [--user-id <id>] hermes kanban notify-list [<id>] [--json] hermes kanban notify-unsubscribe <id> --platform <name> --chat-id <id> [--thread-id <id>] hermes kanban context <id> # what a worker sees hermes kanban gc [--event-retention-days N] # workspaces + old events + old logs [--log-retention-days N] </code></pre></div> <p>[/code]</p> <p>Все команды также доступны как слэш-команда в интерактивном CLI и в шлюзе обмена сообщениями (см. раздел <a href="#kanban-slash-command"><code>/kanban</code> слэш-команда</a> ниже).</p> <h2 id="-kanban">Слэш-команда <code>/kanban</code><a href="#kanban-slash-command" title="Прямая ссылка на раздел «Слэш-команда /kanban»">​</a><a class="headerlink" href="#-kanban" title="Permanent link">¶</a></h2> <p>Каждый глагол <code>hermes kanban <action></code> также доступен как <code>/kanban <action></code> — из интерактивной сессии <code>hermes chat</code> <strong>и</strong> с любой платформы шлюза (Telegram, Discord, Slack, WhatsApp, Signal, Matrix, Mattermost, email, SMS). Оба интерфейса вызывают одну и ту же точку входа <code>hermes_cli.kanban.run_slash()</code>, которая использует то же дерево argparse <code>hermes kanban</code>, поэтому поверхность аргументов, флаги и формат вывода идентичны через CLI, <code>/kanban</code> и <code>hermes kanban</code>. Вам не нужно покидать чат, чтобы управлять доской.</p> <p>[code] /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 [/code]</p> <p>Заключайте многословные аргументы в кавычки так же, как в оболочке — <code>run_slash</code> парсит остаток строки с помощью <code>shlex.split</code>, так что <code>"..."</code> и <code>'...'</code> оба работают.</p> <h3 id="kanban">Использование во время выполнения: <code>/kanban</code> обходит защиту от запущенного агента<a href="#mid-run-usage-kanban-bypasses-the-running-agent-guard" title="Прямая ссылка на раздел «Использование во время выполнения: /kanban обходит защиту от запущенного агента»">​</a><a class="headerlink" href="#kanban" title="Permanent link">¶</a></h3> <p>Шлюз обычно ставит в очередь слэш-команды и сообщения пользователя, пока агент ещё думает — это предотвращает случайный запуск второго оборота, пока первый в полёте. <strong><code>/kanban</code> явно исключён из этой защиты.</strong> Доска живёт в <code>~/.hermes/kanban.db</code>, а не в состоянии работающего агента, поэтому чтение (<code>list</code>, <code>show</code>, <code>context</code>, <code>tail</code>, <code>watch</code>, <code>stats</code>, <code>runs</code>) и запись (<code>comment</code>, <code>unblock</code>, <code>block</code>, <code>assign</code>, <code>archive</code>, <code>create</code>, <code>link</code>, …) проходят немедленно, даже в середине оборота.</p> <p>В этом и заключается весь смысл разделения:</p> <ul> <li>Рабочий заблокирован в ожидании коллеги → вы отправляете <code>/kanban unblock t_abcd</code> со своего телефона, и диспетчер подхватывает коллегу на следующем тике. Заблокированный рабочий не прерывается — он просто перестаёт быть заблокированным.</li> <li>Вы заметили карточку, которой нужен человеческий контекст → <code>/kanban comment t_xyz "используй схему 2026, а не 2025"</code> попадает в ветку задачи, и <em>следующий</em> запуск этой задачи прочитает его в <code>kanban_show()</code>.</li> <li>Вы хотите узнать, чем занимается ваш флот, не останавливая оркестратора → <code>/kanban list --mine</code> или <code>/kanban stats</code> просматривает доску, не трогая ваш основной разговор.</li> </ul> <h3 id="kanban-create">Автоподписка при <code>/kanban create</code> (только шлюз)<a href="#auto-subscribe-on-kanban-create-gateway-only" title="Прямая ссылка на раздел «Автоподписка при /kanban create (только шлюз)»">​</a><a class="headerlink" href="#kanban-create" title="Permanent link">¶</a></h3> <p>Когда вы создаёте задачу из шлюза с помощью <code>/kanban create "…"</code>, исходный чат (платформа + id чата + id ветки) автоматически подписывается на терминальные события этой задачи (<code>completed</code>, <code>blocked</code>, <code>gave_up</code>, <code>crashed</code>, <code>timed_out</code>). Вы получите одно сообщение на каждое терминальное событие — включая первую строку сводки результата рабочего при <code>completed</code> — без необходимости опрашивать или запоминать id задачи.</p> <p>[code] 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)</p> <div class="highlight"><pre><span></span><code>… ~8 minutes later … bot> ✓ t_9fc1a3 completed by transcriber transcribed 42 minutes, saved to podcast/2026-05-04.md </code></pre></div> <p>[/code]</p> <p>Подписки автоматически удаляются, как только задача достигает <code>done</code> или <code>archived</code>. Если вы скриптуете создание с <code>--json</code> (машинный вывод), автоподписка пропускается — предполагается, что скриптовые вызывающие хотят управлять подписками явно через <code>/kanban notify-subscribe</code>.</p> <h3 id="_19">Обрезание вывода в обмене сообщениями<a href="#output-truncation-in-messaging" title="Прямая ссылка на раздел «Обрезание вывода в обмене сообщениями»">​</a><a class="headerlink" href="#_19" title="Permanent link">¶</a></h3> <p>Платформы шлюза имеют практические ограничения на длину сообщения. Если <code>/kanban list</code>, <code>/kanban show</code> или <code>/kanban tail</code> выдают более ~3800 символов, ответ обрезается с нижним колонтитулом <code>… (truncated; use \</code>hermes kanban …` in your terminal for full output)`. У CLI-интерфейса такого ограничения нет.</p> <h3 id="_20">Автодополнение<a href="#autocomplete" title="Прямая ссылка на раздел «Автодополнение»">​</a><a class="headerlink" href="#_20" title="Permanent link">¶</a></h3> <p>В интерактивном CLI при вводе <code>/kanban</code> и нажатии Tab циклически перебирается встроенный список подкоманд (<code>list</code>, <code>ls</code>, <code>show</code>, <code>create</code>, <code>assign</code>, <code>link</code>, <code>unlink</code>, <code>claim</code>, <code>comment</code>, <code>complete</code>, <code>block</code>, <code>unblock</code>, <code>archive</code>, <code>tail</code>, <code>dispatch</code>, <code>context</code>, <code>init</code>, <code>gc</code>). Остальные глаголы, перечисленные в справочнике CLI выше (<code>watch</code>, <code>stats</code>, <code>runs</code>, <code>log</code>, <code>assignees</code>, <code>heartbeat</code>, <code>notify-subscribe</code>, <code>notify-list</code>, <code>notify-unsubscribe</code>, <code>daemon</code>), тоже работают — их просто ещё нет в списке подсказок автодополнения.</p> <h2 id="_21">Паттерны совместной работы<a href="#collaboration-patterns" title="Прямая ссылка на раздел «Паттерны совместной работы»">​</a><a class="headerlink" href="#_21" title="Permanent link">¶</a></h2> <p>Доска поддерживает эти восемь паттернов без каких-либо новых примитивов:</p> <table> <thead> <tr> <th>Паттерн</th> <th>Форма</th> <th>Пример</th> </tr> </thead> <tbody> <tr> <td><strong>P1 Разветвление</strong></td> <td>N братьев, та же роль</td> <td>«research 5 angles in parallel»</td> </tr> <tr> <td><strong>P2 Конвейер</strong></td> <td>цепочка ролей: разведчик → редактор → писатель</td> <td>сборка ежедневного брифинга</td> </tr> <tr> <td><strong>P3 Голосование / кворум</strong></td> <td>N братьев + 1 агрегатор</td> <td>3 исследователя → 1 рецензент выбирает</td> </tr> <tr> <td><strong>P4 Долгоиграющий журнал</strong></td> <td>тот же профиль + общая директория + cron</td> <td>хранилище Obsidian</td> </tr> <tr> <td><strong>P5 Человек в цикле</strong></td> <td>рабочий блокируется → пользователь комментирует → разблокировка</td> <td>неоднозначные решения</td> </tr> <tr> <td><strong>P6 <code>@упоминание</code></strong></td> <td>встроенная маршрутизация из прозы</td> <td><code>@reviewer look at this</code></td> </tr> <tr> <td><strong>P7 Рабочая область в рамках ветки</strong></td> <td><code>/kanban here</code> в ветке</td> <td>проектные ветки шлюза</td> </tr> <tr> <td><strong>P8 Фермерство</strong></td> <td>один профиль, N субъектов</td> <td>50 соцсетей</td> </tr> <tr> <td><strong>P9 Спецификатор триажа</strong></td> <td>сырая идея → <code>triage</code> → спецификатор расширяет тело → <code>todo</code></td> <td>«turn this one-liner into a spec' task»</td> </tr> </tbody> </table> <p>Для проработанных примеров каждого паттерна см. <code>docs/hermes-kanban-v1-spec.pdf</code>.</p> <h2 id="_22">Мультитенантное использование<a href="#multi-tenant-usage" title="Прямая ссылка на раздел «Мультитенантное использование»">​</a><a class="headerlink" href="#_22" title="Permanent link">¶</a></h2> <p>Когда один флот специалистов обслуживает несколько бизнесов, пометьте каждую задачу арендатором:</p> <p>[code] hermes kanban create "monthly report" \ --assignee researcher \ --tenant business-a \ --workspace dir:~/tenants/business-a/data/ [/code]</p> <p>Рабочие получают <code>$HERMES_TENANT</code> и пространство имён для своих записей памяти через префикс. Доска, диспетчер и определения профилей являются общими; данные изолированы.</p> <h2 id="_23">Уведомления шлюза<a href="#gateway-notifications" title="Прямая ссылка на раздел «Уведомления шлюза»">​</a><a class="headerlink" href="#_23" title="Permanent link">¶</a></h2> <p>Когда вы запускаете <code>/kanban create …</code> из шлюза (Telegram, Discord, Slack и т.д.), исходный чат автоматически подписывается на новую задачу. Фоновый уведомитель шлюза опрашивает <code>task_events</code> каждые несколько секунд и доставляет одно сообщение на каждое терминальное событие (<code>completed</code>, <code>blocked</code>, <code>gave_up</code>, <code>crashed</code>, <code>timed_out</code>) в этот чат. Для завершённых задач также отправляется первая строка <code>--result</code> рабочего, чтобы вы видели результат без необходимости выполнять <code>/kanban show</code>.</p> <p>Вы можете управлять подписками явно из CLI — полезно, когда скрипт / cron-задача хочет уведомить чат, из которого она не была создана:</p> <p>[code] 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 [/code]</p> <p>Подписка удаляется автоматически, как только задача достигает <code>done</code> или <code>archived</code>; никакой очистки не требуется.</p> <h2 id="_24">Запуски — одна строка на попытку<a href="#runs--one-row-per-attempt" title="Прямая ссылка на раздел «Запуски — одна строка на попытку»">​</a><a class="headerlink" href="#_24" title="Permanent link">¶</a></h2> <p>Задача — это логическая единица работы; <strong>запуск</strong> — это одна попытка её выполнения. Когда диспетчер захватывает готовую задачу, он создаёт строку в <code>task_runs</code> и устанавливает <code>tasks.current_run_id</code> на неё. Когда эта попытка завершается — выполнена, заблокирована, упала, превысила таймаут, не удалась при запуске, возвращена — строка запуска закрывается с <code>outcome</code>, и указатель задачи очищается. Задача, которая была предпринята трижды, имеет три строки <code>task_runs</code>.</p> <p>Зачем две таблицы вместо простого изменения задачи: вам нужна <strong>полная история попыток</strong> для реальных посмертных анализов («вторая попытка ревьювера получила одобрение, третья слила»), и вам нужно чистое место для хранения метаданных каждой попытки — какие файлы изменились, какие тесты запускались, какие выводы отметил рецензент. Это факты запуска, а не факты задачи.</p> <p>Запуски также являются местом, где хранится <strong>структурированная передача</strong>. Когда рабочий завершает задачу (через <code>kanban_complete(...)</code>), он может передать:</p> <ul> <li><code>summary</code> (параметр инструмента) / <code>--summary</code> (CLI) — человеческая передача; попадает в запуск; downstream-потомки видят его в своём <code>build_worker_context</code>.</li> <li><code>metadata</code> (параметр инструмента) / <code>--metadata</code> (CLI) — свободный JSON-словарь в запуске; потомки видят его сериализованным вместе со сводкой.</li> <li><code>result</code> (параметр инструмента) / <code>--result</code> (CLI) — короткая строка лога, которая попадает в строку задачи (устаревшее поле, сохранено для обратной совместимости).</li> </ul> <p>Downstream-потомки читают сводку + метаданные самого последнего завершённого запуска для каждого родителя. Рабочие при повторных попытках читают предыдущие попытки своей задачи (результат, сводку, ошибку), чтобы не повторять путь, который уже не удался.</p> <p>[code] # What a worker actually does — a tool call, from inside the agent loop: kanban_complete( summary="implemented token bucket, keys on user_id with IP fallback, all tests pass", metadata={"changed_files": ["limiter.py", "tests/test_limiter.py"], "tests_run": 14}, result="rate limiter shipped", ) [/code]</p> <p>Та же передача доступна из CLI, когда вам (человеку) нужно закрыть задачу, которую рабочий не может — например, задачу, которая была abandonирована, или ту, которую вы отметили как выполненную вручную из панели управления:</p> <p>[code] 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}'</p> <div class="highlight"><pre><span></span><code># Review the attempt history on a retried task: hermes kanban runs t_abcd # # OUTCOME PROFILE ELAPSED STARTED # 1 blocked worker 12s 2026-04-27 14:02 # → BLOCKED: need decision on rate-limit key # 2 completed worker 8m 2026-04-27 15:18 # → implemented token bucket, keys on user_id with IP fallback </code></pre></div> <p>[/code]</p> <p>Запуски отображаются на панели управления (раздел Run History в боковой панели, по одной цветной строке на попытку) и в REST API (<code>GET /api/plugins/kanban/tasks/:id</code> возвращает массив <code>runs[]</code>). <code>PATCH /api/plugins/kanban/tasks/:id</code> с <code>{status: "done", summary, metadata}</code> передаёт оба в ядро, так что кнопка «отметить как выполненное» в панели управления эквивалентна CLI. Строки <code>task_events</code> содержат <code>run_id</code>, к которому они принадлежат, чтобы UI мог группировать их по попыткам, а событие <code>completed</code> встраивает первую строку сводки в свою полезную нагрузку (ограничено 400 символами), чтобы уведомители шлюза могли отображать структурированные передачи без второго кругого запроса к SQL.</p> <p><strong>Предостережение о массовом закрытии.</strong> <code>hermes kanban complete a b c --summary X</code> отклоняется — структурированная передача выполняется для каждого запуска, поэтому копирование одной и той же сводки к N задачам почти всегда неправильно. Массовое закрытие <em>без</em> <code>--summary</code> / <code>--metadata</code> по-прежнему работает для обычного случая «я закончил кучу административных задач».</p> <p><strong>Возвращённые запуски при изменении статуса.</strong> Если вы перетащите запущенную задачу из <code>running</code> в панели управления (обратно в <code>ready</code> или сразу в <code>todo</code>) или архивируете задачу, которая всё ещё выполнялась, выполняющийся запуск закрывается с <code>outcome='reclaimed'</code> вместо того, чтобы остаться осиротевшим. Строка <code>task_runs</code> всегда находится в терминальном состоянии, когда <code>tasks.current_run_id</code> равно <code>NULL</code>, и наоборот — этот инвариант сохраняется через CLI, панель управления, диспетчер и уведомитель.</p> <p><strong>Синтетические запуски для никогда не захватывавшихся завершений.</strong> Завершение или блокировка задачи, которая никогда не была захвачена (например, человек закрывает задачу со статусом <code>ready</code> из панели управления со сводкой, или пользователь CLI выполняет <code>hermes kanban complete <ready-task> --summary X</code>) в противном случае потеряли бы передачу. Вместо этого ядро вставляет строку запуска нулевой длительности (<code>started_at == ended_at</code>), несущую сводку / метаданные / причину, так что история попыток остаётся полной. <code>run_id</code> события <code>completed</code> / <code>blocked</code> указывает на эту строку.</p> <p><strong>Живое обновление боковой панели.</strong> Когда поток событий WebSocket панели управления сообщает о новых событиях для задачи, которую пользователь сейчас просматривает, боковая панель перезагружается (через счётчик событий на задачу, встроенный в список зависимостей <code>useEffect</code>). Закрытие и повторное открытие больше не требуется, чтобы увидеть новую строку запуска или обновлённый результат.</p> <h3 id="_25">Прямая совместимость<a href="#forward-compatibility" title="Прямая ссылка на раздел «Прямая совместимость»">​</a><a class="headerlink" href="#_25" title="Permanent link">¶</a></h3> <p>Два nullable-столбца в <code>tasks</code> зарезервированы для маршрутизации рабочих процессов v2: <code>workflow_template_id</code> (к какому шаблону принадлежит эта задача) и <code>current_step_key</code> (какой шаг в этом шаблоне активен). Ядро v1 игнорирует их для маршрутизации, но позволяет клиентам их записывать, так что релиз v2 сможет добавить механизм маршрутизации без ещё одной миграции схемы.</p> <h2 id="_26">Справочник событий<a href="#event-reference" title="Прямая ссылка на раздел «Справочник событий»">​</a><a class="headerlink" href="#_26" title="Permanent link">¶</a></h2> <p>Каждый переход добавляет строку в <code>task_events</code>. Каждая строка содержит опциональный <code>run_id</code>, чтобы UI мог группировать события по попыткам. Виды группируются в три кластера для удобства фильтрации (<code>hermes kanban watch --kinds completed,gave_up,timed_out</code>):</p> <p><strong>Жизненный цикл</strong> (что изменилось в задаче как логической единице):</p> <table> <thead> <tr> <th>Вид</th> <th>Полезная нагрузка</th> <th>Когда</th> </tr> </thead> <tbody> <tr> <td><code>created</code></td> <td><code>{assignee, status, parents, tenant}</code></td> <td>Задача вставлена. <code>run_id</code> равен <code>NULL</code>.</td> </tr> <tr> <td><code>promoted</code></td> <td>—</td> <td><code>todo → ready</code>, потому что все родители достигли <code>done</code>. <code>run_id</code> равен <code>NULL</code>.</td> </tr> <tr> <td><code>claimed</code></td> <td><code>{lock, expires, run_id}</code></td> <td>Диспетчер атомарно захватил задачу <code>ready</code> для запуска.</td> </tr> <tr> <td><code>completed</code></td> <td><code>{result_len, summary?}</code></td> <td>Рабочий записал <code>--result</code> / <code>--summary</code>, и задача достигла <code>done</code>. <code>summary</code> — это первая строка передачи (ограничение 400 символов); полная версия хранится в строке запуска. Если <code>complete_task</code> вызван для никогда не захваченной задачи с полями передачи, синтезируется запуск нулевой длительности, чтобы <code>run_id</code> всё равно на что-то указывал.</td> </tr> <tr> <td><code>blocked</code></td> <td><code>{reason}</code></td> <td>Рабочий или человек перевёл задачу в <code>blocked</code>. Синтезирует запуск нулевой длительности при вызове для никогда не захваченной задачи с <code>--reason</code>.</td> </tr> <tr> <td><code>unblocked</code></td> <td>—</td> <td><code>blocked → ready</code>, либо вручную, либо через <code>/unblock</code>. <code>run_id</code> равен <code>NULL</code>.</td> </tr> <tr> <td><code>archived</code></td> <td>—</td> <td>Скрыта из представления доски по умолчанию. Если задача всё ещё выполнялась, содержит <code>run_id</code> запуска, который был возвращён как побочный эффект.</td> </tr> </tbody> </table> <p><strong>Редактирование</strong> (изменения, инициированные человеком, не являющиеся переходами):</p> <table> <thead> <tr> <th>Вид</th> <th>Полезная нагрузка</th> <th>Когда</th> </tr> </thead> <tbody> <tr> <td><code>assigned</code></td> <td><code>{assignee}</code></td> <td>Исполнитель изменён (включая снятие назначения).</td> </tr> <tr> <td><code>edited</code></td> <td><code>{fields}</code></td> <td>Заголовок или тело обновлены.</td> </tr> <tr> <td><code>reprioritized</code></td> <td><code>{priority}</code></td> <td>Приоритет изменён.</td> </tr> <tr> <td><code>status</code></td> <td><code>{status}</code></td> <td>Перетаскивание на панели управления записало статус напрямую (например, <code>todo → ready</code>). Содержит <code>run_id</code> запуска, который был возвращён при перетаскивании из <code>running</code>; в противном случае <code>run_id</code> равен NULL.</td> </tr> </tbody> </table> <p><strong>Телеметрия рабочего</strong> (о процессе выполнения, а не о логической задаче):</p> <table> <thead> <tr> <th>Вид</th> <th>Полезная нагрузка</th> <th>Когда</th> </tr> </thead> <tbody> <tr> <td><code>spawned</code></td> <td><code>{pid}</code></td> <td>Диспетчер успешно запустил процесс рабочего.</td> </tr> <tr> <td><code>heartbeat</code></td> <td><code>{note?}</code></td> <td>Рабочий вызвал <code>hermes kanban heartbeat $TASK</code> для сигнала жизнеспособности во время долгих операций.</td> </tr> <tr> <td><code>reclaimed</code></td> <td><code>{stale_lock}</code></td> <td>TTL захвата истёк без завершения; задача возвращается в <code>ready</code>.</td> </tr> <tr> <td><code>crashed</code></td> <td><code>{pid, claimer}</code></td> <td>PID рабочего больше не жив, но TTL ещё не истёк.</td> </tr> <tr> <td><code>timed_out</code></td> <td><code>{pid, elapsed_seconds, limit_seconds, sigkill}</code></td> <td>Превышено <code>max_runtime_seconds</code>; диспетчер отправил SIGTERM (затем SIGKILL через 5 с льготного периода) и повторно поставил в очередь.</td> </tr> <tr> <td><code>spawn_failed</code></td> <td><code>{error, failures}</code></td> <td>Одна попытка запуска не удалась (отсутствует PATH, рабочая область не монтируется, ...). Счётчик увеличивается; задача возвращается в <code>ready</code> для повтора.</td> </tr> <tr> <td><code>gave_up</code></td> <td><code>{failures, error}</code></td> <td>Автоматический выключатель сработал после N последовательных <code>spawn_failed</code>. Задача автоматически блокируется с последней ошибкой. N по умолчанию = 5; переопределяется через <code>--failure-limit</code>.</td> </tr> </tbody> </table> <p><code>hermes kanban tail <id></code> показывает их для одной задачи. <code>hermes kanban watch</code> транслирует их по всей доске.</p> <h2 id="_27">Вне области ответственности<a href="#out-of-scope" title="Прямая ссылка на раздел «Вне области ответственности»">​</a><a class="headerlink" href="#_27" title="Permanent link">¶</a></h2> <p>Kanban намеренно однопользовательский. <code>~/.hermes/kanban.db</code> — это локальный файл SQLite, и диспетчер запускает рабочих на той же машине. Работа общей доски на двух хостах не поддерживается — нет координационного примитива для «рабочий X на хосте A, рабочий Y на хосте B», и путь обнаружения сбоев предполагает, что PID локальны для хоста. Если вам нужно несколько хостов, запустите независимую доску на каждом хосте и используйте <code>delegate_task</code> / очередь сообщений для их моста.</p> <h2 id="_28">Спецификация дизайна<a href="#design-spec" title="Прямая ссылка на раздел «Спецификация дизайна»">​</a><a class="headerlink" href="#_28" title="Permanent link">¶</a></h2> <p>Полный дизайн — архитектура, корректность конкурентности, сравнение с другими системами, план реализации, риски, открытые вопросы — находится в <code>docs/hermes-kanban-v1-spec.pdf</code>. Прочитайте его перед отправкой любого PR, изменяющего поведение.</p> <ul> <li><a href="#two-surfaces-the-model-talks-through-tools-you-talk-through-the-cli">Два интерфейса: модель общается через инструменты, вы — через CLI</a></li> <li><a href="#kanban-vs-delegate_task">Kanban vs <code>delegate_task</code></a></li> <li><a href="#core-concepts">Основные понятия</a></li> <li><a href="#boards-multi-project">Доски (мультипроект)</a></li> <li><a href="#managing-boards-from-the-cli">Управление досками через CLI</a></li> <li><a href="#managing-boards-from-the-dashboard">Управление досками через панель управления</a></li> <li><a href="#quick-start">Быстрый старт</a></li> <li><a href="#gateway-embedded-dispatcher-default">Диспетчер, встроенный в шлюз (по умолчанию)</a></li> <li><a href="#idempotent-create-for-automation--webhooks">Идемпотентное создание (для автоматизации / вебхуков)</a></li> <li><a href="#bulk-cli-verbs">Массовые CLI-команды</a></li> <li><a href="#how-workers-interact-with-the-board">Как рабочие взаимодействуют с доской</a></li> <li><a href="#why-tools-instead-of-shelling-to-hermes-kanban">Почему инструменты вместо вызова <code>hermes kanban</code> в оболочке</a></li> <li><a href="#recommended-handoff-evidence">Рекомендуемые свидетельства передачи</a></li> <li><a href="#the-worker-skill">Навык рабочего</a></li> <li><a href="#pinning-extra-skills-to-a-specific-task">Прикрепление дополнительных навыков к конкретной задаче</a></li> <li><a href="#the-orchestrator-skill">Навык оркестратора</a></li> <li><a href="#dashboard-gui">Панель управления (GUI)</a></li> <li><a href="#what-the-plugin-gives-you">Что даёт плагин</a></li> <li><a href="#architecture">Архитектура</a></li> <li><a href="#rest-surface">REST-поверхность</a></li> <li><a href="#dashboard-config">Конфигурация панели управления</a></li> <li><a href="#security-model">Модель безопасности</a></li> <li><a href="#live-updates">Живые обновления</a></li> <li><a href="#extending-it">Расширение</a></li> <li><a href="#scope-boundary">Граница области ответственности</a></li> <li><a href="#cli-command-reference">Справочник CLI-команд</a></li> <li><a href="#kanban-slash-command"><code>/kanban</code> слэш-команда</a></li> <li><a href="#mid-run-usage-kanban-bypasses-the-running-agent-guard">Использование во время выполнения: <code>/kanban</code> обходит защиту от запущенного агента</a></li> <li><a href="#auto-subscribe-on-kanban-create-gateway-only">Автоподписка при <code>/kanban create</code> (только шлюз)</a></li> <li><a href="#output-truncation-in-messaging">Обрезание вывода в обмене сообщениями</a></li> <li><a href="#autocomplete">Автодополнение</a></li> <li><a href="#collaboration-patterns">Паттерны совместной работы</a></li> <li><a href="#multi-tenant-usage">Мультитенантное использование</a></li> <li><a href="#gateway-notifications">Уведомления шлюза</a></li> <li><a href="#runs--one-row-per-attempt">Запуски — одна строка на попытку</a></li> <li><a href="#forward-compatibility">Прямая совместимость</a></li> <li><a href="#event-reference">Справочник событий</a></li> <li><a href="#out-of-scope">Вне области ответственности</a></li> <li><a href="#design-spec">Спецификация дизайна</a></li> </ul> <!-- Source: https://hermes-agent.nousresearch.com/docs/user-guide/features/kanban --> </article> </div> <script>var tabs=__md_get("__tabs");if(Array.isArray(tabs))e:for(var set of document.querySelectorAll(".tabbed-set")){var labels=set.querySelector(".tabbed-labels");for(var tab of tabs)for(var label of labels.getElementsByTagName("label"))if(label.innerText.trim()===tab){var input=document.getElementById(label.htmlFor);input.checked=!0;continue e}}</script> <script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script> </div> <button type="button" class="md-top md-icon" data-md-component="top" hidden> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8z"/></svg> К началу </button> </main> <footer class="md-footer"> <div class="md-footer-meta md-typeset"> <div class="md-footer-meta__inner md-grid"> <div class="md-copyright"> <div class="md-copyright__highlight"> Copyright © 2025-2026 Nous Research — перевод сообщества </div> </div> <div class="md-social"> <a href="https://github.com/perejaslav/hermes-docs-ru" target="_blank" rel="noopener" title="github.com" class="md-social__link"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M173.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6m-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3m44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9M252.8 8C114.1 8 8 113.3 8 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C436.2 457.8 504 362.9 504 252 504 113.3 391.5 8 252.8 8M105.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1m-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7m32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1m-11.4-14.7c-1.6 1-1.6 3.6 0 5.9s4.3 3.3 5.6 2.3c1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2"/></svg> </a> <a href="https://discord.gg/NousResearch" target="_blank" rel="noopener" title="discord.gg" class="md-social__link"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M492.5 69.8c-.2-.3-.4-.6-.8-.7-38.1-17.5-78.4-30-119.7-37.1-.4-.1-.8 0-1.1.1s-.6.4-.8.8c-5.5 9.9-10.5 20.2-14.9 30.6-44.6-6.8-89.9-6.8-134.4 0-4.5-10.5-9.5-20.7-15.1-30.6-.2-.3-.5-.6-.8-.8s-.7-.2-1.1-.2C162.5 39 122.2 51.5 84.1 69c-.3.1-.6.4-.8.7C7.1 183.5-13.8 294.6-3.6 404.2c0 .3.1.5.2.8s.3.4.5.6c44.4 32.9 94 58 146.8 74.2.4.1.8.1 1.1 0s.7-.4.9-.7c11.3-15.4 21.4-31.8 30-48.8.1-.2.2-.5.2-.8s0-.5-.1-.8-.2-.5-.4-.6-.4-.3-.7-.4c-15.8-6.1-31.2-13.4-45.9-21.9-.3-.2-.5-.4-.7-.6s-.3-.6-.3-.9 0-.6.2-.9.3-.5.6-.7c3.1-2.3 6.2-4.7 9.1-7.1.3-.2.6-.4.9-.4s.7 0 1 .1c96.2 43.9 200.4 43.9 295.5 0 .3-.1.7-.2 1-.2s.7.2.9.4c2.9 2.4 6 4.9 9.1 7.2.2.2.4.4.6.7s.2.6.2.9-.1.6-.3.9-.4.5-.6.6c-14.7 8.6-30 15.9-45.9 21.8-.2.1-.5.2-.7.4s-.3.4-.4.7-.1.5-.1.8.1.5.2.8c8.8 17 18.8 33.3 30 48.8.2.3.6.6.9.7s.8.1 1.1 0c52.9-16.2 102.6-41.3 147.1-74.2.2-.2.4-.4.5-.6s.2-.5.2-.8c12.3-126.8-20.5-236.9-86.9-334.5zm-302 267.7c-29 0-52.8-26.6-52.8-59.2s23.4-59.2 52.8-59.2c29.7 0 53.3 26.8 52.8 59.2 0 32.7-23.4 59.2-52.8 59.2m195.4 0c-29 0-52.8-26.6-52.8-59.2s23.4-59.2 52.8-59.2c29.7 0 53.3 26.8 52.8 59.2 0 32.7-23.2 59.2-52.8 59.2"/></svg> </a> </div> </div> </div> </footer> </div> <div class="md-dialog" data-md-component="dialog"> <div class="md-dialog__inner md-typeset"></div> </div> <script id="__config" type="application/json">{"annotate": null, "base": "../../../..", "features": ["navigation.instant", "navigation.tracking", "navigation.indexes", "navigation.top", "search.highlight", "search.suggest", "content.code.copy", "content.tabs.link"], "search": "../../../../assets/javascripts/workers/search.2c215733.min.js", "tags": null, "translations": {"clipboard.copied": "\u0421\u043a\u043e\u043f\u0438\u0440\u043e\u0432\u0430\u043d\u043e \u0432 \u0431\u0443\u0444\u0435\u0440", "clipboard.copy": "\u041a\u043e\u043f\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0432 \u0431\u0443\u0444\u0435\u0440", "search.result.more.one": "\u0415\u0449\u0451 1 \u043d\u0430 \u044d\u0442\u043e\u0439 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0435", "search.result.more.other": "\u0415\u0449\u0451 # \u043d\u0430 \u044d\u0442\u043e\u0439 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0435", "search.result.none": "\u0421\u043e\u0432\u043f\u0430\u0434\u0435\u043d\u0438\u0439 \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u043e", "search.result.one": "\u041d\u0430\u0439\u0434\u0435\u043d\u043e 1 \u0441\u043e\u0432\u043f\u0430\u0434\u0435\u043d\u0438\u0435", "search.result.other": "\u041d\u0430\u0439\u0434\u0435\u043d\u043e \u0441\u043e\u0432\u043f\u0430\u0434\u0435\u043d\u0438\u0439: #", "search.result.placeholder": "\u041d\u0430\u0447\u043d\u0438\u0442\u0435 \u043f\u0435\u0447\u0430\u0442\u0430\u0442\u044c \u0434\u043b\u044f \u043f\u043e\u0438\u0441\u043a\u0430", "search.result.term.missing": "\u041e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u0435\u0442", "select.version": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0432\u0435\u0440\u0441\u0438\u044e"}, "version": null}</script> <script src="../../../../assets/javascripts/bundle.79ae519e.min.js"></script> </body> </html>