# Задание B (Codex): переработка интерфейса Hermes Hub ## Дата поступления 2026-08-21 ## База Проверочный HEAD на момент выдачи: **`50fde5f`**. Перед началом выполнить `git fetch`, зафиксировать фактический `BASE_SHA`, изучить существующий UI по коду, не по названиям файлов. ## Ветка `codex/ui-redesign` ## Зависимость Работа ведётся против контракта `docs/UI_STATE_CONTRACT.md`, который публикует Antigravity в рамках Задания A. **Дождаться публикации контракта перед PHASE 2.** PHASE 1 (токены, компоненты) можно начинать сразу — она от контракта не зависит. --- ## ГРАНИЦА РАБОТ — читать первым Параллельно выполняется **Задание A (Antigravity)** — слой состояния и данных. Разделение проходит **по файлам**. **Ваша зона (можно менять):** ``` src/antigravity_provider/router/ui/** ← theme, components, assets, views, wizard src/antigravity_provider/router/hermes_hub_app.py tests/test_ui_*.py ``` **Чужая зона (НЕ трогать):** ``` src/antigravity_provider/router/state_store.py src/antigravity_provider/router/unified_health.py src/antigravity_provider/router/scheduler.py src/antigravity_provider/router/event_bus.py src/antigravity_provider/router/quota_collector.py src/antigravity_provider/router/model_registry.py src/antigravity_provider/router/router_engine.py src/antigravity_provider/router/router_config.py src/antigravity_provider/router/adapters/** src/antigravity_provider/router/*_oauth.py src/antigravity_provider/router/profile_manager.py scripts/**, installer/**, config/** ``` Если для отрисовки не хватает данных — **не добавлять сбор данных в UI**. Зафиксировать пробел в разделе «Backend gaps» отчёта и отрисовать честное отсутствие данных. --- ## УЖЕ СДЕЛАНО — не переделывать Проверено по коду на `50fde5f`. Эти пункты из исходного черновика закрыты: | Пункт черновика | Факт | |---|---| | §14 OAuth URL копируется до нажатия «Открыть браузер» | Реализовано: `_init_antigravity_oauth()` вызывается при входе в шаг 2, поля `oauth_url_entry` и `copy_url_btn` уже есть | | §22 AccountsView пересоздаёт все карточки | Уже keyed-дельта: `if p.profile_id in self._cards: card.update_from_model(...)`. Осталась пересборка внутреннего `quota_box` — только её и чинить | | §36 запрет выдуманных метрик | Обеспечен, есть 7 регрессионных тестов (`test_data_truthfulness_and_oauth_security.py`) | | §37.14 `importorskip` для UI-тестов | Сделано и закреплено CI-job `headless` + `tests/test_import_invariants.py` | **Изъято из задания как чужой scope** (передано Antigravity): §23 RoutingView-перестройка на уровне данных, §24 удаление `scan_all` из сервиса, §25 stale-политика, §27 планировщик обновлений, §28 событийная модель, §9/§10 сбор мульти-корзинных квот, §17 логика выбора модели. Вам остаётся **отрисовка** того, что отдаёт контракт. --- ## Область задачи ### PHASE 1 — Фундамент (не зависит от контракта) **Design tokens.** Единый набор: цвета, отступы, радиусы, типографика, высоты контролов, паддинги карточек. Никаких случайных `padx=7`/`padx=11` по коду. Тема остаётся тёмной (Hermes), но константы централизованы так, чтобы светлая тема later не требовала переписывания views. **Библиотека компонентов.** Нормализовать и достроить: `HubCard`, `SectionHeader`, `StatusBadge`, `PlanBadge`, `ProviderBadge`, `QuotaBar`, `QuotaBucketWidget`, `AccountCardWidget`, `AgentCardWidget`, `RouteTargetWidget`, `EmptyState`, `SearchField`, `FilterButton`, `ActionButton`, `IconButton`, `ConfirmDialog`, `Toast`. Одинаковая разметка не должна дублироваться по views. **Семантика цвета.** Зелёный — работает; янтарный — предупреждение, резерв, приближение к лимиту; красный — ошибка, недоступен, исчерпан; серый — отключён, неизвестно. **Золото — только брендинг и акцент, не статус «всё хорошо».** **Плотность и типографика.** Уменьшить заголовки и пустоты, унифицировать паддинги, высоты кнопок, радиусы. Длинные email — многоточие с полным текстом в подсказке. Карточки аккаунтов одинаковой геометрии. **Иконки.** Один стиль, на существующей инфраструктуре ассетов. Не менять всё на emoji и не тянуть тяжёлую зависимость. Логотип Hermes Hub и символика H остаются главным идентификатором. ### PHASE 2 — Аккаунты и квоты (приоритетная страница) По контракту отрисовать так, чтобы за секунды читалось: провайдер, идентичность аккаунта, тариф, способ авторизации, здоровье, назначенная роль. **Идентичность обязательна.** Порядок предпочтения: email → username → provider user ID → внятный fallback. Не показывать «Google account #1», если реальная идентичность доступна в `ProfileViewModel`. **Тариф** показывать только когда провайдер его реально отдаёт (см. контракт), иначе не показывать бейдж вовсе. **Мульти-корзинные квоты.** Отрисовать столько корзин, сколько отдаёт `QuotaSnapshot`, с их собственными метками, остатками и временем сброса. Не сводить к одному проценту. Если снапшот помечен `is_estimated` — показать это явно, признак уже есть в модели. **Компактный режим.** Свёрнутая карточка: провайдер, идентичность, тариф, роль/статус, полосы квот, время сброса, действия. Развёрнутая: подробные корзины, доступные модели, роли, авторизация, приоритет в маршруте, отметки времени. **Группировка по провайдерам** со сворачиванием, плюс поиск и фильтры по провайдеру, здоровью, роли, тарифу — насколько это не усложняет код чрезмерно. **Дельта-отрисовка.** Внутренний `quota_box` не пересоздавать целиком: корзины обновлять по стабильному ключу. Удаление аккаунта уничтожает только его карточку. ### PHASE 3 — Обзор Dashboard как обзорный центр управления, **только реальные данные**: состояние системы, доступные провайдеры, активные аккаунты, доступные агенты, предупреждения и ошибки; компактное представление активного маршрута; аккаунты с низкой квотой, приближающимся сбросом, исчерпанные, недоступные, с истёкшей авторизацией; последние реальные события журнала. Если backend не собирает величину — «Н/Д» или скрыть блок. Никаких «842 rps», «99.98 %», «1.23 % ошибок» из макетов, пока таких данных нет. Пункты навигации без функциональности — скрыть либо честный «Позже», но не пустые страницы и не выдуманные метрики. ### PHASE 4 — Команда Иерархия «оркестратор → роли → агенты». Для агента: имя, роль, провайдер, аккаунт, модель, здоровье, индикатор квоты, активная сессия — там, где данные есть в контракте. Существующее переиспользование виджетов в `TeamView` не ломать. ### PHASE 5 — Маршрутизация Понятная цепочка отказоустойчивости для каждой роли: основной → резерв 1 → резерв 2 → резерв 3. Для каждого узла: провайдер, аккаунт, модель, статус квоты, здоровье. Показывать активный узел и, если причина есть в состоянии маршрута, — почему произошло переключение (квота, авторизация, таймаут, недоступность, ручное). Сложный drag-and-drop не делать: редактор оставить кнопочным/селекторным, подготовив модель состояния под будущий React-фронтенд. ### PHASE 6 — Второстепенные экраны Привести к единому стилю: состояние, журнал, настройки, «о программе», мастер подключения, диалоги. Мастер должен визуально соответствовать новому интерфейсу; **не ломать** уже работающие потоки Antigravity OAuth, Codex OAuth, Codex API, OpenCode, Claude, Grok и вставку из буфера. --- ## Ограничения **Не переписывать приложение на Tauri/React.** Архитектура UI готовится к будущему разделению «состояние → ViewModel → компоненты», но миграция в этот scope не входит. **Не делать слепую переработку.** Запрещено «удалить `router/ui` целиком и написать заново». Итеративно, после каждой фазы приложение запускается. **Не блокировать UI-поток.** Сетевые запросы, опрос OAuth, получение квот, subprocess, сканирование диска — только в фоне, на существующей инфраструктуре потоков. **Секреты.** Не логировать access/refresh-токены и коды авторизации, не показывать API-ключи в открытом виде, не сохранять учётные данные в состоянии UI. Показ — только маскированный. **Тег `v0.1.1` не создавать**, релиз не публиковать, манифест не менять. --- ## Критерии приёмки 1. Ни один файл из чужой зоны не изменён (проверяется `git diff --name-only`). 2. Приложение запускается после каждой фазы. 3. Аккаунты показывают реальную идентичность; тариф — только когда он известен. 4. Отрисовывается несколько квотных корзин; ни одна не сведена к единому проценту; оценочные данные помечены. 5. `quota_box` обновляется по ключу, а не пересоздаётся; удаление аккаунта затрагивает только его карточку. 6. Изменение аккаунта A не перерисовывает карточку аккаунта B. 7. Маршрутизация показывает цепочку основной/резервы со статусами. 8. Команда показывает провайдера, аккаунт и модель там, где данные доступны. 9. OAuth URL копируется до открытия браузера; вставка из буфера работает; ни один из существующих потоков подключения не сломан. 10. Нет выдуманных метрик: отсутствующие данные показаны как «Н/Д» либо скрыты. 11. Секреты не попадают в UI и логи. 12. Проверено на 1280×720, 1366×768, 1920×1080 при масштабировании Windows 100 %, 125 %, 150 % — критичные элементы управления не уходят за экран. 13. Тесты: отрисовка идентичности, бейджа тарифа, нескольких корзин, отсутствующих полей квоты, неизвестного провайдера, неизвестного тарифа, длинного email, копирования OAuth URL, работы буфера обмена, цепочки маршрутизации, удаления одной карточки, независимости обновления A и B. Все UI-модули начинаются с `pytest.importorskip("customtkinter")`. 14. Полный `pytest` без новых падений; прогон **в окружении без UI-зависимостей** тоже зелёный; `ruff check .` чисто; release gate не ухудшен. ## Проверка производительности На тестовых данных из 50 аккаунтов изменить квоту одного и зафиксировать в отчёте количество уничтоженных и созданных виджетов до и после изменений. Ожидается обновление одной карточки, а не пересоздание пятидесяти. ## Отчёт `CODEX_UI_REDESIGN_REPORT.md`: `BASE_SHA`, `FINAL_SHA`, ветка, изменённые файлы, что переработано по экранам, что реально поддержано по идентичности/тарифам/квотам, результат замера производительности, точные команды и результат тестов (`X passed / Y skipped / Z failed`), известные ограничения и **обязательный раздел «Backend gaps»** — чего нельзя сделать одними изменениями UI. Скриншоты основных экранов приложить, если возможно. --- ## Главное Интерфейс должен за несколько секунд отвечать на четыре вопроса: кто сейчас выполняет задачу, через какого провайдера, аккаунт и модель, сколько реального лимита осталось, и кто подхватит работу при недоступности исполнителя. Оптимизировать под ежедневное управление десятками аккаунтов, а не под красивый скриншот.