# Hermes Hub — редизайн по утверждённому макету B5

Дата: 2026-08-21

Ветка: `codex/mockup-redesign`

BASE_SHA: `69cbefcab6d566929d99b7ad7dc1b6b9824bfb2b` (`origin/main`, обновлён перед финальной интеграцией)

FINAL_SHA: `git rev-parse codex/mockup-redesign` в момент handoff. Точный SHA указан в итоговом сообщении, поскольку commit не может содержать собственный SHA.

## Реализовано

- Один утверждённый layout для трёх схем: `dark`, `hybrid`, `light`.
- Все палитры находятся в `theme.py`, имеют одинаковый набор токенов и меняют всё окно после сохранения настройки.
- Выбор темы сохраняется в `hub_settings.json`; приложение применяет его при следующем запуске.
- Общий каркас соответствует макету: брендовая левая панель, прокручиваемая навигация, пользователь и версия снизу, глобальная верхняя строка с состоянием, `Ctrl + K`, добавлением аккаунта и служебными действиями.
- Dashboard: пять компактных KPI, провайдеры → оркестратор → роли, правая панель состояния, реальные события снизу.
- Второй визуальный проход заменил прямоугольный центр круглым брендированным оркестратором, добавил плавные связи со стрелками и реальными подписями, provider logos, quota-bars, sparklines и табличную ленту событий.
- Подключён утверждённый пользователем `logo_approved.png`; боковое меню и кнопки header используют единый рисуемый контурный набор иконок вместо разнородных Unicode-глифов.
- После обновления контракта подключены реальные доли/число вызовов и P50 по провайдерам, вызовы по ролям, active calls и локальные CPU/RAM/disk/network.
- Добавлены отдельные вкладки «Квоты и лимиты» и «Аналитика» на данных `HubSnapshot`/`TelemetryService`.
- Журнал получает реальные события через action layer приложения, поддерживает поиск и фильтр уровня.
- На «Аккаунты» возвращены `test`, `set_main`, `set_orchestrator`, `assign_role`; механический и фактический UI-тесты проверяют все четыре триггера.
- Сохранена keyed-дельта карточек и квотных корзин.
- Цветовые литералы вне `theme.py` удалены; отдельный тест запрещает их повторное появление.

## Изменённые файлы

- `src/antigravity_provider/router/hermes_hub_app.py`
- `src/antigravity_provider/router/ui/theme.py`
- `src/antigravity_provider/router/ui/components.py`
- `src/antigravity_provider/router/ui/assets.py`
- `src/antigravity_provider/router/ui/add_account_wizard.py`
- `assets/branding/logo/logo_approved.png`
- `src/antigravity_provider/router/ui/views/dashboard_view.py`
- `src/antigravity_provider/router/ui/views/logs_view.py`
- `src/antigravity_provider/router/ui/views/settings_view.py`
- `src/antigravity_provider/router/ui/views/team_view.py`
- `src/antigravity_provider/router/ui/views/analytics_view.py`
- `src/antigravity_provider/router/ui/views/quotas_view.py`
- `tests/test_ui_mockup_redesign.py`
- `tests/test_ui_screenshot_harness.py`
- `artifacts/mockup-redesign/*.png`
- `CODEX_MOCKUP_REDESIGN_REPORT.md`

Backend/state/adapters, installer, scripts, config, legacy и чужие тесты не изменялись.

## Проверка размеров и scaling

Проверены `1280×720`, `1366×768`, `1920×1080`. На наименьшем размере дополнительно проверены 100%, 125% и 150% widget scaling. Правая граница поиска и кнопки добавления оставалась внутри верхней строки:

```text
scale 1.00: search_right=539, add_right=1074, header_width=1090
scale 1.25: search_right=661, add_right=1023, header_width=1043
scale 1.50: search_right=781, add_right=971,  header_width=995
```

Навигация помещена в прокручиваемую область, поэтому нижние разделы доступны при 150%.

## Скриншоты — все вкладки и темы

