hermes-hub/agents/inbox/2026-08-24-A21-antigravity-flash-web-parity.md
Hermes Team 972e34911c docs(task): A21 — четыре недостающих экрана веб-интерфейса
Веб покрывает пять экранов из девяти. Не хватает Аналитики, Состояния,
Журнала событий и Настроек.

Данные для первых двух уже в снапшоте: metrics.telemetry (19 вызовов,
15 отказов, латентность p50/p95/max) и metrics.host плюс readiness.
Для двух других источника в API нет вовсе — нужны GET /api/events
(EventLogService в backend есть) и GET /api/settings без секретов.

Зона Flash на это задание расширена на router/web/** целиком, включая
server.py: A20 в web/ не заходит, конфликта не будет.

Обнаружение моделей в задание НЕ включено, хотя A18 его пропустил:
причина оказалась глубже и лежит в авторизации agy, это чинит A20.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-23 22:49:45 +07:00

162 lines
11 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.

# Задание A21 (Antigravity Flash): довести веб-интерфейс до паритета с десктопом
## Дата поступления
2026-08-24
## База
Проверочный HEAD на момент выдачи: **`187f181`**.
## Ветка
`antigravity/web-parity`
---
## Порядок работы с git
```
cd <каталог репозитория>; git fetch origin --prune; git status
git checkout main; git pull --ff-only origin main
git checkout -b antigravity/web-parity
git commit -m "..." <- сначала коммит
git push -u origin antigravity/web-parity
```
В конце:
```
git status <- дерево чистое
git log --oneline -1 origin/antigravity/web-parity <- ваш финальный коммит
```
---
## Что принято по A18
Действие `set_model` сделано и устроено правильно: живёт в `action_handler`, валидирует модель по списку провайдера, десктоп и веб зовут одно и то же.
**Но обнаружение моделей (P0-2) не сделано вовсе**`model_discovery_service` и зонд не менялись, ручного обновления нет, кэша на диске нет. Из-за этого `set_model` отклоняет **любую** модель, включая настоящую: сравнивать не с чем.
```
do_set_model('ag-w2','gemini-3.1-pro-high')
-> (False, "список моделей провайдера ещё не получен")
```
Это второе задание подряд, где пункт про обнаружение остался нетронутым. **В это задание он не включён** — выяснилось, что причина глубже и лежит в авторизации: `agy models` отвечает «Please sign in» при шести валидных OAuth-профилях, потому что Hub не пишет `oauth_creds.json` в формате, который CLI ожидает. Это чинит **A20**, и обнаружение доделывается там же, после авторизации.
Отдельно скажу прямо, потому что это повторяется: **пропущенный пункт нужно называть в отчёте пропущенным.** Не «сделано», не молчание — просто «не успел» или «не смог, потому что». Один такой абзац экономит раунд.
## Что принято по A16 — и это важно для нового задания
Веб-клиент был лучшей работой за все раунды. Экран «Аккаунты» с квотами, пулами и периодами, честный индикатор источника данных, восемь содержательных скриншотов. Это задание — продолжение той же работы.
---
## Задача: четыре недостающих экрана
Веб покрывает пять экранов из девяти:
```
есть: Аккаунты, Обзор, Маршрутизация, Модели и провайдеры, Команда
нет: Аналитика, Состояние, Журнал событий, Настройки
```
Цель — паритет, чтобы десктоп можно было наконец перестать тащить второй веткой. Пока паритета нет, **десктоп не трогаем**: `router/ui/**` остаётся рабочим.
## P0-1. Аналитика — данные уже есть
`snapshot.metrics.telemetry` заполнена настоящими измерениями. С машины владельца:
```json
"global": {
"window_seconds": 86400, "total_calls": 19,
"successful_calls": 4, "failed_calls": 15,
"call_share": 1.0, "error_rate": 0.7895,
"latency_p50_ms": 1.4, "latency_p95_ms": 81631.1, "latency_max_ms": 81777.7,
"total_prompt_tokens": null, "total_completion_tokens": null, "total_tokens": null
}
```
Показать: вызовы, доля ошибок, латентность p50/p95/max, разрезы по провайдерам и ролям (они рядом в том же объекте).
**Токены равны `null` — это не ноль.** Провайдеры их не отдают. Показывать «Н/Д» с причиной, ни в коем случае не «0».
Обратите внимание на сами числа: 15 отказов из 19 и p95 в 81 секунду — это следствие сломанной авторизации `agy`, которую чинит A20. Экран должен честно показывать такую картину, а не сглаживать её.
## P0-2. Состояние — данные тоже есть
`snapshot.metrics.host`:
```json
"cpu_percent": 11.8, "memory_percent": 75.5, "memory_used_mb": 9077.9,
"disk_percent": 53.7, "disk_used_gb": 255.7, "net_speed_mbps": null
```
Плюс `snapshot.readiness`: `state`, `title_ru`, `summary_ru`, `roles_ready_count` / `total_roles`, `accounts_connected_count` / `total_accounts`, `providers_ready_count` / `total_providers`, `warnings`.
`net_speed_mbps` равен `null` — показывать «Н/Д», не ноль. Список `warnings` вывести целиком: это готовности ради него и считаются.
## P0-3. Журнал событий — источника в API нет, его нужно добавить
Проверено: **событий в снапшоте нет ни в каком виде.** В backend они есть — `EventLogService` в `unified_health.py` с методом `get_events(limit, category)`.
Требуется новый эндпоинт:
```
GET /api/events?limit=<n>&category=<необязательно>
-> {"events": [{"timestamp","category","message","details","level"}]}
```
Отдавать в обратном хронологическом порядке, с разумным пределом по умолчанию. **Секреты в события не попадают** — существующий тест на отсутствие секретов в ответах должен продолжать проходить, и на новый эндпоинт его нужно распространить.
На экране: лента с фильтром по уровню и категории и поиском по тексту.
## P0-4. Настройки — текущих значений в API тоже нет
Действие `save_settings` существует, а прочитать текущие значения через API нельзя: в снапшоте их нет.
Требуется:
```
GET /api/settings -> текущее содержимое hub_settings.json
```
**Без секретов.** Поле `web_api_token` наружу не отдавать никогда — ни целиком, ни частично. Отдавать признак «токен задан / не задан».
На экране: параметры, выбор темы, интервал обновления квот, пути. Сохранение — через существующее действие `save_settings`, второй реализации не заводить.
## P0-5. Контракт
Оба новых эндпоинта — в `docs/web-api/CONTRACT.md`, с примерами ответов. Версию контракта поднять.
Это не бюрократия: контракт версии 1.0 описал каталог `static/`, но не назвал, **кто его отдаёт**, — обе стороны выполнили написанное, и в браузере был 404. Пропуск был на авторе контракта, но цена — потерянный раунд.
---
## Ограничения
- Параллельно идёт **A20** (авторизация Antigravity). Его файлы: `oauth.py`, `profile_oauth.py`, `profile_manager.py`, `agy_subprocess.py`, `adapters/**`, `model_discovery*`, `credentials.py`. **Ничего из этого не трогать.**
- **Ваша зона на это задание расширена**: `router/web/**` целиком, включая `server.py` — там нужны два новых эндпоинта. A20 в `web/` не заходит, конфликта не будет.
- `router/ui/**` не трогать: десктоп остаётся рабочим до паритета.
- Правило честности без исключений: `null` — это «Н/Д» с причиной, а не ноль. Отличать «данных нет» от «данные грузятся» — механизм `is_loading` уже работает на «Аккаунтах», используйте его же.
- Секреты не отдавать ни в одном новом эндпоинте.
- Тег `v0.1.1` не создавать.
## Критерии приёмки
1. Ветка в `origin`, финальный коммит виден, `git status` чист.
2. Четыре экрана работают на данных: Аналитика, Состояние, Журнал событий, Настройки.
3. `GET /api/events` и `GET /api/settings` реализованы и описаны в контракте; версия контракта поднята.
4. Тест на отсутствие секретов распространён на новые эндпоинты и проходит; `web_api_token` наружу не отдаётся.
5. Ни одно `null` не показано как ноль; у каждого «Н/Д» есть причина.
6. Ни один файл зоны A20 не изменён; `router/ui/**` не тронут.
7. **Скриншоты всех четырёх экранов с настоящими данными.** Открыть и посмотреть перед тем, как прикладывать: в прошлый раз три из восьми оказались пустым чёрным кадром.
8. `ruff check .` чисто; релизный гейт не ухудшен.
9. Отчёт: `START_HEAD`, `FINAL_HEAD`, `origin/main`, `git status`, точный `X passed / Y skipped / Z failed`. На `main` сейчас 342 passed, 2 skipped.
10. **Если какой-то пункт не сделан — сказать об этом прямо**, с причиной.
## Главное
После этого задания веб покрывает всё, что умеет десктоп, и владелец сможет пользоваться Hub на своём сервере с Ubuntu, не запуская окно по SSH. Это же условие для того, чтобы перестать поддерживать два интерфейса — а интерфейс был узким местом каждого раунда этого проекта.
## Порядок сдачи
Передать точный `FINAL_COMMIT_SHA`. Сдано только после появления коммита в `origin`.