Архітектура динамічних воркерів
Phase 26: Dynamic Worker Registry. Замінює hardcoded dual-agent flow із Phase 24.5. Останнє оновлення: 2026-04-06
Огляд
Phase 26 замінює hardcoded бінарну логіку Consultant/Developer на систему динамічного реєстру воркерів.
Воркери задекларовані у config/workers_registry.json. Кожен воркер визначає: id, label, icon, type (chat/terminal), model, max_turns, tools, system_prompt_skill, prompt_style (history/gsd), output_format, focus_dirs, log_category, builtin.
Вбудовані воркери:
- Consultant (chat, sonnet) — read-only аналіз, brainstorm, створення спек. Маршрутизується через
/cабо/w:consultant. - Developer (terminal, opus) — повна імплементація. Маршрутизується через
/dабо/w:developer. - UI/UX Designer (chat, sonnet) — аналіз фронтенду, дизайн-пропозиції. Маршрутизується через
/w:ui-designer.
CEO обирає воркера через Telegram-команди. Спеки — це міст між воркерами: Consultant їх створює, CEO підтверджує, Developer виконує.
CEO (Telegram)
│
├── /c <message> ──► Consultant subprocess (read-only Claude)
│ │
│ ├── Analyzes code, searches web
│ └── Outputs SPEC blocks
│ │
│ ▼
│ spec_queue.json (status: "draft")
│
├── /approve <id> ──► spec status → "approved"
│ │
│ ▼
├── /d <message> ──► Developer (main GSD loop)
│ │
│ ├── Reads approved specs from queue
│ ├── Implements changes
│ └── Marks spec → "done"
│
├── /reject <id> ──► spec status → "rejected"
│
└── /specs ──► List all specs with status
Модель даних
Інтерфейс Spec
interface Spec {
id: string; // nanoid(8), e.g. "a1b2c3d4"
title: string; // One-line title from SPEC block
problem: string; // What issue this solves
approach: string; // Technical approach (2-3 sentences)
acceptance: string[]; // Acceptance criteria list
files: string[]; // Affected file paths
complexity: "low" | "medium" | "high";
status: SpecStatus;
createdAt: string; // ISO 8601
updatedAt: string; // ISO 8601
consultantThread?: string; // Thread ID that produced this spec
}
type SpecStatus = "draft" | "approved" | "rejected" | "executing" | "done";
Машина станів
IDEA ──► CONSULTATION ──► SPEC DRAFT ──► REVIEW ──► APPROVED ──► EXECUTING ──► DONE
↑ │
+──── REJECTED ───────────+
Переходи:
| З | До | Тригер |
|---|---|---|
| — | draft |
Consultant видає SPEC-блок |
draft |
approved |
CEO надсилає /approve <id> |
draft |
rejected |
CEO надсилає /reject <id> |
rejected |
draft |
Consultant переробляє після фідбеку |
approved |
executing |
Developer починає імплементацію |
executing |
done |
Developer виконує всі критерії приймання |
API-ендпоінти
Всі ендпоінти внутрішні (маршрутизація через master bot), не HTTP API.
| Ендпоінт | Метод | Auth | Опис |
|---|---|---|---|
/c <msg> |
Telegram | CEO only | Маршрутизація повідомлення до Consultant subprocess |
/d <msg> |
Telegram | CEO only | Маршрутизація повідомлення до Developer (GSD loop) |
/approve <id> |
Telegram | CEO only | Підтвердити чернетку спеки |
/reject <id> |
Telegram | CEO only | Відхилити чернетку спеки з опційним фідбеком |
/specs |
Telegram | CEO only | Перелічити всі спеки з фільтром за статусом |
/role |
Telegram | CEO only | Показати поточну активну роль |
| default | Telegram | CEO only | Маршрутизація до активної ролі (consultant або developer) |
Маршрутизація повідомлень
Master bot handleMessage() маршрутизує вхідні Telegram-повідомлення:
function handleMessage(text: string, chatId: number) {
if (text.startsWith("/c ")) → callWorker("consultant", text.slice(3))
if (text.startsWith("/d ")) → callWorker("developer", text.slice(3))
if (text.startsWith("/w:")) → callWorker(parsedWorkerId, parsedText)
if (text.startsWith("/approve ")) → approveSpec(text.slice(9).trim())
if (text.startsWith("/reject ")) → rejectSpec(text.slice(8).trim())
if (text === "/specs") → listSpecs()
if (text === "/role") → showActiveRole()
else → callWorker(activeWorkerId, text)
}
Уніфікована диспетчеризація: getWorkerConfig(workerId) → callWorker(workerId, text, options). Конфіг воркера визначає модель, інструменти, стиль prompt і log-категорію. Маршрутизація за замовчуванням використовує active_role.json. Початкове значення: developer.
Frontend-компоненти
WorkerPanel (Phase 26)
Універсальний диспетчер панелей. Рендерить ChatPanelView або TerminalPanelView залежно від worker.type.
ChatPanelView
Відображає розмови воркерів chat-типу (Consultant, UI/UX Designer тощо).
| Аспект | Деталь |
|---|---|
| Джерело даних | /api/sse/logs/:name?category=${worker.log_category} |
| Відображення | Бульбашки повідомлень у стилі чату (CEO справа, воркер зліва) |
| Виявлення SPEC | Парсить ### SPEC:-блоки, рендерить як картки з кнопками approve/reject |
| Введення | Повна input bar з селектором моделі, меню інструментів, голосовим введенням, файловими вкладеннями |
| Dev Result | Кнопка для перегляду останньої відповіді developer-терміналу |
TerminalPanelView
Відображає вивід воркерів terminal-типу (Developer тощо).
| Аспект | Деталь |
|---|---|
| Джерело даних | /api/sse/logs/:name?category=${worker.log_category} |
| Відображення | Monospace log-записи з іконками інструментів, індикаторами thinking, станом processing |
| Введення | Простий command bar з кнопкою Send |
Hook useSSEStream
Уніфікований SSE-hook для обох типів панелей. Обробляє дедуплікацію (seenIds), виявлення стану processing і трансформацію записів на основі worker.type.
Workers Bar
Верхня панель, що показує всіх зареєстрованих воркерів як toggle-пігулки. Клік додає/прибирає панелі. Layout зберігається у localStorage для кожного проєкту (citadel-workspace-active-${project.name}).
SpecPanel
Kanban-дошка, що показує життєвий цикл спек.
| Колонка | Які спеки показано |
|---|---|
| Draft | status: "draft" — з кнопками approve/reject |
| Approved | status: "approved" — чекають на developer |
| In Progress | status: "executing" — developer працює |
| Done | status: "done" — завершено |
| Rejected | status: "rejected" — згорнуто за замовчуванням |
RoleToggle
Візуальний індикатор у заголовку project pod, що показує активну роль/воркера.
Клік по toggle надсилає /role команду для отримання інформації; реальне перемикання ролей відбувається лише через Telegram-команди (безпека: жодних мутацій із фронтенду).
Модель безпеки
Обмеження інструментів воркера
Інструменти воркерів задекларовані у workers_registry.json. Функція callWorker() читає worker.tools і передає --allowedTools до Claude CLI:
"tools": ["Read", "Glob", ...]— явний whitelist"tools": "all"— повний доступ (тільки Developer)
Це гарантує, що chat-воркери (Consultant, UI Designer) НЕ можуть:
- Писати або редагувати файли
- Виконувати shell-команди
- Деплоїти, комітити або пушити
Ізоляція воркерів
- Кожен воркер запускається у власному Claude process/thread
- Історія розмов воркера трекається у Map
workerThreads(ключ — ID воркера) - Backward compat: масив
consultantThreadсинхронізується паралельно зworkerThreads - Лише CEO chatId може виконувати команди
/approveі/reject
Структура зберігання
Всі state-файли зберігаються у робочій директорії проєкту під state/:
state/
├── active_role.json # { "role": "developer"|"consultant", "since": ISO8601 }
├── consultant_thread.json # { "threadId": "...", "startedAt": ISO8601 }
├── spec_queue.json # Spec[] array — all specs with status
└── logs/
├── consultant-YYYY-MM-DD.log # JSONL consultant conversation
└── specs-YYYY-MM-DD.log # JSONL spec state transitions
active_role.json
{
"role": "developer",
"since": "2026-04-05T10:00:00Z"
}
consultant_thread.json
{
"threadId": "thread_abc123",
"startedAt": "2026-04-05T10:00:00Z",
"messageCount": 12
}
spec_queue.json
[
{
"id": "a1b2c3d4",
"title": "Add rate limiting to CRM endpoints",
"problem": "CRM API has no rate limiting, vulnerable to abuse",
"approach": "Add sliding window rate limiter middleware using in-memory Map with IP-based tracking",
"acceptance": [
"Rate limiter middleware applied to all /api/crm/* routes",
"100 requests per minute per IP",
"429 response with Retry-After header"
],
"files": ["shared/crm-routes.ts", "shared/rate-limiter.ts"],
"complexity": "medium",
"status": "approved",
"createdAt": "2026-04-05T10:05:00Z",
"updatedAt": "2026-04-05T10:12:00Z"
}
]
Реєстр воркерів
Всі воркери задекларовані у config/workers_registry.json:
{
"version": 1,
"workers": [
{
"id": "consultant",
"label": "Consultant",
"icon": "\ud83d\udcac",
"type": "chat",
"model": "claude-sonnet-4-5",
"max_turns": 5,
"tools": ["Read", "Glob", "Grep", "WebSearch", "WebFetch"],
"system_prompt_skill": "consultant_system.md",
"prompt_style": "history",
"output_format": "text",
"focus_dirs": [],
"log_category": "consultant",
"builtin": true
}
]
}
| Поле | Тип | Опис |
|---|---|---|
id |
string | Унікальний ідентифікатор воркера (використовується у маршрутизації, сховищі, SSE) |
label |
string | Назва для відображення в UI |
icon |
string | Emoji-іконка для пігулки/заголовка |
type |
"chat" | "terminal" |
Тип панелі: бульбашки чату vs monospace-логи |
model |
string | ID моделі Claude |
max_turns |
number | Макс. кількість agentic-кроків за виклик |
tools |
string[] | "all" |
Whitelist дозволених інструментів, або "all" для повного доступу |
system_prompt_skill |
string | null | Шлях до system prompt у директорії skills/ |
prompt_style |
"history" | "gsd" |
Стиль розмови (append history vs GSD-prompt) |
output_format |
"text" | "stream-json" |
Режим обробки виводу |
focus_dirs |
string[] | Директорії, на яких фокусуватись (інжектяться у prompt) |
log_category |
string | JSONL log-категорія (мапиться на SSE ?category= параметр) |
builtin |
boolean | Чи постачається цей воркер з Arc OS |
Конфігурація ролей
Конфігурація ролей для кожного проєкту у config/project_roles.json:
{
"version": 1,
"defaults": {
"activeRole": "developer",
"specAutoApprove": false,
"maxDraftSpecs": 20
}
}
Логування
Всі взаємодії з consultant і переходи спек логуються у форматі JSONL.
Записи consultant-*.log
{"ts":"2026-04-05T10:05:00Z","role":"ceo","text":"How should we add caching?"}
{"ts":"2026-04-05T10:05:15Z","role":"consultant","text":"Based on analysis...","specs":["a1b2c3d4"]}
Записи specs-*.log
{"ts":"2026-04-05T10:05:15Z","event":"created","specId":"a1b2c3d4","title":"Add caching layer"}
{"ts":"2026-04-05T10:12:00Z","event":"approved","specId":"a1b2c3d4","by":"ceo"}
{"ts":"2026-04-05T10:30:00Z","event":"executing","specId":"a1b2c3d4"}
{"ts":"2026-04-05T11:00:00Z","event":"done","specId":"a1b2c3d4"}
Підтримується Rick (Orchestrator). Phase 26 — Dynamic Workers.