Написано под схему владельца: Flash реализует, Pro проводит аудит.
Пункт P0-4 — чек-лист для второго прохода, составлен из дефектов,
которые уже проходили мимо первого.
P0-1: mark_auth_required ставит состояние без срока истечения, а
маршрутизация пропускает нездоровый профиль — успеха не случится,
отметка не снимется никогда. Подтверждено: после починки авторизации в
A22 все шесть профилей Antigravity остались помечены, ревьюер снимал
отметки вручную.
P0-2: do_set_model пропускает проверку целиком при пустом кэше моделей.
Проверено — выдуманная модель записалась в конфигурацию владельца.
Отсутствие данных трактуется как разрешение.
P0-3: ручного обновления списка моделей нет с A18.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Владелец возразил на моё утверждение, что gemini-3.7-flash не существует.
Он прав, утверждение было неверным, и я повторил его в четырёх заданиях.
gemini-3.7-flash — настоящее семейство, уровень усилия у неё отдельный
параметр. В интерфейсе Antigravity это видно прямо: пункт «Gemini 3.7
Flash» с вложенным выбором Low/Medium/High. В коде это отражено:
_display_to_cli разбирает «Gemini 3.7 Flash (High)» в пару
("gemini-3.7-flash", "high"). Меня ввела в заблуждение первая колонка
вывода agy models со склеенными идентификаторами.
Настоящий дефект был в коде: _model_supported_efforts вызывала
discover_models() БЕЗ профиля, то есть в глобальном окружении без входа.
Карта поддерживаемых усилий оставалась пустой, подстановка уровня по
умолчанию не срабатывала, и agy отвергал вызов с «requires --effort» —
при совершенно настоящей модели.
profile_id проведён через agy_generate в _model_supported_efforts.
Проверено исполнением: gemini-3.7-flash без указания усилия отрабатывает
и возвращает ответ.
Моки в test_antigravity_concurrency приведены к терпимости по kwargs.
Добавлена поправка agents/inbox/2026-08-24-CORRECTION-gemini-model-names.md:
ложное утверждение попало в A9, A11, A18 и B8, и без опровержения кто-то
чинил бы несуществующую проблему или сломал рабочую конфигурацию.
Тесты: 359 passed, ruff чисто.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Четыре недостающих экрана добавлены: Аналитика, Состояние, Журнал
событий, Настройки. Веб покрывает все девять разделов десктопа.
Два новых эндпоинта реализованы: GET /api/events и GET /api/settings.
Секреты не утекают — проверено исполнением, наружу отдаётся только
признак web_api_token_configured (bool), сам токен отсутствует.
Слияние без конфликтов. Тесты: 359 passed, ruff чисто.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Проверено исполнением, все три требуемых доказательства получены.
1. agy models через профиль Hub: обнаружено 14 моделей, включая
gemini-3.1-pro-high и gemini-3.1-pro-low.
2. Реальный вызов adapter.invoke(ag-w1): модель ответила «ОК».
3. route_request(coder-primary): переключение с отказавшего
codex-worker-1 на ag-w1, ответ получен, router_error отсутствует.
Подход из A20 (синтез oauth_creds.json) действительно был тупиковым;
родной вход agy с подменой HOME решает задачу. Реализация аккуратная:
видимая консоль на Windows через CREATE_NEW_CONSOLE, терминалы на Linux,
HOMEDRIVE выставляется корректно.
P0-4 подтверждён: пересохранение профиля не изменяет ни одного файла в
глобальном ~/.gemini.
Исправлено при слиянии:
1. Инструкция в веб-клиенте вела на НЕСУЩЕСТВУЮЩИЙ файл
launcher/main.py. Заменена на реальный вызов launch_native_agy_login
с указанием профиля.
2. test_headless_server_auth_matrix проверял дословную формулировку и
падал при её правке, хотя поведение оставалось верным. Приведён к
проверке сути, добавлена защита от возврата несуществующего пути.
Конфигурация владельца: обоим кодерам поставлена gemini-3.1-pro-high по
его прямой просьбе — теперь это возможно.
Тесты: 355 passed, ruff чисто.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A20 закрыто как неверно поставленное. Синтез oauth_creds.json из
OAuth-потока Hub не работает в принципе: проверены и закрыты пять
гипотез (полнота полей, срок токенов, тот же OAuth-клиент, тот же
scope, согласованность активного аккаунта), а решающий опыт показал,
что agy отказывает даже РАБОЧИМ глобальным учётным данным владельца,
положенным в подменённый HOME. Ошибка в гипотезе автора задания.
Полезное следствие разведки: agy уважает подмену HOME — в каталогах
профилей лежат созданные им же файлы. Изоляция работает, не работал
только синтез.
A22 строит вход через родной механизм agy в окружении профиля. Первым
пунктом — разведка: есть ли неинтерактивный вход, что agy выводит,
как определить завершение. Установлено, что без аргументов это TUI:
в пайп ничего не пишет и виснет.
Приёмка только с тремя доказательствами исполнением: непустой вывод
agy models, успешный реальный вызов, route_request без ухода в резерв.
Плюс проверка побочной находки: в глобальном google_accounts.json
активен один аккаунт, а токен принадлежит другому — Hub мог писать
мимо каталога профиля и сломать владельцу обычный agy.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- Add openid to OAuth scopes for id_token issuance
- Preserve id_token, scope, and token_type on token exchange and refresh
- Atomically write and sync .gemini/oauth_creds.json in profile directories
- Auto-resolve active profile environment in discover_models
- Cache discovered models in models_cache.json with graceful timeout handling
- Add unit test coverage for full OAuth lifecycle and model discovery caching
Веб покрывает пять экранов из девяти. Не хватает Аналитики, Состояния,
Журнала событий и Настроек.
Данные для первых двух уже в снапшоте: metrics.telemetry (19 вызовов,
15 отказов, латентность p50/p95/max) и metrics.host плюс readiness.
Для двух других источника в API нет вовсе — нужны GET /api/events
(EventLogService в backend есть) и GET /api/settings без секретов.
Зона Flash на это задание расширена на router/web/** целиком, включая
server.py: A20 в web/ не заходит, конфликта не будет.
Обнаружение моделей в задание НЕ включено, хотя A18 его пропустил:
причина оказалась глубже и лежит в авторизации agy, это чинит A20.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Маршрутизация через Antigravity не работает ни для одного из шести
аккаунтов при полностью валидных токенах: квоты по ним приходят
настоящими через прямой HTTPS, а путь через CLI падает с
AuthExpiredError.
Причина найдена и проверена: agy читает <HOME>/.gemini/oauth_creds.json
с шестью полями, включая id_token и scope. Hub пишет свой auth.json в
другом месте и другой структурой, а id_token и scope теряет в двух
местах — oauth.py:88-92 и profile_oauth.py:241-249. Ни один профиль их
не хранит.
Проверено и не сработало: подмена HOME на каталог профиля, сборка
oauth_creds.json без id_token. Значит id_token обязателен.
Владелец решил остаться на OAuth, прямой API отклонён.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A17 принято, проверено на живых профилях владельца: «Работает» больше не
ставится непроверенному профилю. Из 22 профилей теперь 1 «Работает»
(есть записанный успех), 7 «Не проверялся», 11 «Аккаунт не добавлен»,
3 «Отключён». Grok и opengo-1, на которые жаловался владелец, показаны
честно. Добавлено поле last_success_at.
A18 принято частично: действие set_model существует и валидирует модель
по списку провайдера. Но обнаружение моделей (P0-2) не сделано вовсе —
model_discovery_service и зонд не менялись, ручного обновления нет,
кэша на диске нет. Из-за этого set_model отклоняет ЛЮБУЮ модель, включая
настоящую: список провайдера пуст, и валидация не с чем сравнивать.
Правка при слиянии: discover_models запускался в ГЛОБАЛЬНОМ окружении,
где вход agy не выполнен, — при шести рабочих OAuth-профилях. Теперь
принимает profile_id и подменяет HOME/USERPROFILE на каталог профиля,
как это делает adapter.invoke. Таймаут поднят с 10 до 60 секунд.
Это не вылечило симптом, и причина оказалась глубже — см. отчёт.
Тесты: 336 passed, ruff чисто.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A17 (Pro): «Работает» — ветка else в определении здоровья, она означает
«мы не знаем о проблемах», а подана как утверждение. Отказы попадают в
статус только после боевого сбоя, поэтому непроверенный профиль
автоматически зелёный. Плюс «Проверить подключение» не вызывает модель
вовсе — Grok её проходит и не работает.
A18 (Flash): действия смены модели не существует ни среди семнадцати, ни
в клиенте; десктоп это умеет, но логика заперта в методе интерфейса.
И выбирать не из чего: кэш моделей пуст по всем провайдерам, потому что
agy models нестабильна — в одном прогоне 40 секунд, в следующем висит
больше двух минут.
A19: два установщика. Windows — ярлык, открывающий веб окном приложения
через --app без адресной строки (проверено на машине владельца: Edge и
Chrome есть, окно открывается). Linux — скрипт установки, .desktop и
удаление с сохранением данных, плюс честная подсказка про проброс порта
при пустом DISPLAY.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Владелец: «стоит опенкод аккаунт, который не подключен, у него кончились
лимиты и аккаунт не работает». Лимиты ни при чём.
Мастер подключения сохраняет ключ через ProfileAuthManager, а
_resolve_api_key смотрел только в auth_config из YAML и в переменные
окружения. Хранилище профилей он не читал вовсе — в отличие от grok,
claude и codex, где такая проверка есть.
Следствие: любой аккаунт OpenCode Go, подключённый через интерфейс, был
нерабочим. Маршрутизация падала с «No API key found for OpenCode Go
profile», и эта строка уже попадалась в следе отказов оркестратора.
Проверено на живом профиле владельца: до правки health_check=False и
тест профиля возвращал «локальный runtime недоступен», хотя api_key
лежал в auth.json. После — health_check=True, тест проходит.
Закрыто тестами, включая проверку, что пустое хранилище по-прежнему
даёт отказ, а не ложноположительный результат.
Тесты: 331 passed, ruff чисто.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Владелец запустил веб, увидел «Н/Д» у всех аккаунтов и сообщил, что
лимиты не подтягиваются. Через пятнадцать секунд всё появилось: опрос
провайдера просто ещё шёл.
Признак is_loading сервер отдавал (баз�овый снапшот выставляет его при
незавершённом опросе), но клиент его игнорировал и рисовал «Н/Д» — тот
же текст, что у подключённого аккаунта без лимитов. Два разных состояния
выглядели одинаково, и различить их было нельзя.
Теперь во время опроса ячейка показывает «Загрузка…» и «Опрашиваем
провайдера…» вместо прочерка.
Причина отказа важнее флага: если провайдер уже ответил «лимитов не
даю», состояние загрузки подавляется — иначе opencode-go и grok
показывали бы «Загрузка…» бесконечно.
Закреплено тестом в test_web_client_contract.py, включая проверку этого
подавления.
Тесты: 328 passed, ruff чисто.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
/api/snapshot отдавал квоты с source="baseline" и нулём измеренных
корзин всегда. Две независимые причины, и лечение одной из них ничего
не давало.
1. state_store наполняет квоты через quota_service.get_snapshot, который
читает кэш и при промахе отдаёт пустую заглушку, живой опрос НЕ
запуская. Кэш никто не грел: в десктопе это делал
_refresh_quotas_on_startup, в вебе аналога не было. Штатный
планировщик службы не спасает — его цикл сначала спит интервал
(300 с по умолчанию) и только потом опрашивает.
2. HubStateStore.get_snapshot() возвращает КЭШИРОВАННЫЙ снапшот и
пересобирает его только при первом вызове. Даже после прогрева квот
ответ оставался прежним. В десктопе пересбор делал _refresh_data.
Добавлен фоновый цикл: прогрев квот при старте, затем пересбор снапшота
каждые 30 секунд. Порядок важен — снапшот, собранный до прогрева,
зафиксировал бы пустые корзины.
Проверено исполнением: квоты появляются через ~10 секунд после старта,
24 измеренных корзины, source=provider_api, ag-w2 Gemini неделя 80.5% —
совпадает с прямым опросом провайдера.
Регрессия закрыта tests/test_web_snapshot_freshness.py, включая проверку
порядка «прогрев перед пересбором».
Тесты: 327 passed, ruff чисто.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Пропуск версии 1.0: каталог static/ был описан в структуре пакета, но не
сказано, кто его отдаёт. Обе стороны выполнили написанное и не собрались.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A15 и A16 сошлись в пустоту: API отвечал, файлы клиента лежали в
репозитории, но server.py не монтировал static — в браузере был 404 и до
интерфейса было не добраться.
Причина организационная и она на ревьюере: контракт описал каталог
static/ в структуре пакета, но в разделе об эндпоинтах не назвал, кто его
отдаёт. Обе стороны выполнили написанное и всё равно не собрались.
Подключены StaticFiles и корневой маршрут. Проверено исполнением:
/ -> 200 (13.5 КБ), /app.js -> 200 (51 КБ), /style.css -> 200 (21 КБ),
/api/health -> 200, /api/snapshot -> 200 (98 КБ).
Тесты: 325 passed, ruff чисто.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Правки при приёмке A15. Веб-API и вынесение действий приняты, но
в сданном виде не работали ни то, ни другое.
1. Десктоп был уничтожен. При выносе действий из hermes_hub_app.py
пропало объявление class HermesHubApp вместе с 13 методами каркаса:
__init__, _build_layout, _create_view, _show_view, _refresh_data и
другими. Оставшиеся 14 методов оказались вложены внутрь функции
_load_saved_theme после её return — синтаксически валидный
недостижимый код, поэтому модуль импортировался и дефект выглядел
безобидно. launch_hub() при этом падал бы с NameError.
hermes_hub_app.py восстановлен из main; задание прямо требовало
десктоп не ломать.
2. Дублирование убрано правильным способом: десктоп импортирует пять
do_* из action_handler, второй реализации в проекте нет.
3. Веб-API падал с 500 на обоих значимых эндпоинтах: get_auth_token и
run_server читали config.hub, а такого атрибута у RouterConfig нет.
Настройки живут в hub_settings.json. Работал только /api/health, у
которого нет проверки авторизации, — из-за чего сервер и выглядел
поднявшимся.
4. do_save_settings при переносе потеряла атомарную запись через
os.replace, ensure_ascii=False и вызов set_refresh_interval, то есть
интервал обновления квот из настроек перестал применяться.
Восстановлено.
5. Импорт адаптера был убран внутрь do_test_profile, что делало функцию
неподменяемой в тестах. Поднят на уровень модуля.
6. Версия в /api/health была зашита как "1.0.0" вместо настоящей.
Проверено исполнением: /api/snapshot отдаёт 200 и 12 ключей, полностью
совпадающих с docs/web-api/snapshot.example.json; секретов в ответе нет;
неизвестное действие даёт 404. Тесты: 319 passed, ruff чисто.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Работа A15 выполнена, но не закоммичена: git в его окружении был
недоступен. Восстановлена ревьюером из рабочего каталога.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Решение владельца: у него сервер с 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>
Переход на веб-интерфейс. Контракт вынесен отдельным документом, потому
что серверная и клиентская стороны делаются параллельно и до слияния
друг друга не видят: в прошлом раунде расхождение в одном имени модуля
молча оставило выбор моделей пустым навсегда.
Зафиксировано: стек (FastAPI + uvicorn, уже объявлены в pyproject;
клиент — обычный JS без сборки), требования безопасности (127.0.0.1 по
умолчанию, токен для внешнего адреса, тест на отсутствие секретов в
ответе), два эндпоинта поверх готового снапшота и семнадцати действий
_handle_action, правило игнорировать ответы с устаревшим seq.
snapshot.example.json снят с живой машины владельца: 63 профиля,
187 КБ. Почты замаскированы, токенов, ключей и JWT нет — проверено.
Клиентская сторона разрабатывается против этого файла и не блокируется
готовностью сервера.
Отдельно зафиксировано ограничение headless: Codex и Grok работают по
device-code, Antigravity и Claude используют redirect на localhost и на
сервере без экрана не заработают.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Принято, проверено исполнением.
Устойчивость набора — главный пункт и он закрыт по-настоящему: общий
корень Tk на сессию вместо шести отдельных ctk.CTk(). Десять полных
прогонов подряд дали 316 passed, 2 skipped и НОЛЬ ошибок. До правки
каждый прогон давал до семи ошибок, кочующих между файлами, из-за чего
настоящую поломку приходилось искать перепроверкой в изоляции.
Раздел контракта о границе Hub и Hermes написан: что Hub получает на
каждом вызове (с явным указанием, что роли среди этого нет), чего не
видит, чем управляет и чем не управляет. Варианты связывания профилей
поданы таблицей с оценкой трудоёмкости: 3-4 дня, 8-12 дней, 4-5 дней.
Ни один не реализован без решения владельца, как и требовалось.
Исправлено при слиянии: в документе оказалось 11 символов BEL (0x07) на
месте буквы "a" — последовательности вида \agy-05 были разобраны как
escape. Пострадали 19 идентификаторов: antigravity -> ntigravity,
agy-05 -> gy-05, ag-w2 -> g-w2. Контракт читают оба исполнителя, битые
имена в нём недопустимы.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A13 (Pro): раздел контракта о границе между учётными системами Hub и
Hermes — единственный незакрытый пункт A11; варианты связывания профилей
с ценой каждого; устранение нестабильности набора тестов (шесть корней
Tk дают до семи плавающих ошибок на прогон, мешая отличать настоящую
поломку от шума).
A14 (Flash): два невыполненных пункта A12 — компактные карточки
аккаунтов (accounts_view.py не менялся вообще) и разбор дублирующих
разделов «Провайдеры»/«Квоты» (quotas_view.py не тронут), плюс живые
скриншоты, которых не было.
Принято по A11: пропуск вызова без роли работает — замер 0.10 с без
попыток цепочки; зонд переведён на agy models; выдуманный список
gemini-2.5-* убран; refresh_codex_token появился; source квоты не
опережает данные. Релизный гейт стал зелёным, 7/7.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Работа A11 была выполнена, но осталась незакоммиченной в рабочем каталоге
на машине владельца: в origin ушла пустая ветка. Восстановлена ревьюером
из рабочего дерева и зафиксирована здесь.
Содержание:
- agy_subprocess: зонд обнаружения моделей переведён с разбора ошибки
заведомо неверной модели на штатную команду `agy models`;
- antigravity_adapter: убран выдуманный запасной список gemini-2.5-*,
моделей с такими именами у провайдера не существует;
- codex_oauth: добавлен refresh_codex_token — обновление по refresh_token,
которого не было вовсе;
- quota_collector: source больше не заявляет provider_api там, где ни
одна корзина не измерена; применено к antigravity и opencode-go;
- hermes_plugin: правка обработки роли, поверх сохранённой 2d62d39.
Исправлено при фиксации: тест test_opencode_shows_published_limits
закреплял прежнюю семантику source и падал. Приведён к честной:
source описывает происхождение чисел, а не факт ответа провайдера;
информация об ответе сохраняется в unavailable_reason.
Проверено исполнением: `agy models` сейчас нестабилен и висит даже при
прямом вызове (rc=124 по таймауту 100 с) — зонд честно возвращает пусто
и сохраняет кэш, а не выдумывает список.
Тесты: 303 passed, ruff чисто.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A11 (Pro, тяжёлая ветка): интеграция с Hermes — перехват без роли,
граница между учётными системами Hub и Hermes, токены Codex и безопасное
переключение аккаунта, замена зонда обнаружения моделей на agy models,
устранение выдуманного запасного списка в адаптере.
A12 (Flash, быстрая ветка): компактные карточки аккаунтов, разбор
дублирующих разделов «Провайдеры»/«Квоты», причина у каждого Н/Д, тест
на достижимость службы обнаружения моделей, живое подключение Grok.
Заданы строгие непересекающиеся списки файлов, чтобы параллельная работа
не дала конфликтов. В A12 порядок работы с git расписан пошагово: pull в
начале, push ветки сразу после первого коммита, обязательная проверка
финального push через git log origin/<ветка>.
A10 удалено — заменено этой парой.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Проверено исполнением на живой конфигурации владельца.
Принято:
- миграция конфигурации работает: 16 -> 22 профиля, claude и grok
получили по 3 слота, find_free_slot возвращает существующие профили по
всем пяти провайдерам. Это снимает корень жалобы «при подключении
грока ошибка»;
- резервная копия router_profiles.yaml.bak_<ts> создаётся, десять
профилей antigravity не изменены ни в одном поле, комментарии не
потеряны;
- квоты grok и opencode-go честно отдают None с причиной вместо
правдоподобных чисел;
- флаг /repair и /reinstall задействован (строка 979), предупреждение
CS0219 при сборке исчезло;
- граница зоны Codex не нарушена, правка плагина 2d62d39 сохранена.
Исправлено при слиянии:
1. Служба обнаружения моделей была недостижима. A9 создал
model_discovery_service.py, интерфейс импортирует model_discovery.
Импорт обёрнут в except ImportError, поэтому расхождение не давало
ошибки — выбор моделей просто оставался пустым навсегда. Добавлена
согласованная точка входа model_discovery.py.
2. Служба отдаёт discovered_at, каталог искал fetched_at/updated_at.
Каталог научен понимать discovered_at.
3. tests/test_ui_routing_graph.py закреплял выдуманный список моделей
("grok-3"). A9 верно убрал литералы, и тест начал падать. Тест
приведён к честному поведению: до обнаружения профиль остаётся без
моделей. Файл в зоне Codex, которому A9 запрещено было её трогать.
Тесты: 307 passed, 2 skipped, ruff чисто.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
У 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>
Codex остановился на исчерпании лимитов. Влито то, что сделано:
- выбор модели: ui/model_catalog.py с честным пустым состоянием;
- карточка кликабельна целиком (cursor=hand2 + Button-1), «три точки»
перестали быть единственным входом;
- окно настроек роли: _open_agent_settings_modal;
- правая панель «Статус в реальном времени» убрана, центр расширен;
- жёсткие срезы providers[:3] и agents[:5] в диаграмме устранены;
- причины у части Н/Д через unavailable_reason.
Не сделано и уходит в задание: компактные карточки аккаунтов
(accounts_view.py не тронут) и разбор дублирующих разделов
«Провайдеры»/«Квоты» (quotas_view.py не тронут).
Проверено: слияние без конфликтов, правка плагина 2d62d39 сохранена,
290 passed, ruff чисто. Падает только известный нестабильный Tk-тест,
воспроизводится на чистом main.
Каталог моделей сейчас честно пуст: он импортирует
router/model_discovery.py, которого в репозитории нет.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
P0-00: Hub подключён к Hermes как middleware llm_execution и срабатывает
на каждом вызове, но Hermes не передаёт role — поэтому всё уходило в роль
по умолчанию, цепочка orchestrator исчерпана, и Hermes получал текст
ошибки вместо ответа модели. Следствие устранено в 2d62d39; в задании —
причина: не претендовать на вызов без достоверной роли, описать границу
между учётными системами Hub и Hermes, подготовить варианты связывания
профилей.
P0-01: Codex сохраняет refresh_token, но функции обновления нет вовсе.
Требуется обновление токена, раздельная проверка access_token и
id_token и переключение аккаунта с остановкой клиента до подмены
учётных данных — по образцу Cockpit Tools, присланному владельцем.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Hub подключён к Hermes как middleware llm_execution и срабатывает на
каждом обращении к модели. Но Hermes роль не передаёт: в kwargs есть
model, provider, session_id, task_id — role нет. resolve_role поэтому
сваливается в роль по умолчанию, и КАЖДЫЙ вызов Hermes маршрутизируется
как orchestrator.
Цепочка orchestrator у владельца исчерпана целиком:
ag-orch-fallback skipped_unhealthy
codex-orch 429 «account is not active, check billing»
opengo-3 No API key found
ag-w1/ag-w3 agy authentication failed or timed out
Роутер возвращал «⚠️ Hermes Router Failover Exhausted» как ответ
ассистента, и Hermes показывал это вместо ответа модели, хотя его
собственный провайдер работал. Это и есть «основной оркестратор не
выбрался» из отчёта владельца.
Теперь при router_error вызов уходит дальше по цепочке (next_call),
а отказ пишется в журнал уровнем warning с полным следом. Плагин обязан
быть незаметным при отказе: он может улучшить маршрутизацию, но не имеет
права сделать Hermes хуже, чем без него.
Проверено исполнением: Hermes получает ответ провайдера, а не текст
ошибки. Тесты: 287 passed (падает только известный нестабильный Tk-тест,
воспроизводится на чистом main).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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>
Сводит две работы: A8 (Antigravity — запуск, развёртывание, самопроверка)
и codex/usability-fixes (B6 граф маршрутизации + B7 дефекты живого прогона,
плюс живой сбор квот).
Проверено исполнением на реальных аккаунтах владельца: квоты Antigravity
теперь приходят от провайдера (source=provider_api) по всем шести
авторизованным профилям с разными числами — ag-w2 показывает 37.4%
остатка недельного пула Claude/GPT. OpenCode Go честно отдаёт None
с причиной.
Разрешение конфликтов:
1. do_test_profile — оба агента чинили P0-3 по-разному. Сохранены обе
правки: проверка просроченной авторизации (A8) поверх локальной
проверки runtime без вызова модели (Codex).
2. _finish в мастере — взята содержательная версия Codex (проверка слота,
создание определения профиля, внесение в маршрутизацию, сброс
cooldown), но её хвост обёрнут так, чтобы сбой в журналировании или
on_complete не оставлял окно открытым. Регрессия 7090c8a закрыта
тестом и продолжает проходить.
Исправлено при слиянии: A8 проверял status.get("expired"), тогда как ключ
называется is_expired. Проверка была мертва изначально — её прикрывал
контроль в адаптере, и это вскрылось только когда Codex убрал вызов
адаптера из «Теста»: протухший аккаунт получал зелёную галочку.
Тесты: 288 passed, 2 skipped, ruff чисто.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>