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

11 KiB
Raw Permalink Blame History

Задание 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 заполнена настоящими измерениями. С машины владельца:

"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:

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