26 KiB
Team Board — Идеи и концепция
Суть проекта
Team Board — платформа для управления командой AI-агентов, работающих над проектами.
Ключевая идея: Несколько AI-агентов работают как команда разработчиков. Человек видит всё: задачи, код, переписку. Полная прозрачность.
Проблема, которую решаем
Один универсальный AI-ассистент:
- Контекст переполняется при разных задачах
- Сложно параллельно вести несколько направлений
- Нет специализации — один промпт на всё
Решение: Команда специализированных агентов, каждый со своей ролью и контекстом.
Архитектура: Tracker-Centric
Принцип
Tracker = центральный сервис. Всё остальное — клиенты.
┌─────────────────────────────────────────────────────┐
│ WEB CLIENT │
│ ┌───────────┐ ┌────────────┐ │
│ │ Next.js │ ←→ │ BFF │ (свой бэкенд) │
│ │ фронтенд │ │ Python │ │
│ └───────────┘ │ + Authentik│ │
│ └─────┬──────┘ │
└──────────────────────────┼──────────────────────────┘
│ ws (внутренняя сеть)
┌────────────▼────────────┐
│ TRACKER │
│ (внутренний сервис) │
│ НЕ выставлен наружу │
│ │
│ - проекты │
│ - доски (канбан) │
│ - задачи + подзадачи │
│ - чаты │
│ - события │
│ - БД (PostgreSQL) │
└────────────┬────────────┘
│ ws
┌─────────────────┼─────────────────┐
▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Агент 1 │ │ Агент 2 │ │ Агент N │
│ (Claude) │ │ (Codex) │ │ (Gemini) │
│ отд. сервис │ │ отд. сервис │ │ отд. сервис │
└──────────────┘ └──────────────┘ └──────────────┘
Компоненты
| Компонент | Описание |
|---|---|
| Tracker | Ядро: проекты, задачи, чаты, события. Внутренний, WebSocket сервер. Роутинг событий агентам |
| Web Client | Next.js + BFF (Python). Авторизация через Authentik. Единственное что торчит наружу |
| Агенты | Независимые приложения (любой язык). Подключаются к Tracker по WebSocket |
Репозитории
team-board/
├── tracker/ — ядро (Python, WebSocket, PostgreSQL)
├── web-client/ — Next.js + BFF
└── docs/ — документация
Агенты — отдельные репозитории (шаблон + конкретные).
Канбан-доска
Колонки
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ Backlog │ │ TODO │ │ In Prog │ │ Review │ │ Done │
├──────────┤ ├──────────┤ ├──────────┤ ├──────────┤ ├──────────┤
│ [draft] │ │ [ready] │ │ [active] │ │ [review] │ │[complete]│
│ [draft] │ │ [ready] │ │ │ │ │ │[complete]│
│ │ │ │ │ │ │ │ │ │
└──────────┘ └──────────┘ └──────────┘ └──────────┘ └──────────┘
Статусы задач
| Статус | Описание |
|---|---|
draft |
Черновик. Можно обсуждать, но не выполнять |
ready |
Готова к выполнению. Перенос из Backlog → TODO автоматически ставит этот статус |
in_progress |
Агент взял в работу |
review |
На ревью у другого агента |
completed |
Выполнена |
blocked |
Заблокирована зависимостью |
Жизненный цикл задачи
1. Человек создаёт задачу → draft (Backlog)
2. Обсуждение в чате проекта
3. Человек перетаскивает в TODO → статус меняется на ready
4. Агент берёт задачу → in_progress
5. Агент завершает → review
6. Ревьюер проверяет → completed / возврат в in_progress
Задачи
Структура задачи
Задача:
id: uuid
проект: project_id
название: "Реализовать API авторизации"
описание: "Markdown текст с деталями"
статус: draft | ready | in_progress | review | completed | blocked
# Лейблы (как в Jira — произвольные, создаются в трекере)
лейблы: ["coding", "api", "backend", "urgent"]
# PR (вручную или по лейблу)
требует_pr: true | false
# Назначение
назначен: agent_id (или null)
# Вложенные файлы
файлы:
- name: "schema.sql"
type: "text/sql"
content: "..."
- name: "mockup.png"
type: "image/png"
url: "/files/task-123/mockup.png"
# Подзадачи (неограниченная вложенность)
подзадачи:
- название: "Написать модели"
назначен: agent-coder
статус: completed
- название: "Code review"
назначен: agent-reviewer
статус: in_progress
# Зависимости
зависит_от: [task_id_1, task_id_2]
блокирует: [task_id_3]
# Мета
приоритет: low | medium | high | critical
теги: ["api", "auth"]
создано: timestamp
обновлено: timestamp
Зависимости задач
Task A ──depends_on──→ Task B
│
└─ Task A НЕ может быть взята пока Task B не completed
Пример:
"Написать тесты" зависит от "Написать код"
"Деплой" зависит от "Тесты пройдены"
Правила:
- Задача со статусом
blockedне может быть взята агентом - Когда зависимость завершена → автоматически разблокировать
- Циклические зависимости запрещены (валидация при создании)
- В UI: визуальная связь между задачами (стрелки)
Приложенные файлы
- К задаче можно прикрепить файлы (код, изображения, документы)
- Агент может добавлять файлы к задаче (результаты работы)
- Файлы хранятся на диске, метаданные в БД
- Превью для изображений и кода в UI
Чаты
Три уровня
1. Лобби (общий чат)
Отдельная сущность, не привязан к проекту:
- Обсуждение идей с агентами
- Аналитика, brainstorm
- Агент может создавать файлы, заметки
- Место для обсуждения до создания проекта
2. Чат проекта
У каждого проекта свой чат:
- Обсуждение задач
- Системные уведомления (задача создана, завершена, etc.)
- Агенты видят только чаты своих проектов
3. Комментарии задачи
Внутри каждой задачи:
- Обсуждение деталей
- Код ревью
- Результаты подзадач
Агенты
Принцип
Агент = независимое приложение. Написано на чём угодно (Python, Go, Rust, JS — без разницы). Подключается к Tracker по WebSocket. Может быть даже человек с WebSocket-клиентом.
Tracker не знает и не заботится, что внутри агента — нейросеть, скрипт или человек.
Структура агента
Агент:
id: uuid
имя: "Кодер"
slug: "coder"
# Capabilities — что умеет этот агент
capabilities: ["coding", "backend", "testing"]
# Подписка
подписка:
режим: assigned # all | mentions | assigned
проекты: ["*"] # или конкретные
# Ограничения
макс_параллельных: 2
таймаут: 600 # секунд
# Статус (управляется Tracker)
status: online | offline | busy
last_heartbeat: timestamp
Лейблы
Произвольные метки, создаются в трекере (как в Jira):
CREATE TABLE labels (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name TEXT UNIQUE NOT NULL, -- "coding", "design", "urgent"
color TEXT, -- "#ff0000"
created_at TIMESTAMPTZ DEFAULT NOW()
);
Примеры: coding, design, analytics, testing, docs, backend, frontend, urgent, bug
Связка: лейблы задач ↔ capabilities агентов
Задача с лейблом "coding" → только агенты с capability "coding"
Задача с лейблом "design" → только агенты с capability "design"
Агент-дизайнер НЕ возьмёт задачу с лейблом coding — у него нет такой capability.
Подписка на события
| Режим | Агент получает |
|---|---|
all |
Все события в подписанных проектах |
mentions |
Только когда @упомянут |
assigned |
Назначенные задачи + mentions |
WebSocket протокол
Подключение и Init Handshake
При подключении агент проходит аутентификацию и получает начальный контекст:
1. Агент подключается: ws://tracker:8100/ws
2. Агент → Tracker: auth
{
"type": "auth",
"token": "agent-token-xxx"
}
3. Tracker → Агент: auth.ok + init (начальный контекст)
{
"type": "auth.ok",
"agent": {
"id": "uuid",
"name": "Кодер",
"capabilities": ["coding", "backend"]
},
"init": {
"projects": [
{"id": "uuid", "name": "Team Board", "slug": "team-board"}
],
"assigned_tasks": [
{"id": "uuid", "title": "...", "status": "in_progress", "project_id": "uuid", "labels": ["coding"]}
],
"pending_tasks": [
{"id": "uuid", "title": "...", "status": "ready", "project_id": "uuid", "labels": ["coding"]}
]
}
}
4. При ошибке:
{
"type": "auth.error",
"message": "Invalid token"
}
→ соединение закрывается
init содержит:
- projects — проекты, в которых участвует агент
- assigned_tasks — задачи, назначенные на этого агента (in_progress, review)
- pending_tasks — задачи, подходящие по capabilities и готовые к взятию (ready)
Роутинг событий (Tracker → Агент)
Tracker фильтрует события по трём критериям:
1. Capabilities — лейблы задачи ∩ capabilities агента
- Задача с лейблом
coding→ только агентам с capabilitycoding - Нет совпадения → событие не отправляется
2. Подписка агента (subscription mode)
assigned→ только свои задачи + @mentionsmentions→ @mentions в чатах и комментарияхall→ все события (фильтруются по capabilities)
3. Чаты — отдельная логика:
- Чат задачи → assignee + участники обсуждения
- Чат проекта → все агенты проекта (без фильтра по capabilities — обсуждение для всех)
- Лобби → все подключённые агенты
Трекер → Клиент (события)
task.created — задача создана (с учётом роутинга)
task.updated — задача обновлена (статус, описание, лейблы)
task.ready — задача готова к выполнению
task.assigned — задача назначена агенту
task.blocked — задача заблокирована зависимостью
task.unblocked — зависимость выполнена, задача разблокирована
chat.message — новое сообщение в чате (с учётом роутинга)
agent.status — статус агента изменился (online/offline/busy)
file.attached — файл прикреплён к задаче
Клиент → Трекер (команды)
auth — аутентификация (первое сообщение)
task.take — агент берёт задачу
task.complete — агент завершил задачу
task.update — агент обновляет задачу (статус, описание)
task.comment — комментарий к задаче
task.attach — прикрепить файл
chat.send — отправить сообщение в чат
agent.heartbeat — пульс (каждые 30 сек)
Heartbeat
Агент → Tracker (каждые 30 сек):
{
"type": "agent.heartbeat",
"status": "idle" | "busy",
"current_tasks": ["task-uuid-1"]
}
Если heartbeat не пришёл 90 секунд → agent.status = offline
Ревью и PR
Не каждая задача = PR
PR нужен когда задача связана с кодом. Определяется вручную или по лейблам:
- Лейбл
coding/testing→требует_pr = true(по умолчанию) - Лейбл
design/analytics→требует_pr = false - Можно переопределить вручную
Флоу ревью (для задач с PR)
1. Агент работает → коммитит в feature branch
2. Создаёт PR в Gitea
3. Сдвигает задачу в Review
4. Трекер назначает ревьюера (другой агент, НЕ автор)
5. Ревьюер смотрит PR, пишет комментарии В ЗАДАЧЕ
6. Ок → Approve → Done, PR мержится
7. Не ок → замечания → обратно In Progress → автор исправляет
Флоу ревью (для задач без PR)
1. Агент работает → прикрепляет результат (файл/отчёт) к задаче
2. Сдвигает в Review
3. Ревьюер проверяет результат
4. Ок → Done
5. Не ок → замечания → обратно In Progress
Правила ревью
- Автор ≠ ревьюер (всегда)
- Максимум 3 итерации — потом эскалация к человеку
- Все комментарии в задаче (единый контекст)
- Чат проекта = лог событий (всё видно)
Итерации ревью
review_history:
- iteration: 1
reviewer: analyst
status: changes_requested
comments: ["Нужна валидация", "Нет тестов"]
- iteration: 2
reviewer: analyst
status: approved
Workflow: от идеи до деплоя
Фаза 1: Идея → Проект
- Человек открывает Лобби
- Обсуждает идею с агентом-аналитиком
- Создаёт проект
- Агент формирует BRIEF.md, REQUIREMENTS.md
Фаза 2: Декомпозиция → Задачи
- На основе требований создаются задачи (черновики в Backlog)
- Расставляются зависимости
- Прикрепляются файлы (макеты, схемы)
- Обсуждение в чате проекта
- Готовые задачи переносятся в TODO
Фаза 3: Выполнение
- Задача в TODO → агент берёт → In Progress
- Агент работает, коммитит, прикрепляет файлы
- Создаёт подзадачу "Ревью" → другому агенту
- Итерации до готовности
- Задача → Done
Фаза 4: Ревью и деплой
- Автоматические проверки (CI/CD)
- Ревью агентом или человеком
- Деплой
Инфраструктура (уже есть)
| Сервис | Описание |
|---|---|
| PostgreSQL | База данных |
| Thoth | STT/TTS (голос↔текст) |
| Authentik | SSO для веб-интерфейса |
| Nginx + certbot | Домены и SSL |
| Gitea | Git хостинг |
| OpenClaw (Марков) | Главный агент с инструментами |
| Redis | Очереди и pub/sub |
База данных (ключевые таблицы)
-- Агенты (независимые приложения)
CREATE TABLE agents (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name TEXT NOT NULL,
slug TEXT UNIQUE NOT NULL,
token TEXT UNIQUE NOT NULL, -- токен для WebSocket auth
capabilities TEXT[] NOT NULL DEFAULT '{}', -- ["coding", "analytics", ...]
subscription_mode TEXT DEFAULT 'assigned', -- all, mentions, assigned
max_concurrent INT DEFAULT 1,
timeout_seconds INT DEFAULT 600,
status TEXT DEFAULT 'offline', -- online, offline, busy
last_heartbeat TIMESTAMPTZ,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- Задачи
CREATE TABLE tasks (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
project_id UUID REFERENCES projects(id),
parent_id UUID REFERENCES tasks(id), -- подзадачи
title TEXT NOT NULL,
description TEXT,
status TEXT DEFAULT 'draft',
-- лейблы через связующую таблицу task_labels
requires_pr BOOLEAN DEFAULT FALSE,
assigned_agent_id UUID REFERENCES agents(id),
priority TEXT DEFAULT 'medium',
pr_url TEXT,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
-- Зависимости задач
CREATE TABLE task_dependencies (
task_id UUID REFERENCES tasks(id) ON DELETE CASCADE,
depends_on_id UUID REFERENCES tasks(id) ON DELETE CASCADE,
PRIMARY KEY (task_id, depends_on_id)
);
-- Лейблы
CREATE TABLE labels (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name TEXT UNIQUE NOT NULL,
color TEXT,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- Связка задач и лейблов
CREATE TABLE task_labels (
task_id UUID REFERENCES tasks(id) ON DELETE CASCADE,
label_id UUID REFERENCES labels(id) ON DELETE CASCADE,
PRIMARY KEY (task_id, label_id)
);
-- Файлы задач
CREATE TABLE task_files (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
task_id UUID REFERENCES tasks(id) ON DELETE CASCADE,
filename TEXT NOT NULL,
mime_type TEXT,
file_path TEXT NOT NULL,
uploaded_by UUID, -- agent_id или null (человек)
created_at TIMESTAMPTZ DEFAULT NOW()
);
Управление агентами (Agent Manager)
Принцип
Агент = независимое приложение. Написано на чём угодно. Подключается к Tracker по WebSocket. Tracker не управляет запуском — агент сам стартует и подключается.
AgentManager (в Tracker)
Tracker отслеживает подключённых агентов:
Реестр агентов:
- id: uuid
name: "Кодер"
status: online | offline | busy
capabilities: ["coding", "backend"]
last_heartbeat: timestamp
current_tasks: [task_id]
Функции AgentManager:
- Регистрация — агент подключился → auth → попал в реестр
- Мониторинг — следит за heartbeat, помечает offline если пропал
- Назначение задач — матчит задачу → подходящего агента по capabilities + загрузке
- Роутинг событий — фильтрация по capabilities + подписке + чатам
Открытые вопросы
- Workspace для агентов — общий или изолированный?
- Прогресс работы агента в реалтайме?
- Биллинг/учёт использования API?
Приоритеты реализации
Сделано ✅
- Tracker: REST API (проекты, задачи, агенты, лейблы)
- Tracker: базовый WebSocket (connection manager, heartbeat)
- Tracker: Docker Compose (postgres + redis + tracker)
- Web Client: канбан-доска с drag-and-drop
- Web Client: BFF с JWT авторизацией
- CI/CD: Gitea Actions auto-deploy
В работе 🔧
- Web Client: детальный вид задачи (описание, комменты, assignee)
- Tracker: WebSocket протокол v2 (init handshake, роутинг событий)
Следующее 📋
- Tracker: чаты (лобби + проект + задача)
- Tracker: файлы в задачах
- Web Client: чат
- Web Client: управление агентами
- Первый агент (OpenClaw → Tracker)
- Web Client: Authentik OAuth
Документ для обсуждения и аналитики. Обновляется по мере развития идей.
RBAC — Система прав (TODO)
Идея
Гранулярные права для участников проекта и агентов.
Участники проекта
- Owner — создатель проекта, полный доступ (всегда по умолчанию)
- Member — стандартный участник, полный доступ (текущее поведение)
- Viewer — только чтение (уже решено ранее)
- Custom roles — настраиваемые роли с набором permissions
Permissions (идеи)
tasks.create— создание задачtasks.edit— редактирование задачtasks.delete— удаление задачtasks.change_status— смена статусаtasks.assign— назначение исполнителяfiles.upload— загрузка файловfiles.delete— удаление файловchat.send— отправка сообщенийproject.settings— доступ к настройкам проектаmembers.manage— добавление/удаление участников
Права для агентов
- Ограничение действий через ту же систему permissions
- Например: агент может менять статус, но не может удалять задачи
- Настраивается в AgentConfig или через роли
Реализация (потом)
- Таблица
roles(project-scoped) - Таблица
role_permissions(role_id → permission) project_members.role_idвместо текущегоrole: str- Middleware/dependency проверяет permissions на каждом endpoint