# Задание B8 (Codex): выбор моделей, компактные аккаунты, чистка дублирующих разделов

## Дата поступления
2026-08-23

## База
Проверочный HEAD на момент выдачи: **`b625b0f`**. Обязательно обновить локальную копию — см. следующий раздел.

## Ветка
`codex/cockpit-usability`

---

## Перед началом: обновить локальную копию

Ваша ветка `codex/usability-fixes` **отправлена в git ревьюером и влита в `main`** (`b625b0f`). Она лежала только в локальной папке и нигде не была сохранена — если бы диск отказал, работа четырёх часов пропала бы. Впредь пушьте ветку сразу после первого коммита, до всякой готовности: ветка в `origin` ничего не ломает, а несохранённая работа теряется.

```
cd <каталог репозитория>; git fetch origin --prune; git status
git checkout main; git reset --hard origin/main
```

Зафиксировать фактический `BASE_SHA` через `git rev-parse --short HEAD`. Не считать `b625b0f` актуальным автоматически: параллельно идёт задание A9 у Antigravity.

---

## Что принято

Проверено исполнением, и это лучшая работа за все раунды.

**Живые квоты Antigravity — сделано по-настоящему.** Вы не стали рисовать заглушку, а нашли и подключили настоящий эндпоинт `retrieveUserQuotaSummary` с обновлением OAuth-токена при 401. Запуск на шести аккаунтах владельца:

```
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-w4   Gemini — неделя      remaining=100.0  used= 0.03  source=provider_api
```

Числа настоящие, у каждого аккаунта свои. Это закрывает главную жалобу владельца и делает продукт тем, ради чего он задуман.

Отдельно отмечу: **OpenCode Go отдаёт `None` с причиной**, а не правдоподобный процент. Соблазн заполнить пустоту был, и вы ему не поддались — правило честности выдержано там, где это стоило усилий.

Также принято: `edit_route` открывает настоящий редактор цепочки (`_open_route_editor_modal`), мастер проверяет слот и вносит профиль в маршрутизацию, ввод ключа API сообщает о результате вставки, граф маршрутизации на «Команде» с тестами.

### Что поправлено при слиянии

1. **Граница нарушена.** Изменены пять файлов зоны Antigravity: `quota_collector.py`, `unified_health.py`, `auto_assigner.py`, `health_tracker.py`, `account_identity.py`. Это дало конфликт с A8 в двух файлах. Правки приняты, потому что они ценные, но так делать нельзя: если бы Antigravity в тот же час переписал `quota_collector.py`, одна из работ была бы потеряна при слиянии. Нужен файл в чужой зоне — скажите, и он будет заказан отдельным заданием.

2. **`_finish` снова стал хрупким.** Ваша версия содержательнее прежней, её и взяли. Но хвост `log(...) → on_complete(...) → destroy()` вернул ровно тот дефект, который чинили сутки назад: исключение в любом из двух вызовов оставляет мастер открытым, и под `pythonw` пользователь не видит причины. Хвост обёрнут так, чтобы `destroy()` выполнялся всегда. Ранние `return` с сообщением в окне оставлены как есть — это правильное поведение.

3. **Протухший аккаунт получал зелёную галочку.** Вы переписали «Тест» на локальную проверку runtime без вызова модели — само по себе разумно. Но проверка просроченной авторизации от Antigravity была написана с опечаткой в имени ключа и не срабатывала; пока «Тест» ходил в адаптер, дефект прикрывался, а после вашей правки вскрылся. Исправлено ревьюером.

---

## Что говорит владелец сейчас

Дословно, одиннадцать пунктов. Разобрано по коду; ниже только ваша зона.

Сначала важное: **владелец тестировал устаревшую развёрнутую копию.** Ревьюер зеркально развернул свежий код на его машину (удалено девять устаревших файлов, добавлено два новых, обновлено пятнадцать). Поэтому часть жалоб уже закрыта вашей работой и требует только перепроверки:

| Жалоба | Состояние |
|---|---|
| 1. Лимиты не подтягиваются | Закрыто для Antigravity. Для codex и opencode — задание A9 |
| 4. «Настроить» не работает | Закрыто, открывается редактор цепочки |
| 8. OpenCode не даёт вставить API | Закрыто, поле и «Вставить» с обратной связью |
| 9. Квота не отображается в аккаунтах | Закрыто в части данных; вид — ниже, пункт P0-2 |
| 11. У OpenCode нет лимитов | Провайдер их не отдаёт; причина показывается. Дожимает A9 |
| 3а. Нет «Быстрого» и «Исследователя» на «Обзоре» | Проверено на свежем коде: все пять ролей помещаются. Это был эффект устаревшей копии |

