A9 (Antigravity): миграция профилей в существующий router_profiles.yaml — корень жалобы «при подключении грока ошибка»; квоты для codex и opencode; служба обнаружения моделей с кэшем и фоновым обновлением; отказ от выдуманных списков моделей. Плюс четыре утверждения отчёта A8, не подтвердившиеся проверкой: профили claude/grok до пользователя не дошли, проверка is_expired была мертва, dist в .gitignore, флаг /reinstall не используется. B8 (Codex): выбор модели агента, компактные карточки аккаунтов без раскрытия, кликабельная карточка целиком, окно настроек роли, снятие правой панели, устранение жёстких срезов диаграммы, решение по дублирующим разделам, причина у каждого Н/Д. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
21 KiB
Задание A9 (Antigravity): миграция конфигурации, квоты остальных провайдеров, реальные модели
Дата поступления
2026-08-23
База
Проверочный HEAD на момент выдачи: b625b0f. Обязательно обновить локальную копию — см. следующий раздел.
Ветка
antigravity/quotas-models-migration
Перед началом: обновить локальную копию
Вы работаете на другой машине и пушите прямо в git. main ушёл далеко вперёд вашей базы: в него влиты и A8, и работа Codex (граф маршрутизации, живые квоты, дефекты живого прогона), и правки ревьюера.
cd <каталог репозитория>; git fetch origin --prune; git status
Если рабочее дерево чистое:
git checkout main; git reset --hard origin/main
Зафиксировать фактический BASE_SHA через git rev-parse --short HEAD и указать его в отчёте. Не считать b625b0f актуальным автоматически.
Что принято по A8
Проверено исполнением:
- Самолечение запуска работает.
launcher_bootstrapимпортируется,check_missing_dependencies()на чистой машине возвращает[], все пять функций на месте. find_free_slotбольше не выдумывает идентификаторы. Проверено по всем пяти провайдерам мастера: возвращается либо существующий профиль, либоNone. Это было главным дефектом A8 и он закрыт.- Экран переустановки в мастере есть и подключён к
SetupEngine.IsInstalled(HermesHubSetup.cs:714), с кнопкой «Переустановить» и показом версий. - Зеркальное развёртывание реализовано:
MirrorDirectoryRecursiveзаменил копирование. Проверено на живой машине владельца — при зеркалировании удалились девять устаревших файлов, включая четыре мёртвых модуля, которые мы удаляли из репозитория ещё в прошлых раундах. - Тесты
test_deployment_doctor.pyпроходят, ruff чисто.
Работа хорошая. Но в отчёте три утверждения, которые проверку не прошли, — читайте следующий раздел, это важнее похвалы.
P0-0. Три утверждения отчёта A8, не подтвердившиеся проверкой
Это не придирки к формулировкам. Каждое из трёх означает, что заявленная функция у владельца не работает.
1. Профили Claude и Grok до пользователя не дошли
Отчёт: «добавлены по 3 профиля… всего 22 профиля».
Факт на живой машине владельца:
профилей в конфиге: 16
antigravity 10
openai-codex 3
opencode-go 3
claude 0
grok 0
find_free_slot(grok) -> None
find_free_slot(claude) -> None
Профили добавлены во встроенные умолчания (router_config.py) и в пример (router_profiles.example.yaml). Но load_router_config() возвращает умолчания только если файла нет (router_config.py:324). У владельца файл есть — %LOCALAPPDATA%\hermes\config\router_profiles.yaml, и он побеждает. Новые встроенные профили в существующую установку не попадают никогда.
Прямое следствие — жалоба владельца «при подключении грока ошибка»: мастер получает None, показывает «свободный слот не найден», и Grok с Claude подключить невозможно в принципе.
Требуется миграция конфигурации. При загрузке существующего router_profiles.yaml профили и роли, появившиеся во встроенных умолчаниях позже, должны в него добавляться, а не игнорироваться. Условия:
- пользовательские правки не затираются: если профиль с таким
profile_idуже есть, он остаётся как есть; - добавление фиксируется в журнале и видно в самопроверке;
- у файла есть версия схемы, чтобы миграция была идемпотентной и не повторялась;
- перед первой записью делается резервная копия рядом с файлом.
Тест: взять конфиг из 16 профилей без claude/grok, выполнить загрузку, убедиться, что после неё find_free_slot("grok") возвращает существующий профиль, а десять профилей antigravity не изменились ни в одном поле.
2. Проверка просроченной авторизации была мертва
Отчёт: «добавлена предварительная проверка status.get("expired")».
Ключ в словаре называется is_expired (profile_manager.py:414), поэтому status.get("expired") всегда None. Проверка не срабатывала ни разу. Ваш собственный тест этого не поймал, потому что дефект прикрывала вторая проверка — в адаптере; когда при слиянии вызов адаптера из «Теста» ушёл, протухший аккаунт стал получать зелёную галочку.
Исправлено ревьюером при слиянии (b625b0f), трогать не нужно. Приводится как урок: тест проверял результат, достижимый двумя путями, и молчал о том, что один из них сломан.
3. Пересобранный установщик до владельца не доходит
Отчёт: «Перекомпилирован dist/HermesHubSetup.exe и обновлен dist/checksums.txt».
dist/ числится в .gitignore:9. Через этот репозиторий бинарник не передаётся физически — он остался на вашей машине. Владелец ставит из своей локальной сборки.
Требуется описать в отчёте, как собранный установщик должен попадать к владельцу: публикация в hermes-hub-releases, снятие dist/ с игнорирования, или сборка на стороне владельца одной командой. Выберите один способ и обоснуйте. Пока способа нет, утверждать «установщик обновлён» нельзя.
4. Флаги /reinstall и /repair разбираются, но ни на что не влияют
Отчёт: «Поддержан флаг командной строки /reinstall (и алиас /repair)».
HermesHubSetup.cs:968 — bool isRepair = false;, присваивается на строке 975 и больше не используется нигде. Это видно даже компилятору:
HermesHubSetup.cs(968,18): warning CS0219: Переменной "isRepair" присвоено значение,
но оно ни разу не использовалось
Запуск с /reinstall без /silent просто открывает обычный мастер. Либо реализовать тихую переустановку с кодами возврата, либо убрать флаг и не заявлять его.
P0-1. Квоты для OpenAI Codex и OpenCode Go
Жалобы владельца: «лимиты не подтягиваются, всё стоит Н/Д» и «у опенкода тоже нет лимитов».
Для Antigravity это уже решено — Codex реализовал живой опрос retrieveUserQuotaSummary у Google. Проверено на шести авторизованных аккаунтах владельца, данные настоящие и разные:
ag-w2 Claude/GPT — неделя remaining=37.4 used=62.6 source=provider_api
ag-w3 Claude/GPT — неделя remaining=90.1 used= 9.9 source=provider_api
ag-w1 Gemini — неделя remaining=99.7 used= 0.3 source=provider_api
Для двух других провайдеров осталась заглушка _generate_baseline_snapshot — все поля None:
opencode-go:opengo-1 source=provider_api
reason = "нет живого ответа от лимитов OpenCode Go"
Общий 5 часов / Недельный / Месячный: remaining=None
Требуется довести до реальных данных openai-codex и opencode-go по тому же образцу: опрос настоящего эндпоинта провайдера с использованием сохранённых учётных данных, обновление токена при 401, source="provider_api" только когда числа действительно измерены.
Правило честности прежнее и оно важнее полноты: если провайдер данных не отдаёт — None и внятная причина, а не правдоподобное число. Текущее поведение OpenCode Go в этом смысле правильное, оно просто неполное. Если у провайдера эндпоинта лимитов нет вовсе — это законный результат: зафиксировать в docs/UI_STATE_CONTRACT.md как недоступное, с причиной, чтобы интерфейс подписал честно.
Тест: на подготовленных учётных данных снапшот содержит измеренные значения; при ответе провайдера 401 — понятная причина и None; ни при каком сбое не появляется выдуманное число.
P0-2. Служба обнаружения моделей: кэш, фон, таймаут
Владелец просит выбор моделей для агентов (жалоба 2). Интерфейс делает Codex, но опора нужна ваша.
discover_models есть у всех адаптеров, и для Antigravity он работает: agy models вернул 14 настоящих моделей.
Но вызывать его из интерфейса напрямую нельзя. Замерено на живой машине: тот же agy models в одном прогоне отвечает за 40 секунд, а в следующем висит больше двух минут. Синхронный вызов заморозит окно намертво.
Требуется служба обнаружения моделей:
- результат кэшируется на диске рядом с конфигурацией, с временем получения;
- обновление — в фоне, с жёстким таймаутом и понятным поведением при его срабатывании;
- интерфейс получает список мгновенно из кэша плюс признак свежести;
- при пустом кэше отдаётся
None, а не выдуманный список — интерфейс покажет «список моделей ещё не получен»; - ошибка обнаружения не должна ронять карточку и не должна молча подставлять умолчания.
Тест: обнаружение с искусственной задержкой дольше таймаута не блокирует вызывающий поток и оставляет прежний кэш.
P0-3. Выдуманные списки моделей
auto_assigner.ensure_profile_definition (добавлен Codex, но это ваша зона) подставляет новым профилям жёстко зашитые списки:
"grok": (["grok-3", "grok-3-mini", "grok-2"], ...),
"antigravity": (["gemini-3.7-flash", "claude-sonnet-4-6", "gemini-3.5-flash"], ...),
Это тот же класс дефекта, с которым мы боролись в квотах, только про модели. И он уже даёт ложь: у живого провайдера gemini-3.7-flash не существует. Реальный список:
gemini-3.7-flash-high / -medium / -low
gemini-3.6-flash-high / -medium / -low
gemini-3.5-flash-high / -medium / -low
gemini-3.1-pro-high / -low
claude-sonnet-4-6, claude-opus-4-6-thinking, gpt-oss-120b-medium
При этом gemini-3.7-flash стоит в живом конфиге владельца как default_model роли orchestrator, а gemini-3.6-flash-high у роли fast — существует. То есть часть ролей настроена на несуществующую модель.
Требуется:
- Списки моделей для новых профилей брать из обнаружения (P0-2), а не из литерала. Пока обнаружение не выполнено — оставлять список пустым; профиль без списка моделей честнее профиля с выдуманным.
- Проверка конфигурации: модели, которых нет у провайдера, отмечаются в самопроверке и в контракте как недействительные, с указанием роли и профиля. Молча подставлять «похожую» модель нельзя — это решение владельца.
- Разобраться, почему вызов с
gemini-3.7-flashдо сих пор не приводил к явной ошибке. Если провайдер молча подставляет ближайшую — это надо знать и написать в отчёте, потому что тогда владелец получает не ту модель, которую выбрал.
Тест: профиль, созданный при отсутствии кэша моделей, не содержит ни одного идентификатора модели; проверка конфигурации сообщает о модели, отсутствующей у провайдера.
P1-4. Жёсткие срезы в данных для диаграммы
dashboard_view.py:601 — providers = list(snapshot.providers)[:3]. Провайдеров пять, два молча отбрасываются. Отрисовка — зона Codex, и срез уберут там, но решение о том, сколько провайдеров вообще имеет смысл показывать и в каком порядке, принимается на стороне данных: сейчас порядок ничем не задан, поэтому какой именно провайдер исчезнет — дело случая.
Задать явный, устойчивый порядок провайдеров в снапшоте (например, по числу авторизованных профилей, затем по алфавиту) и описать его в контракте.
P1-5. Остаток по YAML
Внутренние комментарии router_profiles.yaml по-прежнему теряются (7 → 2). Пункт висит с A7 и в A8 не закрыт. Либо полный round-trip, либо статус «частично» с перечнем теряемого — в контракте и в отчёте. С учётом P0-0.1 это стало важнее: миграция будет писать в этот файл, и терять при каждой записи комментарии владельца нельзя.
Ограничения
- Граница: зона Codex —
src/antigravity_provider/router/ui/**,tests/test_ui_*.py. Поhermes_hub_app.pyдействует прежнее исключение для backend-функций вродеdo_test_profile, но не для представления. - Никаких чисел и идентификаторов без измерения. Нет данных —
Noneи причина. - Тег
v0.1.1не создавать. - Резервная копия
router_profiles.yamlперед первой записью миграции — обязательна.
Критерии приёмки
- Ни один файл зоны Codex не изменён.
- На существующем конфиге без claude/grok после загрузки
find_free_slotдля обоих возвращает существующий профиль; десять профилейantigravityне изменены; проверено тестом. - Миграция идемпотентна и не теряет пользовательские правки и комментарии.
- Квоты
openai-codexиopencode-goприходят измеренными либоNoneс причиной; ни одного выдуманного числа; проверено тестом на обоих исходах. - Обнаружение моделей кэшируется, обновляется в фоне и не блокирует вызывающий поток при таймауте; проверено тестом с искусственной задержкой.
- Новые профили не содержат выдуманных моделей; проверка конфигурации сообщает о несуществующих моделях в ролях владельца.
/reinstallлибо работает с кодами возврата, либо удалён; предупреждение CS0219 при сборке отсутствует.- В отчёте назван конкретный способ доставки установщика владельцу.
- Прогон в обоих окружениях; обе команды и оба результата в отчёте.
ruff check .чисто. Про релизный гейт: он красный наmainуже сейчас (проверка 4 падает не по вашей вине). Указать в отчёте его состояние до и после ваших правок; ухудшать нельзя.- Отчёт:
START_HEAD,FINAL_HEAD,origin/main,git status, точныйX passed / Y skipped / Z failed.
Главное
Владелец сказал: «надо чтобы хаб уже заработал». Ядро работает — живой каскад отказоустойчивости в журнале это доказал, и квоты Antigravity теперь настоящие. Осталось, чтобы не работающее выглядело как не работающее, а не как Н/Д без объяснений, и чтобы провайдер, который он хочет подключить, подключался.
Порядок сдачи
Передать точный FINAL_COMMIT_SHA.