9 KiB
Hermes Hub UI redesign — фазы 2–6
Дата: 2026-08-21
Ветка: codex/ui-redesign
INITIAL_BASE_SHA: c6876e96b274b598ada964a6639b6c3ad038aebf
BASE_SHA после финального обязательного rebase на origin/main: e8a404be035fa04b5f76e3e572c6539fba0e83e4. Main продвинулся во время работы одним документационным коммитом Antigravity; этот файл не изменялся Codex.
FINAL_SHA: git rev-parse codex/ui-redesign в момент handoff (точный SHA указан в итоговом сообщении; commit не может содержать собственный SHA).
Изменённые файлы
src/antigravity_provider/router/hermes_hub_app.pysrc/antigravity_provider/router/ui/components.pysrc/antigravity_provider/router/ui/views/accounts_view.pysrc/antigravity_provider/router/ui/views/dashboard_view.pysrc/antigravity_provider/router/ui/views/health_view.pysrc/antigravity_provider/router/ui/views/logs_view.pysrc/antigravity_provider/router/ui/views/providers_view.pysrc/antigravity_provider/router/ui/views/routing_view.pysrc/antigravity_provider/router/ui/views/settings_view.pysrc/antigravity_provider/router/ui/views/team_view.pytests/test_ui_phase2_6.pyCODEX_UI_REDESIGN_REPORT.md
Файлы state/data/router, adapters, installer, scripts, config, legacy и чужие тесты не изменялись.
Переработанные экраны
Аккаунты и квоты
- Одна карточка соответствует одному
profile_idи переиспользуется между поколениями snapshot. QuotaBucketWidgetхранится по стабильномуQuotaBucket.id; существующая корзина обновляется на месте.- Идентичность выбирается в порядке: email →
account_identity→ display name → profile ID. PlanBadgeне создаётся: provenance тарифа отсутствует вProfileViewModel.- Все переданные корзины показываются отдельно; baseline/estimated корзины имеют явную пометку «оценка».
- Неизвестный остаток и время сброса показываются как
Н/Д. - Есть компактный/развёрнутый режим, сворачиваемые группы провайдеров, поиск и фильтры по провайдеру, здоровью и роли.
- Удаление профиля из snapshot уничтожает только соответствующую карточку.
Обзор
- Экран добавлен в основную навигацию.
- Показывает readiness, доступные провайдеры, подключённые аккаунты, готовые роли, активные маршруты и реальные warnings.
- Низкая квота показывается только для неоценочного snapshot с известным числом.
- Истёкшая авторизация показывается из
auth_state. - Выдуманные throughput/SLA/cost метрики отсутствуют.
Команда
- Иерархия разделена на «Оркестратор» и «Роли и агенты».
- Карточки keyed по
role_idи содержат роль, провайдера, аккаунт, модель и здоровье. - UI больше не запрашивает quota service; активная сессия и квота агента не выдумываются.
Маршрутизация
- Роли keyed по
role_id, узлы цепочки — поprofile_id. - Отображается порядок основной → резерв 1 → резерв 2 → резерв 3 и активный узел.
- Показываются только доступные provider/model/status поля.
- Причина переключения честно обозначена как
Н/Д. - Редактирование остаётся кнопочным; drag-and-drop не добавлялся.
Второстепенные экраны
- Providers и Health используют keyed cards/rows.
- Logs не читает backend или файлы напрямую: события отсутствуют в
HubSnapshot, поэтому экран показывает честноеН/Д. - Settings стал presentation-only и отправляет save/update команды в action layer приложения.
- About и мастер подключения сохранены;
_init_antigravity_oauthи шесть рабочих потоков подключения не изменялись.
Замер производительности: 50 аккаунтов
Тест: test_fifty_accounts_update_one_quota_without_rebuilding_other_cards.
| Состояние | Карточек создано | Карточек уничтожено | Quota widgets создано | Quota widgets уничтожено |
|---|---|---|---|---|
| Первичная отрисовка | 50 | 0 | 50 | 0 |
| После изменения квоты аккаунта A | 50 | 0 | 50 | 0 |
| Дельта операции | 0 | 0 | 0 | 0 |
Object identity всех 50 карточек до и после обновления совпадает. Отдельный тест удаления подтверждает: уничтожается одна карточка, соседняя сохраняет object identity.
Проверки
Без UI-зависимостей
uv run --isolated --no-project --with pytest --with pytest-asyncio --with anyio --with pyyaml --with pydantic --with requests --with httpx pytest -q
Результат: 160 passed, 28 skipped, 3 deselected in 9.55s.
С UI-зависимостями
Полный набор успешно проходил одной командой (202 passed, 9 skipped, 3 deselected). Для воспроизводимого финального прогона в uv-managed Python использованы два свежих Tk-процесса: его Tcl runtime нестабильно повторно создаёт root после root.destroy() в соседнем OAuth-тесте.
uv run --with customtkinter --with pillow --with psutil --with pytest --with pytest-asyncio pytest -q -k "not test_f_copy_before_open_browser"
uv run --with customtkinter --with pillow --with psutil --with pytest --with pytest-asyncio pytest -q tests/test_oauth_lifecycle.py::test_f_copy_before_open_browser
Результаты: 201 passed, 9 skipped, 4 deselected in 26.42s и 1 passed in 2.86s. В сумме весь UI-enabled набор зелёный; разделение процессов относится только к Tcl/Tk test runtime, не к приложению.
Статика и release gate
uv run --with ruff ruff check .
uv run python scripts/release_gate.py
Результат: Ruff — All checks passed; release gate — PASSED. Live manifest не менялся; package URL по-прежнему имеет внешний статус pending upload/404, как и на базе задачи.
Backend gaps
- Ни один провайдер не отдаёт live numeric quotas через опубликованный контракт; baseline отображается только как оценка.
- Разделение Antigravity на Claude/Gemini существует структурно, но численные значения не измеряются.
- Provenance тарифа не входит в
ProfileViewModel; поэтомуPlanBadgeскрыт. - В
AgentViewModelнет активной сессии и квоты. - В
PipelineNodeнет идентичности аккаунта, quota state и причины переключения. - События журнала не входят в
HubSnapshot; Dashboard и Logs не обращаются кEventLogServiceнапрямую. - Латентность, RPS, error rate, стоимость и удалённые SLA отсутствуют.
- UI не компенсирует эти пробелы собственным опросом, сканированием диска или синтетическими числами.
Известные ограничения
- В текущем контракте журнал нельзя показать без расширения
HubSnapshot. - Настройки читаются/сохраняются action layer приложения; snapshot не содержит отдельной settings-модели.
- Live release package остаётся внешней задачей. По B2 тег, релиз и manifest не создавались и не изменялись.