# Задание B2 (Codex): переработка интерфейса, фазы 2–6 ## Дата поступления 2026-08-21 ## База Проверочный HEAD на момент выдачи: **`39b1f92`**, `origin/main` = `39b1f92`. Перед началом: `git fetch`, зафиксировать фактический `BASE_SHA`, **обязательно подтянуть `main`** — с момента вашей PHASE 1 в него вошло 8 коммитов. ## Ветка `codex/ui-redesign` — она отстала, начните с `git rebase origin/main` либо создайте новую от `origin/main`. --- ## Это заменяет предыдущее задание Предыдущая выдача была ошибочной: вам передали Задание A (слой состояния), предназначенное Antigravity. Ту работу Antigravity уже выполнил и она влита в `main`. **Всё, что вы делали по слою состояния, не нужно** — не пытайтесь это дослать или смержить, оно конфликтует с влитым. Возьмите чистый `origin/main` и работайте только по этому документу. --- ## ГРАНИЦА РАБОТ Antigravity продолжает Задание A (реальный сбор квот, релизная инфраструктура, остатки долгов). Разделение по файлам прежнее. **Ваша зона:** ``` src/antigravity_provider/router/ui/** src/antigravity_provider/router/hermes_hub_app.py tests/test_ui_*.py ``` **Чужая зона (НЕ трогать):** ``` src/antigravity_provider/router/*.py (state_store, unified_health, scheduler, event_bus, quota_collector, model_registry, router_engine, profile_manager, *_oauth, …) src/antigravity_provider/router/adapters/** src/antigravity_provider/*.py scripts/**, installer/**, config/**, legacy/** tests/** кроме tests/test_ui_*.py ``` Не хватает данных для отрисовки — **не добавляйте сбор в UI**. Зафиксируйте в отчёте разделом «Backend gaps» и покажите честное отсутствие данных. --- ## Что уже сделано — не переделывать **Ваша PHASE 1 влита** (`a7027b4`): дизайн-токены с семантическими ролями, уплотнённая типографика, библиотека компонентов (`PlanBadge`, `QuotaBar`, `QuotaBucketWidget`, `AccountCardWidget`, `AgentCardWidget`, `RouteTargetWidget`, `EmptyState`, `SearchField`, `FilterButton`, `ActionButton`, `IconButton`, `ConfirmDialog`, `Toast`, `ellipsize_text`, `Tooltip`). Стройте на них. Одна правка в вашем тесте: `test_unknown_quota_is_supported_explicitly` сравнивал объект аннотации со строкой и не мог пройти — переписан через `typing.get_args`. Причина в том, что вы прогоняли тесты без `customtkinter`, где ваши UI-тесты **пропускаются**. Поставьте зависимости локально, иначе снова будете отчитываться о непроверенном. **Сделано другими, не откатывать:** - `hermes_hub_app.py` больше не передаёт `app_state` в `update_data`; все view защищены проверкой `isinstance(snapshot, HubSnapshot)` — это чинило краш при старте `'dict' object has no attribute 'readiness'`; - `add_account_wizard.py` — распаковка `start_profile_oauth` приведена к 3-кортежу `(session_id, auth_url, port)` и добавлено поле `self.oauth_port`. **Без этого подключение Antigravity-аккаунта не работает вовсе.** Если ваша ветка содержит старый двухэлементный вариант — при rebase возьмите версию из `main`; - `conftest.py` пропускает GUI-тесты только по маркеру `ui`, не по имени; модули с GUI-импортом обязаны иметь `pytest.importorskip("customtkinter")` на уровне модуля — это проверяется `tests/test_import_invariants.py`; - `scan_all()` из UI убран: все view уже принимают `snapshot` и не ходят в backend сами. Сохраните это свойство. --- ## Контракт данных **`docs/UI_STATE_CONTRACT.md` опубликован** — это единственный источник правды о том, какие поля существуют. Раздел 7 «Backend gaps» перечисляет 12 пробелов; из них прямо влияют на вас: | Пробел | Следствие для UI | |---|---| | Живых числовых квот **нет ни у одного провайдера**, все корзины `baseline` | Показывать как оценку; точные проценты не выдавать за измерение | | Разделение корзин Antigravity есть структурно, значения не измеряются | Отрисовать структуру, значения — с пометкой оценки | | Происхождение тарифа не доходит до `ProfileViewModel` | `PlanBadge` не показывать, пока поле не появится | | `AgentViewModel` не содержит активной сессии и квоты | В карточке агента эти строки — `Н/Д` либо отсутствуют | | `PipelineNode` не содержит идентичности аккаунта, состояния квоты и причины переключения | В цепочке маршрута показать то, что есть; остальное не выдумывать | | Большинство точечных событий объявлены, но никто их не публикует | Не рассчитывать на дельта-события; обновление по снапшоту | | Латентность, RPS, проценты ошибок, стоимость отсутствуют | Только `Н/Д` либо блок скрыт | Если поле нужно, а его нет — это строка в вашем отчёте, а не повод достать данные самому. --- ## Область задачи ### PHASE 2 — Аккаунты и квоты (приоритет) Экран уже на keyed-дельте (`self._cards[profile_id].update_from_model(...)`) — сохранить и достроить: - **внутренний `quota_box` всё ещё пересоздаётся целиком** — перевести на `QuotaBucketWidget` со стабильным ключом и обновлением на месте; - идентичность аккаунта по контракту: email → username → provider user ID → внятный fallback; «Google account #1» недопустимо, если реальная идентичность есть; - мульти-корзинные квоты: сколько корзин отдал `QuotaSnapshot`, столько и показать, со своими метками и временем сброса; признак `is_estimated` — визуально явный; - компактный и развёрнутый режим карточки; - группировка по провайдерам со сворачиванием, поиск, фильтры по провайдеру/здоровью/роли; - удаление аккаунта затрагивает только его карточку. ### PHASE 3 — Обзор Dashboard: состояние системы, число доступных провайдеров, подключённых аккаунтов, готовых ролей, предупреждения и ошибки, компактное представление активного маршрута, аккаунты с низкой квотой и с истёкшей авторизацией, последние реальные события журнала. Всё, чего нет в контракте, — `Н/Д` или блок скрыт. Никаких «842 rps» и «99.98 %» из макетов. ### PHASE 4 — Команда Иерархия «оркестратор → роли → агенты». Для агента: имя, роль, провайдер, аккаунт, модель, здоровье. Активная сессия и квота — по контракту отсутствуют, поэтому либо `Н/Д`, либо не показывать вовсе. ### PHASE 5 — Маршрутизация Цепочка отказоустойчивости по ролям: основной → резерв 1 → 2 → 3, с активным узлом. Провайдер и модель есть; идентичность аккаунта, состояние квоты и причина переключения в `PipelineNode` отсутствуют — не выдумывать. Редактор оставить кнопочным/селекторным, drag-and-drop не делать. ### PHASE 6 — Второстепенные экраны Единый стиль для состояния, журнала, настроек, «о программе», мастера и диалогов. `health_view`, `providers_view`, `routing_view`, `logs_view` пересоздают виджеты через `winfo_children()` + `destroy()` — перевести на обновление по ключу там, где список стабилен. **Мастер подключения не ломать.** Работающие потоки: Antigravity OAuth, Codex OAuth, Codex API, OpenCode, Claude, Grok, вставка из буфера. Особенно бережно с `_init_antigravity_oauth` — он только что чинился. --- ## Ограничения - Не переписывать на Tauri/React. Готовить архитектуру к разделению — да, мигрировать — нет. - Не удалять `router/ui` целиком ради переписывания заново; после каждой фазы приложение запускается. - Сеть, опрос OAuth, subprocess, сканирование диска — только в фоне, никогда в UI-потоке. - Секреты: не логировать токены и коды, ключи показывать маскированно. - Тег `v0.1.1` не создавать, релиз не публиковать, манифест не трогать. --- ## Критерии приёмки 1. Ни один файл чужой зоны не изменён: `git diff --name-only BASE_SHA..HEAD` не содержит путей из списка. 2. Приложение запускается после каждой фазы; экран «Команда» открывается без ошибок. 3. `quota_box` обновляется по ключу; изменение аккаунта A не перерисовывает карточку B; удаление затрагивает одну карточку. 4. Идентичность аккаунта берётся по приоритету из контракта. 5. Оценочные квоты визуально помечены; ни одно значение не подано как измерение провайдера. 6. `PlanBadge` не отображается, пока происхождение тарифа не приходит в модель. 7. Отсутствующие данные — `Н/Д` или скрытый блок; выдуманных метрик нет. 8. Мастер: URL авторизации доступен и копируется до открытия браузера; все шесть потоков подключения работают; вставка из буфера работает. 9. Ни один view не обращается к backend напрямую — только к переданному снапшоту. 10. UI-тесты начинаются с `pytest.importorskip("customtkinter")`. 11. Прогон **в обоих окружениях**: без UI-зависимостей и с установленными `customtkinter`, `pillow`, `psutil`. Оба зелёные. Отчёт содержит обе команды и оба результата. 12. `ruff check .` чисто; release gate не ухудшен. ## Проверка производительности На 50 аккаунтах изменить квоту одного и зафиксировать в отчёте число уничтоженных и созданных виджетов до и после. Ожидается обновление одной карточки. ## Отчёт `CODEX_UI_REDESIGN_REPORT.md`: `BASE_SHA`, `FINAL_SHA`, ветка, изменённые файлы, переработанные экраны, что реально поддержано по идентичности/тарифам/квотам, замер производительности, точные команды и результаты **обоих** прогонов, известные ограничения и раздел «Backend gaps». --- ## Главное Интерфейс должен за несколько секунд отвечать: кто выполняет задачу, через какого провайдера, аккаунт и модель, сколько лимита осталось, кто подхватит при недоступности. Там, где данных нет, — честное «Н/Д». Пустое место лучше выдуманного числа.