diff --git a/agents/inbox/2026-08-23-A15-antigravity-pro-web-api.md b/agents/inbox/2026-08-23-A15-antigravity-pro-web-api.md new file mode 100644 index 0000000..7b2bc66 --- /dev/null +++ b/agents/inbox/2026-08-23-A15-antigravity-pro-web-api.md @@ -0,0 +1,160 @@ +# Задание A15 (Antigravity Pro, этот ПК): веб-API и порт на Linux + +## Дата поступления +2026-08-23 + +## База +Проверочный HEAD на момент выдачи: **`7ae4a28`**. + +## Ветка +`antigravity/web-api` + +--- + +## Порядок работы с git + +``` +cd <каталог репозитория>; git fetch origin --prune; git status +git checkout main; git pull --ff-only origin main +git checkout -b antigravity/web-api +``` + +После первого коммита — `git push -u origin antigravity/web-api`. В конце — push и проверка `git log --oneline -1 origin/antigravity/web-api`, `git status` должен быть чистым. + +--- + +## Что принято по A13 + +Проверено исполнением, работа сильная. + +**Устойчивость набора закрыта по-настоящему.** Общий корень Tk на сессию вместо шести отдельных. Прогнал десять полных прогонов подряд, как требовало задание: **316 passed, ноль ошибок во всех десяти**. До правки каждый прогон давал до семи ошибок, кочующих между файлами, и настоящую поломку приходилось искать перепроверкой в изоляции. Это чинилось не для галочки — оно мешало работе каждый раунд. + +**Раздел контракта написан по существу**: все четыре вопроса раскрыты, явно сказано, что роль Hermes не передаёт. Варианты связывания профилей поданы таблицей с оценкой 3–4 / 8–12 / 4–5 дней, ни один не реализован без решения владельца — ровно как требовалось. + +Одно исправлено при слиянии: в документе оказалось **11 символов BEL (0x07) на месте буквы «a»** — последовательности вида `\agy-05` были разобраны как escape. Пострадали 19 идентификаторов: `antigravity` → `ntigravity`, `ag-w2` → `g-w2`. Контракт читают оба исполнителя, битые имена в нём недопустимы. Проверяйте документы после записи так же, как код. + +--- + +## Решение владельца: переходим на веб-интерфейс + +У владельца сервер с Ubuntu Server и Xubuntu, Hermes там будет линуксовый. Веб-интерфейс на сервере строго лучше десктопа: X-forwarding CustomTkinter по сети — мучение, а интерфейс и так был единственным узким местом всех прошлых раундов. + +Десктоп **остаётся рабочим** до достижения паритета. Ничего из `router/ui/**` не удаляется. + +**Читать перед началом: `docs/web-api/CONTRACT.md`.** Это единственный источник истины для вас и для A16, который параллельно делает клиентскую часть. Отклонение от контракта — дефект, даже если ваш код работает: вторая сторона пишется против документа и вашего кода не видит. + +--- + +## P0-1. Веб-API поверх готового снапшота + +Архитектура уже сделала бо́льшую часть работы, проверено исполнением: `HubSnapshot` — dataclass, сериализуется в JSON **одним вызовом** `dataclasses.asdict`, объём около 100 КБ, вся поверхность действий сведена в `_handle_action` и состоит из семнадцати имён. + +Новый пакет `src/antigravity_provider/router/web/` — структура задана контрактом. + +Эндпоинты — по контракту, раздел 4: + +- `GET /api/snapshot` — весь снапшот, `datetime` в ISO-8601; +- `POST /api/action` — тело `{"action": "...", "data": {...}}`, ответ `{"ok": bool, "message": str, "data": {...}}`. **Отказ действия — это `200` с `ok: false`**, а не `4xx`; `4xx` остаётся неизвестному действию и непройденной авторизации; +- `GET /api/health` — без авторизации. + +Стек: FastAPI и uvicorn. Обе зависимости **уже объявлены** в `pyproject.toml` в группе `legacy` — она осталась от удалённого `gui_server.py` и не используется ничем. Переименовать в `web` и подключить. + +Действия исполнять **через существующий путь**, а не дублировать логику. `_handle_action` сейчас завязан на виджеты; вынести из него исполнительную часть так, чтобы её вызывали и десктоп, и веб. Второй реализации семнадцати действий в проекте быть не должно. + +**Долгие операции не должны держать запрос.** `refresh_all` опрашивает провайдеров по сети, `test` дёргает подпроцесс, `agy models` в замерах то отвечает за 40 секунд, то висит больше двух минут. Такие действия возвращают `ok: true` с сообщением «запущено», а результат приходит следующим снапшотом. + +## P0-2. Безопасность — часть задания, не довесок + +Сервер будет доступен по сети. Контракт, раздел 3: + +1. По умолчанию слушать **только `127.0.0.1`**. Другой адрес — явным параметром, и тогда **токен обязателен**. +2. Токен в заголовке `X-Hub-Token`, сравнение через `secrets.compare_digest`. +3. При запуске на не-локальном адресе без токена сервер **отказывается стартовать** с внятным сообщением. Не поднимается открытым, не пишет предупреждение в лог и не продолжает. +4. **Тест, падающий при появлении секрета в ответе.** Проверено на живых данных: сейчас в сериализованном снапшоте нет ни `access_token`, ни `refresh_token`, ни `api_key`, ни JWT, ни строк `ya29.`/`sk-`. Это состояние надо удержать механически, а не обещанием. +5. Никаких секретов в URL и параметрах запроса. + +## P0-3. Порт на Linux + +Проверено по коду — ядро почти готово: + +- `paths.py` **уже** кроссплатформенный: при отсутствии `LOCALAPPDATA` уходит в `~/.hermes`; +- поиск CLI **уже** готов: `shutil.which("agy") or shutil.which("agy.exe")`. + +Чинить надо восемь мест, которые дублируют логику `LOCALAPPDATA` **в обход** `paths.py` и на Linux дадут неверные пути: + +``` +router/hermes_hub_app.py:37 +router/router_config.py:319, 462 +router/launcher_bootstrap.py:32 +router/model_discovery_service.py:34 +router/ui/assets.py:48 +agy_subprocess.py:50 +``` + +Все — через `paths.get_hermes_home()`. Единый источник истины уже есть, им просто не пользуются. + +**Тест:** при заданном `HERMES_HOME` ни один модуль не обращается к `LOCALAPPDATA` напрямую; пути одинаковы во всех модулях. + +**Проверка на настоящем Linux обязательна** — заявления «должно работать» не принимаются. Если под рукой нет машины, скажите об этом прямо в отчёте, и проверку сделает владелец. + +## P0-4. Авторизация на сервере без экрана — сказать правду + +Разобрано по коду, выяснять заново не нужно: + +| Провайдер | Поток | На headless-сервере | +|---|---|---| +| OpenAI Codex | device-code (12 упоминаний) | **работает** | +| Grok | device-code (13) | **работает** | +| Antigravity | redirect на localhost | **не работает** | +| Claude | redirect на localhost | **не работает** | + +Для Antigravity и Claude редирект придёт на машину пользователя, а не сервера, — поток обрывается. + +**Требуется:** серверная часть сообщает клиенту, какие потоки на этой машине доступны, а какие нет, **и почему**. Поле в ответе `/api/health` или отдельный эндпоинт — на ваше усмотрение, но зафиксируйте в контракте и предупредите A16. + +Обходной путь предложить, а не изобретать молча: проброс порта по SSH либо авторизация на десктопе с переносом каталога профиля. Что из этого работает — проверить и написать. + +**Неработающую кнопку показывать нельзя.** Это прямое продолжение правила честности: интерфейс, предлагающий подключить Antigravity на сервере, где это невозможно, — та же ложь, что выдуманная квота. + +## P1-5. Долг из прошлого раунда: квота не видна до фонового опроса + +Найдено при проверке A14 и относится к вашей зоне. + +`state_store` наполняет снапшот через `quota_service.get_snapshot`, который читает кэш и **при промахе отдаёт пустую заглушку из двух корзин, живой опрос не запуская**. Замер: + +``` +снапшот сразу после старта : 2 корзины, 0 измеренных +после прогрева кэша : 4 корзины, все измерены, ag-w2 = 37.4% +``` + +В работающем приложении квота появляется только после фонового обновления, а до него карточки стоят пустыми **без объяснения**. Это и есть жалоба владельца «в аккаунтах квота так и не отображается». + +Требуется различать в модели данных **«данных нет»** и **«данные ещё грузятся»**. Сейчас оба состояния выглядят одинаково, и ни интерфейс десктопа, ни будущий веб отличить их не могут. Поле состояния — в снапшот и в контракт. + +--- + +## Ограничения + +- Параллельно идёт **A16** (клиентская часть). Ваши файлы: `router/web/**` кроме `static/`, `state_store.py`, `paths.py`, `router_config.py`, `agy_subprocess.py`, `launcher_bootstrap.py`, `model_discovery_service.py`, `pyproject.toml`, `docs/web-api/CONTRACT.md`. **Не ваши:** `router/web/static/**`, `router/ui/**`. +- Контракт менять можно, но **только правкой документа с явным упоминанием в отчёте** — вторая сторона пишется против него. +- Десктоп не ломать: он остаётся рабочим до паритета. +- Никаких чисел и идентификаторов без измерения. +- Тег `v0.1.1` не создавать. + +## Критерии приёмки + +1. Ветка в `origin`, финальный коммит виден, `git status` чист. +2. `GET /api/snapshot` отдаёт снапшот, совпадающий по структуре с `docs/web-api/snapshot.example.json`; проверено тестом сравнения ключей. +3. `POST /api/action` принимает все семнадцать действий; отказ возвращается как `200` с `ok: false`; неизвестное действие — `4xx`. +4. Действия исполняются через общий путь; второй реализации семнадцати действий в проекте нет. +5. Долгие операции не держат запрос; результат приходит следующим снапшотом. +6. Сервер по умолчанию слушает `127.0.0.1`; на внешнем адресе без токена **отказывается стартовать**; проверено тестом. +7. Есть тест, падающий при появлении в ответе `access_token`, `refresh_token`, `api_key`, JWT или строк `ya29.` / `sk-`. +8. Ни один модуль не читает `LOCALAPPDATA` в обход `paths.py`; проверено тестом с заданным `HERMES_HOME`. +9. Доступность потоков авторизации сообщается клиенту с причиной; зафиксировано в контракте. +10. В снапшоте различаются «данных нет» и «данные грузятся»; поле описано в контракте. +11. Прогон в обоих окружениях; `ruff check .` чисто; релизный гейт остаётся 7/7. +12. Отчёт: `START_HEAD`, `FINAL_HEAD`, `origin/main`, `git status`, точный `X passed / Y skipped / Z failed`, и отдельно — проверялось ли на настоящем Linux. + +## Порядок сдачи +Передать точный `FINAL_COMMIT_SHA`. Сдано только после появления коммита в `origin`. diff --git a/agents/inbox/2026-08-23-A16-antigravity-flash-web-client.md b/agents/inbox/2026-08-23-A16-antigravity-flash-web-client.md new file mode 100644 index 0000000..1b5ab6a --- /dev/null +++ b/agents/inbox/2026-08-23-A16-antigravity-flash-web-client.md @@ -0,0 +1,166 @@ +# Задание 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`.