# Задание B6 (Codex): рабочий Hub и граф над маршрутизацией ## Дата поступления 2026-08-21 ## База Проверочный HEAD: **`09d5e5d`**, `origin/main` = `09d5e5d`. `git fetch`, зафиксировать `BASE_SHA`, работать от свежего `main`. ## Ветка `codex/routing-graph` --- ## Что принято по B5 Проверено исполнением и по скриншотам: - **макет воспроизведён** — левая навигация, верхняя строка с поиском и `Ctrl + K`, ряд KPI, схема маршрутизации, правая панель, лента событий; - **три темы работают**, переключение перекрашивает всё приложение; - **дисциплина честности выдержана**: на скриншоте «Обзора» стоят `Н/Д` с пояснением причины там, где данных нет, и реальные значения там, где есть. Ни одного выдуманного числа; - четыре действия вернулись на карточку аккаунта через `MANAGEMENT_ACTIONS`; - 33 скриншота, тесты 193 headless / 249 с UI-зависимостями, ruff чисто. Это лучшая работа по интерфейсу за все раунды. --- ## Про исходный документ «Agent Graph / n8n» Документ на 104 пункта прочитан и разобран. **Реализуется только часть**, и это осознанное решение владельца. Причина: n8n — это Node.js/TypeScript с **собственным движком исполнения workflow** и базой данных; канва там редактирует то, что движок исполняет. У Hermes Hub движок другой — **маршрутизации**: он выбирает провайдера, аккаунт и модель для одного вызова. Агентов, которые исполняются, делегируют задачи и возвращают артефакты, в Hub нет — они живут в Hermes Agent, внутри которого Hub работает плагином. **Исключено из задания** (требует несуществующего слоя исполнения): | Пункт документа | Причина | |---|---| | §3 Hermes Execution Engine, §66 Execution Planner | движка исполнения агентов в Hub нет | | §12 Execution Tree, §56 snapshot execution | нечего вкладывать: `route_request` — один вызов, один ответ | | §31–35 Agent as Tool, input/output schema | межагентных вызовов не существует | | §37 отмена дерева, §38 таймауты на 4 уровнях | нечего отменять | | §39 failure propagation, §40–41 REVIEW и цикл доработки | требует исполнения ролей | | §1, §96 БД и миграции | БД нет, конфигурация в YAML/JSON | | §69 React graph-библиотека | стек — Python + CustomTkinter | | §61 permissions | системы прав нет | **Берётся из документа**: §68 (разделение слоёв), §30 (topology не меняется при provider fallback), §28 (не делать второй редактор маршрутизации), §43–44 (персистентность и версия схемы), §46–47 (валидация с показом на графе), §48–53 (layout, zoom, minimap, поиск), §58 (без выдуманных метрик), §60 (без секретов в графе), §62 (миграция текущих ролей), §72–75 (клавиатура, undo/redo, несохранённые изменения), §80–81 (различать типы связей не только цветом). --- ## P0-1. Hub должен быть демонстрируемо рабочим Главное требование задания. Приложение ни разу не проходило полный путь с живым аккаунтом: все скриншоты сняты на пустой конфигурации, где везде `Н/Д` и «Критическое состояние». **Что проверить и починить:** 1. **Мастер потерял назначение роли.** `add_account_wizard._finish` (строка 1451) теперь только пишет событие в журнал и вызывает `on_complete`. Вызова `AutoAssigner.assign_profile_to_role` там больше нет. Разобраться: достаточно ли того, что слот уже входит в цепочку роли по умолчанию, или подключённый аккаунт остаётся вне маршрутизации. Если достаточно — описать это в отчёте; если нет — вернуть назначение. 2. **Пустое состояние должно вести пользователя.** Сейчас на «Обзоре» при отсутствии аккаунтов — «Критическое состояние» и пять `Н/Д`, без единой подсказки, что делать. Первый экран нового пользователя обязан объяснять следующий шаг и давать кнопку к нему. 3. **Ручная проверка с настоящим аккаунтом — обязательна.** Подключить один реальный аккаунт (любого провайдера), убедиться, что он появился в «Аккаунтах», получил роль, виден на «Маршрутизации», и что тест профиля проходит. Приложить скриншоты **этих** состояний, а не пустых. Без пункта 3 задание не принимается. Скриншоты пустого приложения доказывают вёрстку, но не работоспособность. ## P0-2. «Команда» становится графом маршрутизации Превратить экран «Команда» в визуальный редактор на `CTkCanvas`. **Модель.** Узел = логическая роль из `config.roles` (`orchestrator`, `coder-primary`, `coder-secondary`, `reviewer`, `research`, `fast`) плюс узел оркестратора. Связь = позиция в цепочке отказоустойчивости: основной → резерв 1 → резерв 2. Типы связей минимально: `PRIMARY`, `FALLBACK`, `DELEGATE`. **Ключевой принцип (§68 документа):** граф описывает роли и их отношения. Какой провайдер, аккаунт и модель исполнят роль — решает существующий роутер. **Смена провайдера при отказе не меняет граф.** **Что должно работать:** - перемещение узлов, создание и удаление связей, изменение типа связи; - изменение графа **реально меняет цепочки** в `router_profiles.yaml` через `AutoAssigner.assign_profile_to_role` — это не картинка (§64 документа); - сохранение позиций, масштаба и области просмотра рядом с конфигурацией, с полем версии схемы; - миграция текущих шести ролей в граф по умолчанию при первом открытии; - автовыравнивание (иерархическое), после него ручные координаты не перезатираются; - zoom, вписать в экран, minimap для больших графов; - undo/redo для перемещения, добавления и удаления узлов и связей; - отметка «есть несохранённые изменения», без молчаливой потери. **Валидация перед сохранением** с показом ошибок прямо на графе: отсутствующий оркестратор, роль без профилей, недостижимый узел, ссылка на несуществующий профиль, дубли. ## P0-3. Инспектор узла При выборе роли справа: назначенные профили в порядке цепочки, текущий активный, провайдер, аккаунт, модель, состояние квоты, причина последнего переключения. Кнопка «Открыть маршрутизацию» ведёт в существующий раздел с выбранной ролью — **второй редактор маршрутизации не создавать** (§28). ## P1-4. Живая подсветка на реальных данных Данные уже есть, выдумывать ничего не нужно: | Что показать | Откуда | |---|---| | активный узел и связь | `PipelineNode.is_active`, `RolePipeline.active_profile_id` | | причина переключения | `PipelineNode.failover_reason` | | почему выбран провайдер | `selection_trace` в метаданных ответа | | нагрузка по ролям | телеметрия `by_role`, `total_calls` | | состояние квоты узла | `quota_status` | Обновлять по событиям `EventBus` (`ROUTING_UPDATED`, `QUOTA_UPDATED`, `ACCOUNT_*`), а не полным пересбором канвы на каждое событие (§95). Где данных нет — узел не подсвечивается, метка не рисуется. `Н/Д` вместо чисел, правило прежнее. --- ## Ограничения - Граница: ваша зона — `src/antigravity_provider/router/ui/**`, `hermes_hub_app.py`, `tests/test_ui_*.py`. Модель графа и её сохранение — тоже в UI-слое, поскольку это конфигурация представления; изменение цепочек идёт через существующий `AutoAssigner`. - Не создавать параллельные сущности: роли, профили, цепочки уже есть в `router_config`. - Не трогать движок маршрутизации, адаптеры, телеметрию. - Сеть и подпроцессы — не в UI-потоке. - Три темы сохранить, `#FFFFFF` фоном не использовать. - Логотип Hermes использовать существующий, псевдологотипы агентам не генерировать. - Тег `v0.1.1` не создавать. ## Критерии приёмки 1. Ни один файл чужой зоны не изменён. 2. **Скриншоты с подключённым живым аккаунтом**: «Обзор», «Аккаунты», «Команда», «Маршрутизация» — с реальными данными, а не `Н/Д`. 3. Пустое состояние на «Обзоре» объясняет следующий шаг и ведёт к нему. 4. В отчёте сказано, попадает ли подключённый через мастер аккаунт в маршрутизацию, и на основании чего это установлено. 5. Изменение графа отражается в `router_profiles.yaml` и переживает перезапуск вместе с позициями и масштабом; проверено тестом. 6. Миграция шести существующих ролей в граф выполняется без потери цепочек. 7. Валидация ловит цикл, недостижимый узел и ссылку на несуществующий профиль; ошибки видны на графе. 8. Живая подсветка работает от событий, без полного пересбора канвы. 9. Приложение запускается, все разделы открываются; граф держит не менее 20 узлов без заметных задержек. 10. Прогон **в обоих окружениях** — без UI-зависимостей и с `customtkinter`/`pillow`/`psutil`; обе команды и оба результата в отчёте. 11. `ruff check .` чисто; release gate не ухудшен. 12. Отчёт: `BASE_SHA`, `FINAL_SHA`, изменённые файлы, что из документа реализовано, что исключено и почему, известные ограничения. ## Главное Задача — не нарисовать красивый граф, а сделать так, чтобы владелец установил Hub, подключил аккаунт, увидел свою команду ролей, понял, кто чем исполняется и куда уйдёт запрос при исчерпании квоты. Граф — способ это показать и настроить, а не самоцель. ## Порядок сдачи Передать точный `FINAL_COMMIT_SHA`.