| Вкладка | Тёмная | Гибрид | Светлая |
|---|---|---|---|
| Обзор | [dark](artifacts/mockup-redesign/overview-dark.png) | [hybrid](artifacts/mockup-redesign/overview-hybrid.png) | [light](artifacts/mockup-redesign/overview-light.png) |
| Команда | [dark](artifacts/mockup-redesign/team-dark.png) | [hybrid](artifacts/mockup-redesign/team-hybrid.png) | [light](artifacts/mockup-redesign/team-light.png) |
| Аккаунты | [dark](artifacts/mockup-redesign/accounts-dark.png) | [hybrid](artifacts/mockup-redesign/accounts-hybrid.png) | [light](artifacts/mockup-redesign/accounts-light.png) |
| Маршрутизация | [dark](artifacts/mockup-redesign/routing-dark.png) | [hybrid](artifacts/mockup-redesign/routing-hybrid.png) | [light](artifacts/mockup-redesign/routing-light.png) |
| Провайдеры | [dark](artifacts/mockup-redesign/providers-dark.png) | [hybrid](artifacts/mockup-redesign/providers-hybrid.png) | [light](artifacts/mockup-redesign/providers-light.png) |
| Квоты | [dark](artifacts/mockup-redesign/quotas-dark.png) | [hybrid](artifacts/mockup-redesign/quotas-hybrid.png) | [light](artifacts/mockup-redesign/quotas-light.png) |
| Аналитика | [dark](artifacts/mockup-redesign/analytics-dark.png) | [hybrid](artifacts/mockup-redesign/analytics-hybrid.png) | [light](artifacts/mockup-redesign/analytics-light.png) |
| Состояние | [dark](artifacts/mockup-redesign/health-dark.png) | [hybrid](artifacts/mockup-redesign/health-hybrid.png) | [light](artifacts/mockup-redesign/health-light.png) |
| Журнал | [dark](artifacts/mockup-redesign/logs-dark.png) | [hybrid](artifacts/mockup-redesign/logs-hybrid.png) | [light](artifacts/mockup-redesign/logs-light.png) |
| Настройки | [dark](artifacts/mockup-redesign/settings-dark.png) | [hybrid](artifacts/mockup-redesign/settings-hybrid.png) | [light](artifacts/mockup-redesign/settings-light.png) |
| О программе | [dark](artifacts/mockup-redesign/about-dark.png) | [hybrid](artifacts/mockup-redesign/about-hybrid.png) | [light](artifacts/mockup-redesign/about-light.png) |

## Расхождения с макетом

| Элемент макета | Реализация и причина |
|---|---|
| «Квота сегодня 78%» | Показано `Н/Д`, пока нет хотя бы одной реально измеренной корзины. Baseline `None` не превращается в процент. |
| «Активные задачи 24» | Заменено на реальные вызовы роутера из telemetry. Подсистемы задач/очередей нет. |
| «Окно обслуживания 09:00–21:00» | Заменено количеством реальных failover-переключений. Понятия окна обслуживания нет. |
| Доли провайдеров 45/35/20% и 128/74/56 запросов по ролям | Показываются только из реального `metrics.telemetry.by_provider/by_role`; без вызовов отображаются online/`Н/Д`, а не цифры макета. |
| Латентность каждого провайдера | Показывается реальный P50 из `by_provider`; без измерений значение скрыто. |
| CPU, память, диск, сеть | Показываются реальные локальные измерения `psutil`; при недоступном `psutil` — `Н/Д`. |
| Очереди задач | Блок исключён: подсистемы очередей в продукте нет. |
| Раздел «Инциденты» | Не создан: вместо него используется реальный журнал с фильтром ошибок. |
| Кривые соединения | Реализованы адаптивные сглаженные линии со стрелками и подписями реальной доли/числа вызовов; при отсутствии telemetry показывается `Н/Д`. |
| Версия `2.8.1` на макете | Показывается реальная версия пакета `0.1.1`. |

## Проверки

### Headless без UI-зависимостей

```powershell
uv run --isolated --no-project --with pytest --with pyyaml --with pydantic --with requests --with httpx --with psutil python -m pytest -q
```

Результат: `193 passed, 26 skipped, 3 deselected in 14.32s`.

### UI-enabled

Из-за известной повторной инициализации Tcl/Tk набор выполнен в трёх свежих процессах:

```powershell
uv run --extra dev python -m pytest -q --ignore=tests/test_ui_phase2_6.py --deselect=tests/test_oauth_lifecycle.py::test_f_copy_before_open_browser
uv run --extra dev python -m pytest -q tests/test_ui_phase2_6.py
uv run --extra dev python -m pytest -q tests/test_oauth_lifecycle.py::test_f_copy_before_open_browser
```

Результаты: `244 passed, 2 skipped, 4 deselected`; `4 passed`; `1 passed`. Итог уникального набора: `249 passed, 2 skipped, 3 deselected`, ошибок нет.

### Статика и release gate

```powershell
uv run ruff check .
uv run --extra dev python scripts/release_gate.py
```

Результат: Ruff — `All checks passed!`; release gate — `PASSED`, публичный package и hash подтверждены.

## Backend gaps

1. Внешние provider RPS/SLA, task queues и maintenance windows отсутствуют в продукте; UI их не синтезирует.
2. Baseline-квоты остаются `None`; реальное число появляется только из provider claim/runtime event.
3. `HubSnapshot` не содержит журнал событий; app action layer передаёт в views безопасные presentation-объекты из `EventLogService`, сами views backend не читают.
4. Network contract содержит накопительные bytes с момента загрузки ОС, а не мгновенную скорость; UI показывает объём, не выдуманный Мбит/с.
5. Тег `v0.1.1`, release и manifest не создавались и не изменялись.
