Веб покрывает пять экранов из девяти. Не хватает Аналитики, Состояния, Журнала событий и Настроек. Данные для первых двух уже в снапшоте: 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>
11 KiB
Задание 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не создавать.
Критерии приёмки
- Ветка в
origin, финальный коммит виден,git statusчист. - Четыре экрана работают на данных: Аналитика, Состояние, Журнал событий, Настройки.
GET /api/eventsиGET /api/settingsреализованы и описаны в контракте; версия контракта поднята.- Тест на отсутствие секретов распространён на новые эндпоинты и проходит;
web_api_tokenнаружу не отдаётся. - Ни одно
nullне показано как ноль; у каждого «Н/Д» есть причина. - Ни один файл зоны A20 не изменён;
router/ui/**не тронут. - Скриншоты всех четырёх экранов с настоящими данными. Открыть и посмотреть перед тем, как прикладывать: в прошлый раз три из восьми оказались пустым чёрным кадром.
ruff check .чисто; релизный гейт не ухудшен.- Отчёт:
START_HEAD,FINAL_HEAD,origin/main,git status, точныйX passed / Y skipped / Z failed. Наmainсейчас 342 passed, 2 skipped. - Если какой-то пункт не сделан — сказать об этом прямо, с причиной.
Главное
После этого задания веб покрывает всё, что умеет десктоп, и владелец сможет пользоваться Hub на своём сервере с Ubuntu, не запуская окно по SSH. Это же условие для того, чтобы перестать поддерживать два интерфейса — а интерфейс был узким местом каждого раунда этого проекта.
Порядок сдачи
Передать точный FINAL_COMMIT_SHA. Сдано только после появления коммита в origin.