diff --git a/agents/inbox/2026-08-21-B3-codex-contract-v11-fields.md b/agents/inbox/2026-08-21-B3-codex-contract-v11-fields.md new file mode 100644 index 0000000..81cbdcc --- /dev/null +++ b/agents/inbox/2026-08-21-B3-codex-contract-v11-fields.md @@ -0,0 +1,97 @@ +# Задание 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` и не вести разработку поверх него до вердикта.