# Задание A16 (Antigravity Flash, другой ПК): веб-клиент ## Дата поступления 2026-08-23 ## База Проверочный HEAD на момент выдачи: **`7ae4a28`**. ## Ветка `antigravity/web-client` --- ## Порядок работы с git ``` cd <каталог репозитория>; git fetch origin --prune; git status git checkout main; git pull --ff-only origin main git checkout -b antigravity/web-client ``` Сначала коммит, потом `git push -u origin antigravity/web-client`. В конце — push и проверка: ``` git status <- дерево чистое git log --oneline -1 origin/antigravity/web-client <- ваш финальный коммит ``` --- ## Что принято по A14 Приложены восемь скриншотов, пять содержательных. Скриншот «Аккаунты» подтверждает компактные карточки фиксированной высоты без раскрытия. В `unified_health` добавлено свойство `auth_label_ru` — правка вне заявленной зоны, но безобидная. Слияние без конфликтов, 316 passed. **Что не сошлось, и это важно для нового задания.** `accounts_view.py` и `quotas_view.py` не изменялись — второе задание подряд. Пункты при этом оказались закрыты чужой работой: компактные карточки пришли из `components.py`, а пункт навигации «Квоты и лимиты» убрал Codex ещё в `40b3466`. То есть в отчёте они числятся сделанными вами, а сделаны не вами. **Три скриншота из восьми — пустой чёрный кадр**: шаг мастера Grok, окно роли, детали аккаунта. Два весят ровно по 1148 байт. Живое подключение Grok ими не подтверждено, а именно оно и требовалось. Скриншот, который ничего не показывает, хуже отсутствующего: он создаёт впечатление проверки, которой не было. Проверяйте, что сняли, прежде чем прикладывать. --- ## Решение владельца: переходим на веб-интерфейс У владельца сервер с Ubuntu Server и Xubuntu. Веб-интерфейс там строго лучше десктопа. Десктоп остаётся рабочим до паритета — `router/ui/**` не трогаем вообще. **Ваша часть — клиент.** Параллельно A15 делает серверную. **Читать перед началом: `docs/web-api/CONTRACT.md`.** Это единственный источник истины. Вы пишете против документа, а не против чужого кода — сервера вы не увидите до слияния. Любое отклонение от контракта — дефект, даже если у вас всё работает. ## Вы не заблокированы готовностью сервера Рядом лежит **`docs/web-api/snapshot.example.json`** — настоящий снапшот с машины владельца: 63 профиля, 187 КБ, почты замаскированы, секретов нет. Разрабатывайте против этого файла. Пока сервера нет, `fetch('/api/snapshot')` подменяется загрузкой фикстуры — один флаг в начале `app.js`. Когда сервер появится, переключение будет стоить одну строку. --- ## P0-1. Клиент без сборки ``` src/antigravity_provider/router/web/static/ index.html app.js style.css ``` **Обычный JavaScript и `fetch`. Без npm, без сборки, без фреймворка.** Это не вкусовщина. Проект ведут агенты на трёх машинах; любой шаг сборки означает согласование версий Node и дрейф lock-файлов. Страница, которую можно открыть файлом и отладить в браузере без инструментов, снимает целый класс проблем. React в проекте отклонён и раньше, по той же причине. Опрос `/api/snapshot` раз в 5 секунд и после каждого действия. **Обязательно:** поле `seq` в снапшоте монотонно растёт. **Ответ с `seq` меньше уже применённого игнорировать** — иначе поздний ответ затрёт свежий. Этот дефект в десктопе уже ловили, повторять не нужно. ## P0-2. Экраны — порядок по ценности Не пытайтесь сразу повторить все девять разделов десктопа. Порядок такой, и он не случаен: **1. Аккаунты** — самый нужный экран и самая старая жалоба владельца. - плотный список фиксированной высоты: провайдер, идентичность, роль, состояние авторизации, квота — одной строкой, **без раскрытия**; - **квота видна сразу**, числом и полосой, **с указанием пула и периода**. У Antigravity четыре пула, «просто процент» вводит в заблуждение — нужно `Claude/GPT — неделя`, а не `62%`. В фикстуре эти данные есть, посмотрите структуру `quotas`; - шестнадцать аккаунтов читаются без прокрутки внутрь карточек. **2. Обзор** — готовность, роли, схема маршрутизации, последние события. **3. Маршрутизация** — цепочка основной → резервы, активный узел, причина последнего переключения. Дальше — по остатку времени. Пустой раздел не заводить: либо содержание, либо нет пункта меню. ## P0-3. Действия Все через `POST /api/action` с телом `{"action": "...", "data": {...}}`. Семнадцать имён перечислены в контракте. Ответ `{"ok": bool, "message": str}`. **`ok: false` приходит с кодом `200`** — это нормальный отказ действия, а не ошибка сети. Обрабатывать как результат, а не как сбой. Требования к обратной связи, купленные прошлыми раундами: - **результат действия виден в месте действия**, а не в строке состояния внизу, где его не замечают; - **при ошибке модальное окно не закрывается** — человек должен видеть причину; - долгие операции (`refresh_all`, `test`) возвращают «запущено» сразу, результат приходит следующим снапшотом. Показывать это как выполняющееся, а не как зависшее. ## P0-4. Честность на экране Правило проекта действует в вебе без исключений. - **Ни одного числа, идентификатора или названия модели без измерения.** - Нет данных — «Н/Д» **и причина рядом**, доступная пользователю. Причина уже приходит в снапшоте полем `unavailable_reason`. Владелец прямо спрашивал: «что означает н/д в маршрутизации» — прочерк без объяснения его не устраивает. - **Отличать «данных нет» от «данные ещё грузятся».** Найдено при проверке прошлого раунда: квота появляется только после фонового опроса, а до него карточки стоят пустыми и это выглядит как поломка. A15 добавит в снапшот поле состояния — до его появления показывайте загрузку по отсутствию значения при `is_stale`. ## P0-5. Авторизация на сервере без экрана — сказать правду Разобрано по коду: | Провайдер | Поток | На headless-сервере | |---|---|---| | OpenAI Codex | device-code | **работает** — код вводится с любой машины | | Grok | device-code | **работает** | | Antigravity | redirect на localhost | **не работает** | | Claude | redirect на localhost | **не работает** | У двух последних редирект придёт на машину пользователя, а не сервера. A15 сообщит клиенту доступность потоков с причиной. **Недоступный поток показывать отключённым с объяснением и обходным путём, а не рабочей кнопкой, которая молча ничего не сделает.** Это то же правило честности: кнопка, обещающая невозможное, — та же ложь, что выдуманная квота. Для device-code повторить то, что уже отработано в десктопе: пронумерованная последовательность, код крупно и моноширинным, кнопки копирования ссылки и кода. Владелец жаловался «появляется код, куда его вставлять, не понятно» — не повторяйте. ## P0-6. Оформление Тёмная тема как основная — по образцу существующего десктопа, чтобы переход не выглядел другой программой. Семантика цвета прежняя: зелёный работает, янтарный предупреждение, красный ошибка, серый нет данных. **Золото остаётся брендом, а не статусом.** Ширина от 1280 и выше. Мобильную вёрстку не делать. --- ## Ограничения - Ваши файлы: `src/antigravity_provider/router/web/static/**`, `tests/test_web_client_*.py`. **Не ваши:** `router/web/` кроме `static/`, `router/ui/**`, backend целиком. - Контракт **не менять в одностороннем порядке**. Нужна правка — скажите, она согласуется с A15. - Секретов на клиенте не хранить и в URL не передавать. - Тег `v0.1.1` не создавать. ## Критерии приёмки 1. Ветка в `origin`, финальный коммит виден, `git status` чист. 2. Клиент работает против `snapshot.example.json` без сервера; переключение на живой API — не более одной строки. 3. Никакой сборки: страница открывается и отлаживается без npm и без инструментов. 4. Ответ с устаревшим `seq` игнорируется; проверено тестом. 5. «Аккаунты»: плотный список без раскрытия, квота видна сразу с пулом и периодом, шестнадцать аккаунтов без внутренней прокрутки. 6. «Обзор» и «Маршрутизация» на реальных данных фикстуры. 7. У каждого «Н/Д» доступна причина; загрузка отличается от отсутствия данных. 8. `ok: false` обрабатывается как результат, а не как сбой; ошибка видна в месте действия; окно при ошибке не закрывается. 9. Недоступные на сервере потоки авторизации показаны отключёнными с причиной и обходным путём. 10. **Скриншоты каждого экрана — открытые и проверенные.** Пустой кадр не прикладывать. 11. `ruff check .` чисто; релизный гейт остаётся 7/7. 12. Отчёт: `START_HEAD`, `FINAL_HEAD`, `origin/main`, `git status`, точный `X passed / Y skipped / Z failed`. ## Главное Веб-интерфейс — единственный способ пользоваться Hub на сервере владельца. Первый экран, который должен заработать по-настоящему, — «Аккаунты» с видимыми квотами: это самая старая незакрытая жалоба, и она тянется с самого начала проекта. ## Порядок сдачи Передать точный `FINAL_COMMIT_SHA`. Сдано только после появления коммита в `origin`.