hermes-hub/agents/inbox/2026-08-21-B-codex-ui-redesign.md
Hermes Team 93883f9ce4 docs(task): split Plan A and UI redesign into two non-overlapping assignments
The UI redesign draft duplicated eight Plan A phases on the same files
(accounts_view, routing_view, hermes_hub_app, unified_health), which would have
put two agents into the same merge conflicts. Ownership is now split by file
path, with docs/UI_STATE_CONTRACT.md as the interface between them.

Four draft items were already implemented at 50fde5f and are marked as such
rather than reassigned: OAuth URL copy-before-open, keyed account card reuse,
the no-fake-metrics rule, and the importorskip guard.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 23:13:59 +07:00

164 lines
17 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Задание 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.
Скриншоты основных экранов приложить, если возможно.
---
## Главное
Интерфейс должен за несколько секунд отвечать на четыре вопроса: кто сейчас выполняет задачу, через какого провайдера, аккаунт и модель, сколько реального лимита осталось, и кто подхватит работу при недоступности исполнителя. Оптимизировать под ежедневное управление десятками аккаунтов, а не под красивый скриншот.