docs(task): A10 — вся оставшаяся работа переходит к Antigravity

У Codex закончились лимиты. Границы зон сняты: router/ui/** и
tests/test_ui_*.py переходят к Antigravity вместе с ответственностью за
честность интерфейса.

Codex успел: выбор модели, кликабельная карточка целиком, окно настроек
роли, снятие правой панели, устранение срезов диаграммы, частично
причины у Н/Д. Не успел: компактные карточки аккаунтов и разбор
дублирующих разделов «Провайдеры»/«Квоты».

Отдельно: работа A9 заявлена выполненной, но в репозитории её нет ни в
одной ветке. Первое действие по A10 — отправить её в origin.

Зафиксирован обязательный контракт службы обнаружения моделей:
router/model_discovery.py + ModelDiscoveryService. Codex импортирует
именно этот путь, отчёт A9 называет другой; импорт защищён except
ImportError, поэтому расхождение сломало бы выбор моделей молча.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Hermes Team 2026-08-23 12:48:17 +07:00
parent 0165434bce
commit 1ce08c0361

View file

@ -0,0 +1,178 @@
# Задание A10 (Antigravity): всё оставшееся — backend и интерфейс
## Дата поступления
2026-08-23
## База
Проверочный HEAD на момент выдачи: **`0165434`**. Обязательно обновить локальную копию — см. следующий раздел.
## Ветка
`antigravity/all-remaining`
---
## Главное изменение: границы зон больше нет
У Codex закончились лимиты. **Вся работа переходит к вам, включая интерфейс.** Прежнее ограничение «зона Codex — `router/ui/**`, не трогать» **снимается**: теперь `src/antigravity_provider/router/ui/**` и `tests/test_ui_*.py` ваши.
Из этого следует требование, которого раньше не было: вы отвечаете и за то, чтобы интерфейс не врал. Правило честности распространяется на экран целиком — ни одного числа, идентификатора или названия модели без измерения.
---
## Перед началом: обновить локальную копию
Ваш отчёт по A9 назвал базой `6494c51`. С тех пор в `main` вошли: исправление плагина (`2d62d39`), дополнение задания A9 и **влитая частичная работа Codex по B8** (`0165434`).
```
cd <каталог репозитория>; git fetch origin --prune; git status
git checkout main; git reset --hard origin/main
```
Зафиксировать фактический `BASE_SHA` через `git rev-parse --short HEAD`.
---
## О статусе A9
Отчёт по A9 получен и прочитан. **Ни одной строки этой работы в репозитории нет.** Проверено: ветки `antigravity/quotas-models-migration` на `origin` не существует, ни в одной ветке нет ни `model_discovery_service.py`, ни `test_a9_migration_quotas_models.py`, ни миграции в `router_config.py`.
Работа осталась на вашей машине. Это уже второй такой случай в проекте: ветка Codex сутки лежала только локально, пока её не отправили в git.
**Первое действие по этому заданию — отправить готовую работу A9 в `origin`**, до всякой новой правки:
```
git push origin antigravity/quotas-models-migration
```
Ветка в `origin` ничего не ломает и никого не обязывает. Несохранённая работа теряется вместе с диском.
Пока код не в репозитории, приёмки быть не может: проверка идёт **исполнением, а не по отчёту**, и это не формальность. По A8 отчёт назвал сделанными четыре вещи, из которых ни одна у владельца не работала: профили claude/grok не дошли до существующего конфига, проверка просрочки была написана с опечаткой в имени ключа и не срабатывала ни разу, пересобранный установщик остался на вашей машине из-за `.gitignore`, флаг `/reinstall` не использовался в коде вообще.
Два числа из отчёта A9, которые нужно объяснить при сдаче:
- **«218 passed, 29 skipped»**. На `main` в venv Hermes набор даёт **290 passed, 2 skipped**. Двадцать девять пропусков означают, что UI-тесты в вашем окружении не выполнялись — то есть прогон не был полным. Требуется прогон в окружении **с** `customtkinter`, `pillow`, `psutil`.
- **«Release Gate 7/7 PASSED»**. Замер на чистом `main` даёт **FAILED**: проверка 4 (полный офлайн-прогон pytest) падает. Если у вас гейт зелёный — покажите, чем отличается окружение, либо приложите правку, которая его чинит.
---
## P0-1. Довести A9 до репозитория и проверяемого состояния
Всё содержание задания A9 остаётся в силе без изменений — миграция конфигурации, квоты остальных провайдеров, служба обнаружения моделей, отказ от выдуманных моделей, устойчивый порядок провайдеров, флаги установщика, доставка установщика. Читайте A9 как часть этого задания.
Добавляются два пункта из дополненного A9, которые ваш отчёт не покрывает, потому что писался до них:
- **P0-00 — Hub перехватывает каждый вызов Hermes как `orchestrator`.** Следствие устранено ревьюером; от вас — причина: не претендовать на вызов, если роль не определена достоверно; описать границу между учётными системами Hub и Hermes в контракте; подготовить варианты связывания профилей с ценой каждого.
- **P0-01 — обновление токена Codex и безопасное переключение аккаунта** по образцу Cockpit Tools: раздельная проверка `access_token` и `id_token`, обновление по `refresh_token`, остановка клиента **до** подмены учётных данных.
## P0-2. Обязательный контракт службы обнаружения моделей
Расхождение, которое сломает выбор моделей молча, и оно уже в коде.
Codex в `ui/model_catalog.py` импортирует:
```python
from antigravity_provider.router.model_discovery import ModelDiscoveryService
```
Ваш отчёт называет модуль `model_discovery_service.py`. Импорт обёрнут в `except (ImportError, AttributeError, TypeError): return None`, поэтому при несовпадении имени **ошибки не будет** — интерфейс просто всегда покажет «Список моделей ещё не получен».
Проверено на текущем `main`: каталог возвращает `models=()`, `is_stale=True`, причина «Список моделей ещё не получен». Выбор модели подключён и пуст.
**Контракт фиксируется:**
- модуль: `src/antigravity_provider/router/model_discovery.py`
- класс: `ModelDiscoveryService`
- получение экземпляра: classmethod `get()`
- чтение для интерфейса: поля `models`, `fetched_at`, `is_stale`, `unavailable_reason`
Если у вас модуль назван иначе — переименовать либо добавить `model_discovery.py` точкой входа.
**Тест обязателен, и он должен падать при несовпадении пути.** Защитный `except ImportError` уместен в бою, но он же прячет несобранную интеграцию: тест обязан отличать «службы нет» от «служба есть, но импорт не тот».
## P0-3. Компактные карточки аккаунтов
Codex до этого не дошёл — `accounts_view.py` не менялся.
Жалобы владельца: **«аккаунты должны выглядеть как у кокпит тулс, компактно»**, **«вид не тот, не надо делать раскрывающееся окно»**, **«в аккаунтах квота так и не отображается»**.
Требуется:
- плотный список фиксированной высоты: провайдер, идентичность, роль, состояние авторизации, квота — одной строкой, без раскрытия;
- квота видна сразу, числом и полосой, **с указанием пула и периода**: у Antigravity четыре пула, и «просто процент» вводит в заблуждение. Данные уже есть и настоящие — на живых аккаунтах владельца приходят измеренные значения от провайдера (`ag-w2` — 37.4% остатка недельного пула Claude/GPT, `source=provider_api`);
- шестнадцать аккаунтов читаются без прокрутки внутрь карточек;
- **дельта-отрисовку по стабильным ключам не терять** — изменение одного аккаунта не должно перерисовывать остальные. Это главное преимущество текущей версии, и в `COCKPIT_TOOLS_ARCHITECTURE_COMPARISON.md` описано, почему: при пересборке всех карточек на 50 аккаунтах выходит больше тысячи операций с виджетами в UI-потоке.
Документ в корне репозитория — архитектурное сравнение, а не макет. Берите из него принцип обновления и плотность, не буквальную вёрстку.
## P0-4. Разобрать дублирующие разделы
Codex до этого не дошёл — `quotas_view.py` не менялся.
Жалоба: **«провайдеры и квоты и лимиты вообще не понятно для чего нужны, там всё то же, что и в аккаунты»**.
Владелец прав: после появления квот в карточках аккаунтов отдельный раздел квот потерял смысл. Нужно решение, а не сохранение обоих на всякий случай.
- **«Квоты и лимиты»** — убрать из навигации либо оставить только то, чего нет в «Аккаунтах»: сводка по провайдеру целиком, история расхода, ближайшие сбросы.
- **«Провайдеры»** — оставить относящееся к провайдеру, а не к аккаунту: доступность runtime, обнаруженные модели, версия CLI, состояние авторизации в целом.
В отчёте перечислить перенесённое и удалённое. Пустой раздел не оставлять.
## P0-5. Причина у каждого Н
Жалоба: **«что означает н/д в маршрутизация запросов»**.
Codex закрыл это частично: `unavailable_reason` используется в `components.py` и на «Обзоре», но не везде.
Везде, где стоит `Н/Д`, должна быть доступна причина — подсказкой при наведении и текстом рядом, если место позволяет. Формулировки конкретные: «нет телеметрии: роль ещё не вызывалась», «провайдер не отдаёт лимиты», «аккаунт не подключён». Данные для этого есть.
## P1-6. Подключение Grok и Claude довести до конца
После миграции конфигурации (A9 P0-0.1) слоты для Grok и Claude появятся, и мастер перестанет упираться в «свободный слот не найден». Но проверить нужно весь путь целиком, а не только `find_free_slot`: подключение, назначение роли, появление в маршрутизации, тест профиля.
Жалоба владельца была **«при подключении грока ошибка»** — принимается только пройденный вживую сценарий со скриншотами.
---
## Что уже принято и не подлежит переделке
Проверено исполнением, работает — не откатывать при слиянии:
- **живые квоты Antigravity**: настоящий опрос `retrieveUserQuotaSummary` с обновлением OAuth-токена при 401, шесть аккаунтов владельца отдают разные измеренные числа;
- **правка плагина `2d62d39`**: при `router_error` вызов уходит вниз по цепочке, а не подменяет ответ модели; закрыта тестом `tests/test_plugin_passthrough.py`;
- **`_finish` мастера**: `destroy()` выполняется всегда, ранние `return` показывают ошибку в самом окне;
- **из работы Codex по B8**: выбор модели (`model_catalog.py`), кликабельная карточка целиком, окно настроек роли `_open_agent_settings_modal`, снятая правая панель, устранённые срезы `providers[:3]` и `agents[:5]`.
---
## Ограничения
- Границы зон нет — весь репозиторий ваш. Ответственность за честность интерфейса теперь тоже ваша.
- Не выдумывать числа, идентификаторы и названия моделей. Нет данных — `None` в модели, `Н/Д` с причиной на экране.
- Три темы сохранить, дельта-отрисовку не терять.
- Мастер не ломать: шесть потоков подключения, трёхэлементная распаковка `start_profile_oauth`, `destroy()` в `_finish` всегда.
- Сеть и подпроцессы — не в UI-потоке. Замерено: `agy models` в одном прогоне отвечает за 40 секунд, в следующем висит больше двух минут.
- Тег `v0.1.1` не создавать.
- **Ветку пушить сразу после первого коммита.**
## Критерии приёмки
1. Работа A9 находится в `origin` и проверяема исполнением.
2. Прогон в окружении **с** UI-зависимостями; число пропусков объяснено; обе команды и оба результата в отчёте.
3. Состояние релизного гейта названо до и после; расхождение с замером ревьюера объяснено.
4. Служба обнаружения моделей доступна по согласованному пути; тест падает при несовпадении импорта; выбор модели в интерфейсе показывает настоящий список.
5. Карточка аккаунта компактна, фиксированной высоты, без раскрытия; квота видна сразу с указанием пула и периода; шестнадцать аккаунтов без внутренней прокрутки; дельта-отрисовка сохранена и проверена тестом.
6. Принято решение по «Провайдерам» и «Квотам»; перенесённое и удалённое перечислено.
7. У каждого `Н/Д` доступна причина.
8. Вызов без достоверной роли уходит вниз, не тратя попыток роутера; правка `2d62d39` сохранена.
9. Граница между учётными системами Hub и Hermes описана в контракте; варианты связывания профилей — с ценой каждого, без односторонней реализации.
10. Grok и Claude подключаются вживую: подключение, роль, маршрутизация, тест профиля — со скриншотами.
11. `ruff check .` чисто.
12. Отчёт: `START_HEAD`, `FINAL_HEAD`, `origin/main`, `git status`, точный `X passed / Y skipped / Z failed`.
13. **Скриншоты живого сценария с настоящими данными.** Пустых состояний не присылать.
## Главное
Владелец сказал: «надо чтобы хаб уже заработал». Ядро работает, квоты Antigravity настоящие, интерфейс наполовину доведён. Осталось закрыть остаток и, что важнее всего, **довести сделанное до репозитория** — работа, лежащая на одной машине, для продукта не существует.
## Порядок сдачи
Передать точный `FINAL_COMMIT_SHA`.