Codex was handed Task A by mistake and duplicated the state-layer work that
Antigravity had already merged. This reissues its own scope, rebased on the
facts at 39b1f92: PHASE 1 is merged, docs/UI_STATE_CONTRACT.md now exists, and
the twelve backend gaps it declares are mapped to what the UI may and may not
render.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
14 KiB
Задание 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не создавать, релиз не публиковать, манифест не трогать.
Критерии приёмки
- Ни один файл чужой зоны не изменён:
git diff --name-only BASE_SHA..HEADне содержит путей из списка. - Приложение запускается после каждой фазы; экран «Команда» открывается без ошибок.
quota_boxобновляется по ключу; изменение аккаунта A не перерисовывает карточку B; удаление затрагивает одну карточку.- Идентичность аккаунта берётся по приоритету из контракта.
- Оценочные квоты визуально помечены; ни одно значение не подано как измерение провайдера.
PlanBadgeне отображается, пока происхождение тарифа не приходит в модель.- Отсутствующие данные —
Н/Дили скрытый блок; выдуманных метрик нет. - Мастер: URL авторизации доступен и копируется до открытия браузера; все шесть потоков подключения работают; вставка из буфера работает.
- Ни один view не обращается к backend напрямую — только к переданному снапшоту.
- UI-тесты начинаются с
pytest.importorskip("customtkinter"). - Прогон в обоих окружениях: без UI-зависимостей и с установленными
customtkinter,pillow,psutil. Оба зелёные. Отчёт содержит обе команды и оба результата. ruff check .чисто; release gate не ухудшен.
Проверка производительности
На 50 аккаунтах изменить квоту одного и зафиксировать в отчёте число уничтоженных и созданных виджетов до и после. Ожидается обновление одной карточки.
Отчёт
CODEX_UI_REDESIGN_REPORT.md: BASE_SHA, FINAL_SHA, ветка, изменённые файлы, переработанные экраны, что реально поддержано по идентичности/тарифам/квотам, замер производительности, точные команды и результаты обоих прогонов, известные ограничения и раздел «Backend gaps».
Главное
Интерфейс должен за несколько секунд отвечать: кто выполняет задачу, через какого провайдера, аккаунт и модель, сколько лимита осталось, кто подхватит при недоступности. Там, где данных нет, — честное «Н/Д». Пустое место лучше выдуманного числа.