Остальное — работа.

## P0-1. Выбор модели для агента

Жалоба: **«нет возможности выбрать модели для агентов. и надо сделать»**.

Сейчас модель только показывается (`team_view.py:690`, `dashboard_view.py:614`) и нигде не выбирается. Ни одного элемента управления моделью в интерфейсе нет.

Требуется выбор модели для роли и для профиля, сохраняемый в конфигурацию.

Источник списка — служба обнаружения моделей, которую делает Antigravity в A9 (кэш на диске, обновление в фоне, отметка свежести). **Синхронно вызывать `discover_models` из интерфейса нельзя:** замерено, что `agy models` в одном прогоне отвечает за 40 секунд, а в следующем висит больше двух минут. Окно замёрзнет.

До появления службы: читать список из кэша, если он есть; при пустом кэше показывать «список моделей ещё не получен» и кнопку обновления, работающую в фоне. **Не подставлять список литералом** — у владельца в конфигурации уже стоит `gemini-3.7-flash`, которой у провайдера не существует, и появилась она именно так.

Если выбранная модель отсутствует в обнаруженном списке — пометить её и объяснить, но не менять молча за пользователя.

## P0-2. Аккаунты: компактно, как в Cockpit Tools, без раскрывающихся карточек

Жалобы: **«аккаунты должны выглядеть как у кокпит тулс, компактно»** и **«вид не тот, не надо делать раскрывающееся окно»**.

Требуется:

- плотный список фиксированной высоты: провайдер, идентичность, роль, состояние авторизации, квота — в одной строке, без раскрытия;
- квота видна сразу, числом и полосой, с пометкой периода (`Claude/GPT — неделя`), потому что у Antigravity пулов четыре и «просто процент» вводит в заблуждение;
- шестнадцать аккаунтов должны читаться без прокрутки внутрь карточек;
- дельта-отрисовку по стабильным ключам не терять — она уже есть и это главное преимущество перед прошлой версией.

Документ `COCKPIT_TOOLS_ARCHITECTURE_COMPARISON.md` в корне репозитория описывает целевую модель обновления (`splice-by-index` вместо пересборки всех карточек). Это архитектурное сравнение, а не макет: берите из него принцип обновления и плотность, а не буквальную вёрстку.

## P0-3. Вся карточка кликабельна

Жалоба: **«чтобы полностью окно было активным, чтобы не выцеливать нажимать 3 точки справа в углу»**.

Клик по любому месту карточки аккаунта или агента открывает её детали. Меню «три точки» остаётся для второстепенных действий, но перестаёт быть единственным входом. Курсор меняется на указатель, есть состояние наведения, работает клавиатура (Tab и Enter).

## P0-4. Карточка роли открывает настройки роли

Жалоба: **«при нажатии на окно (например кодер) нужно чтобы выходило окно с настройками кодера, где выходят сразу лимиты, и можно поменять модель агента и сменить аккаунт»**.

Клик по роли на «Обзоре» и на «Команде» открывает окно роли, в котором сразу видно:

- цепочка отказоустойчивости: основной профиль и резервы по порядку, какой активен сейчас;
- квоты активного профиля — числом, по пулам, с временем сброса;
- смена модели (P0-1);
- смена аккаунта, то есть перестановка профиля в цепочке — через существующий `AutoAssigner`, второй редактор маршрутизации не создавать;
- причина последнего переключения, если она была.

## P0-5. Убрать «Статус в реальном времени», центр растянуть

Жалоба: **«блок справа не понятный. Статус в реальном времени. там ничего не отображается, просто левые цифры. можно пока его убрать, а центральный блок растянуть на все окно»**.

Убрать правую панель (`dashboard_view.py:447`) и отдать её ширину схеме маршрутизации. Владелец прав по существу: панель занимает треть экрана и показывает то, что либо дублируется, либо не измеряется.

Если внутри неё есть блок с настоящими измеренными данными — не выбрасывать, а перенести в строку KPI и сказать в отчёте, какой именно и на каком поле контракта он основан.

## P0-6. Жёсткие срезы в диаграмме

`dashboard_view.py:601` — `providers = list(snapshot.providers)[:3]` при пяти провайдерах: `claude` и `grok` молча отбрасываются. Строкой ниже `agents[:5]` — сейчас ролей ровно пять и срез не виден, но добавление шестой роли её потеряет.

