hermes-hub/agents/inbox/2026-08-21-B2-codex-ui-redesign-phase2.md
Hermes Team c6876e96b2 docs(task): reissue the UI redesign assignment against the published contract
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>
2026-08-21 08:13:46 +07:00

14 KiB
Raw Permalink Blame History

Задание B2 (Codex): переработка интерфейса, фазы 26

Дата поступления

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».


Главное

Интерфейс должен за несколько секунд отвечать: кто выполняет задачу, через какого провайдера, аккаунт и модель, сколько лимита осталось, кто подхватит при недоступности. Там, где данных нет, — честное «Н/Д». Пустое место лучше выдуманного числа.