hermes-hub/agents/inbox/2026-08-23-A16-antigravity-flash-web-client.md
Hermes Team 06b55b49f4 docs(tasks): A15 и A16 — переход на веб-интерфейс
Решение владельца: у него сервер с Ubuntu Server и Xubuntu, Hermes там
будет линуксовый. Веб-интерфейс на сервере строго лучше десктопа.
Десктоп остаётся рабочим до паритета, router/ui/** не трогается.

A15 (Pro): веб-API поверх готового снапшота — GET /api/snapshot и
POST /api/action на семнадцать существующих действий; безопасность
(127.0.0.1 по умолчанию, отказ стартовать на внешнем адресе без токена,
тест на отсутствие секретов в ответе); порт на Linux — восемь мест,
читающих LOCALAPPDATA в обход paths.py; честное сообщение о том, какие
потоки авторизации на headless-сервере не работают. Плюс долг из
прошлого раунда: снапшот не различает «данных нет» и «данные грузятся».

A16 (Flash): клиент без сборки на обычном JS. Не заблокирован сервером —
разрабатывает против docs/web-api/snapshot.example.json. Экраны по
ценности: Аккаунты с видимыми квотами, затем Обзор и Маршрутизация.

Обе стороны пишутся против docs/web-api/CONTRACT.md и до слияния друг
друга не видят — отсюда требование не менять контракт односторонне.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-23 19:03:48 +07:00

166 lines
14 KiB
Markdown
Raw Permalink 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.

# Задание 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`.