Диаграмма должна следовать данным: сколько ролей и провайдеров в снапшоте, столько узлов. Порядок провайдеров задаёт Antigravity в A9.

## P0-7. «Провайдеры» и «Квоты и лимиты» дублируют «Аккаунты»

Жалоба: **«провайдеры и квоты и лимиты вообще не понятно для чего нужны, там все тоже, что и в аккаунты»**.

Владелец прав: после того как квоты появились прямо в карточках аккаунтов, отдельный раздел квот потерял смысл.

Требуется решение, а не сохранение обоих на всякий случай:

- **«Квоты и лимиты»** — либо убрать из навигации, либо оставить только то, чего нет в «Аккаунтах»: сводка по провайдеру целиком, история расхода, ближайшие сбросы. Если такого содержания нет — убрать.
- **«Провайдеры»** — оставить то, что относится к провайдеру, а не к аккаунту: доступность runtime, обнаруженные модели, версия CLI, состояние авторизации в целом.

В отчёте перечислить, что перенесено, что удалено и почему. Пустой раздел не оставлять — правило прежнее: либо содержание, либо нет пункта.

## P0-8. Объяснить Н/Д в маршрутизации

Жалоба: **«что означает н/д в маршрутизация запросов»**.

Это претензия не к честности, а к молчаливости: владелец видит `Н/Д` и не знает, это поломка, ненастроенное или неизмеримое.

Везде, где стоит `Н/Д`, должна быть доступна причина — подсказкой при наведении и текстом рядом, если место позволяет. Формулировки конкретные: «нет телеметрии: роль ещё не вызывалась», «провайдер не отдаёт лимиты», «аккаунт не подключён». Данные для этого есть — `unavailable_reason` в снапшоте квот уже заполняется.

---

## Ограничения

- Граница: ваша зона — `src/antigravity_provider/router/ui/**`, `hermes_hub_app.py` в части представления, `tests/test_ui_*.py`. **Файлы зоны Antigravity не трогать** — понадобился, закажите.
- Не выдумывать модели, числа и идентификаторы. Нет данных — `Н/Д` с причиной либо блок отсутствует.
- Три темы сохранить, дельта-отрисовку не терять.
- Мастер не ломать: шесть потоков подключения, трёхэлементная распаковка `start_profile_oauth`, `destroy()` в `_finish` выполняется всегда.
- Тег `v0.1.1` не создавать.
- **Ветку запушить сразу после первого коммита.**

## Критерии приёмки

1. Ни один файл зоны Antigravity не изменён.
2. Модель агента выбирается и сохраняется; список берётся из кэша, интерфейс не блокируется; при пустом кэше показано «список ещё не получен», а не литерал.
3. Карточка аккаунта компактна, фиксированной высоты, без раскрытия; квота видна сразу с указанием пула и периода; шестнадцать аккаунтов читаются без внутренней прокрутки.
4. Клик по любому месту карточки открывает детали; «три точки» перестали быть единственным входом; работает Tab и Enter.
5. Клик по роли открывает окно роли с цепочкой, квотами, сменой модели и сменой аккаунта.
6. Правая панель убрана, схема занимает освободившуюся ширину; в отчёте сказано, что перенесено в KPI и на каком поле контракта основано.
7. Диаграмма показывает все роли и всех провайдеров из снапшота; жёстких срезов не осталось.
8. Принято решение по «Провайдерам» и «Квотам»; в отчёте перечислено перенесённое и удалённое.
9. У каждого `Н/Д` доступна причина.
10. Прогон **в обоих окружениях** — без UI-зависимостей и с `customtkinter`/`pillow`/`psutil`; обе команды и оба результата в отчёте.
11. `ruff check .` чисто. Релизный гейт **уже красный на `main`** (проверка 4 падает не по вашей вине) — указать состояние до и после; ухудшать нельзя.
12. **Скриншоты живого сценария с настоящими данными**: «Аккаунты» с видимыми квотами, окно роли, выбор модели, «Обзор» без правой панели. Пустых состояний не присылать.

## Главное

Квоты вы уже сделали настоящими — самое трудное позади. Осталось, чтобы владелец мог управлять тем, что видит: выбрать модель, сменить аккаунт у роли, окинуть взглядом шестнадцать аккаунтов без прокрутки и в каждом непонятном месте получить ответ, почему там прочерк.

## Порядок сдачи
Передать точный `FINAL_COMMIT_SHA`.
