# Задание B3 (Codex): подключить поля контракта v1.1 ## Дата поступления 2026-08-21 ## База Проверочный HEAD: **`2c612f2`**, `origin/main` = `2c612f2`. Перед началом `git fetch` и обязательно подтянуть `main` — ваши фазы 2–6 уже влиты, поверх них есть правки. ## Ветка `codex/contract-v11`. Ветка `codex/ui-redesign` влита в `main`. --- ## Что принято по фазам 2–6 Проверено исполнением, работа хорошая: - граница соблюдена — вне вашей зоны только файл отчёта; - **пересоздания виджетов не осталось нигде**: ни одного `winfo_children()` в `views/`, всё на стабильных ключах; - **выдуманных метрик нет**, «Н/Д» используется в семи экранах; - **views вообще не обращаются к backend** — ни `HubStateStore`, ни `scan_all`, ни сервисов; данные приходят только снапшотом; - мастер подключения пережил слияние: трёхэлементная распаковка `start_profile_oauth` и `oauth_port` на месте; - защита от краша при старте не только сохранена, но усилена — `update_data` выходит рано вместо отката к хранилищу. Одна правка с моей стороны: инвариант `tests/test_view_startup_contract.py` требовал обращения к хранилищу и после вашей переделки начал скипаться для всех девяти views, то есть перестал защищать тот самый краш. Переписан под новый контракт — проверка `isinstance(snapshot, HubSnapshot)` теперь обязательна независимо от того, читает ли view хранилище. --- ## Задача Вы работали по контракту **v1.0**, а Antigravity параллельно выпустил **v1.1** и добавил поля, которых на тот момент не было. Данные в снапшоте есть, но интерфейс их не показывает. Проверено на `2c612f2`: | Поле | Где появилось | Используется в UI | |---|---|---| | `plan_code` | `ProfileViewModel` | **0 файлов** | | `plan_source` | `ProfileViewModel` | **0 файлов** | | `active_quota_status` | `AgentViewModel` | **0 файлов** | | `active_quota_label` | `AgentViewModel` | **0 файлов** | | `quota_status` | `PipelineNode` | **0 файлов** | | `failover_reason` | `PipelineNode` | **0 файлов** | | `unavailable_reason` | `QuotaBucket` | **0 файлов** | | `seq` | `HubSnapshot` | **0 файлов** | | `session_id` | `AgentViewModel` | 1 файл | | `account_identity` | `PipelineNode` | 4 файла | | `is_estimated` | `QuotaSnapshot` | 2 файла | Перечитайте `docs/UI_STATE_CONTRACT.md` целиком — он переписан, появился раздел «Provider Truth Matrix». ### 1. Тариф — `PlanBadge` наконец можно показать Компонент вы сделали в PHASE 1, данные появились в v1.1. Показывать бейдж, когда `plan_source` говорит, что тариф известен от провайдера (`provider_api`, `jwt_claim`, `provider_auth`), и **не показывать** при `inferred`/`unknown` — либо показывать с явной пометкой, что значение выведено, а не получено. Пользователь должен отличать «PRO по данным провайдера» от «похоже на PRO». ### 2. Квоты — состояние без данных `QuotaBucket.unavailable_reason` объясняет, **почему** данных нет. Сейчас пользователь видит пустоту без причины. Показывать причину в подсказке или подписи корзины. Важно: по контракту baseline-значения теперь `None`, а не числа. Убедитесь, что `QuotaBar` и `QuotaBucketWidget` корректно отрисовывают именно отсутствие значения, а не «0 %» — ноль означает «квота исчерпана», это принципиально разные состояния. ### 3. Команда — состояние агента `active_quota_status` и `active_quota_label` показывают, в каком состоянии квота у аккаунта, на котором сейчас работает агент. Это прямо отвечает на вопрос «сколько осталось у того, кто выполняет задачу». ### 4. Маршрутизация — причина переключения `PipelineNode.failover_reason` и `quota_status` — то, ради чего экран существует: видно не только «активен резерв 1», но и почему ушли с основного. Показывать причину у узла, с которого произошло переключение. ### 5. Свежесть данных `HubSnapshot.seq` и политика `is_stale` (по контракту — просрочка фонового обновления свыше 300 с). Показать пользователю, что данные устарели, вместо молчаливого показа старых значений. --- ## Ограничения - Граница прежняя: ваша зона — `src/antigravity_provider/router/ui/**`, `hermes_hub_app.py`, `tests/test_ui_*.py`. Остальное не трогать. - Не добавлять сбор данных в UI. Не хватает поля — строка в разделе «Backend gaps» отчёта. - Нет данных — «Н/Д» либо скрытый блок. Ноль и отсутствие значения не путать. - Секреты маскировать, в логи не писать. - Тег `v0.1.1` не создавать. --- ## Критерии приёмки 1. Ни один файл чужой зоны не изменён. 2. `PlanBadge` отображается и различает известный тариф от выведенного. 3. Отсутствие данных о квоте отрисовано как отсутствие, а не как ноль; причина видна пользователю. 4. Экран «Команда» показывает состояние квоты активного аккаунта агента. 5. Экран «Маршрутизация» показывает причину переключения там, где она есть в состоянии. 6. Устаревание снапшота видно пользователю. 7. Тесты на каждый пункт: известный и выведенный тариф, корзина без данных против нулевой, причина переключения, устаревший снапшот. 8. Прогон **в обоих окружениях** — без UI-зависимостей и с `customtkinter`/`pillow`/`psutil`; обе команды и оба результата в отчёте. Прогон только headless не считается: ваши UI-тесты там пропускаются. 9. `ruff check .` чисто; release gate не ухудшен. 10. Отчёт: `BASE_SHA`, `FINAL_SHA`, изменённые файлы, что подключено, что осталось, раздел «Backend gaps». ## Порядок сдачи Передать точный `FINAL_COMMIT_SHA` и не вести разработку поверх него до вердикта.