hermes-hub/agents/inbox/2026-08-25-A26-accounts-without-slots.md
Hermes Team 8cb57dd6a1 docs(agents): задание A26 — аккаунты без слотов, распределение и пороги квот
Владелец сформулировал, ради чего задумывался хаб: вручную распределять
аккаунты по агентам и следить за квотами, подставляя другой аккаунт там, где
лимит подходит к концу. Модель слотов этому мешает — она навязывает
внутреннее устройство конфигурации и уже привела к тому, что подключённый
аккаунт не появился в маршрутизации.

Задание опирается на три факта, снятых исполнением, чтобы агенты не
переписывали работающее: один профиль уже может обслуживать все шесть ролей;
произвольный идентификатор профиля регистрируется, то есть потолок в три
аккаунта Codex держит только зашитый список в auto_assigner; порогов квот в
коде нет вовсе.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-25 14:40:37 +07:00

171 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Задание A26: аккаунты без слотов, распределение по агентам и пороги квот
## Дата поступления
2026-08-25
## База
Проверочный HEAD на момент выдачи: **`d4b99a4`**.
## Ветка
`antigravity/accounts-without-slots`
## Порядок исполнения
Два прохода: **Flash** реализует, **Pro** проводит аудит. Пункт **P0-6** написан для аудитора.
---
## Порядок работы с git
```
cd <каталог репозитория>; git fetch origin --prune; git status
git checkout main; git pull --ff-only origin main
git checkout -b antigravity/accounts-without-slots
git commit -m "..." <- сначала коммит
git push -u origin antigravity/accounts-without-slots
```
В `main` напрямую не пушить. В конце — push и проверка `git log --oneline -1`, `git status` чистый.
---
## Ради чего это всё
Дословно от владельца:
> «вот для этого хаб и делался. чтобы вручную правильно распределять аккаунты и играться с лимитами. где подзаканчиваются, там ставить другой аккаунт»
Сейчас продукт этого не даёт. Модель «слотов» — десять предсозданных ячеек Antigravity, три Codex, три OpenCode — навязывает пользователю внутреннее устройство конфигурации. Владелец выбирает слот, ничего не зная о последствиях, и получает результат, которого не ожидал: аккаунт подключён, а на экранах его нет, потому что слот не входит ни в одну цепочку. Это уже случилось вживую.
Нужен обратный порядок: **сначала аккаунты, потом назначение**.
---
## Что уже проверено исполнением — заново не выясняйте
Три факта, снятые ревьюером на `d4b99a4`. Они сильно сокращают работу.
**1. Один профиль может обслуживать все шесть ролей.** Движок это держит уже сейчас:
```
persist_role_chain(role, ['ag-w1']) для всех шести ролей -> True
конфигурация: все шесть цепочек равны ['ag-w1']
```
Значит требование «если есть только Antigravity, он встаёт во всех агентах» **не требует изменений в маршрутизаторе**. Не переписывайте `router_engine`.
**2. Произвольный идентификатор профиля регистрируется.**
```
ensure_profile_definition('openai-codex', 'codex-worker-9') -> True
'codex-worker-9' in load_router_config().profiles -> True
```
Значит потолок «три аккаунта Codex» — это **только** зашитый список `provider_slots` в `auto_assigner.py:144`, а не структурное ограничение. Снятие потолка не требует переделки формата конфигурации.
**3. Порогов квот в коде нет вообще.** Поиск по `threshold|min_quota|quota_floor|switch_at` в `router/` не даёт ни одного совпадения. Это делается с нуля.
---
## P0-1. Снять потолок на число аккаунтов
Дословно: «например у меня есть 4 кодекса, и их все я хочу добавить. или 5 опенкодов».
Сейчас `AutoAssigner.find_free_slot` (`auto_assigner.py:140`) перебирает жёсткий список и возвращает `None`, когда список кончился. Четвёртый Codex подключить невозможно.
Требуется: число аккаунтов провайдера **ничем не ограничено**. Когда предопределённые имена кончились, идентификатор выдаётся автоматически по понятной схеме (`codex-4`, `codex-5`, …), с проверкой, что такого ещё нет.
Идентификатор — деталь реализации, и в интерфейсе он не должен быть главным. Владелец мыслит аккаунтами и почтами, а не слотами.
## P0-2. «Аккаунты» показывают только настоящие аккаунты
Дословно: «надо убрать все ячейки со вкладки аккаунты. как добавляю аккаунт, тогда он там появляется. не надо захламлять страницу».
Сейчас в умолчаниях предсозданы **24 профиля**, и страница показывает их все, включая никогда не подключённые: «Аккаунт не добавлен», «Холодный резерв». У владельца это 24 карточки при одном реальном аккаунте.
Требуется показывать **только подключённые**.
Осторожно: не удаляйте профили из конфигурации молча — на них ссылаются цепочки ролей. Речь о том, что показывать, а не о том, что хранить. Решите чистить и конфигурацию — обоснуйте в отчёте и сохраните работоспособность цепочек.
## P0-3. Назначение аккаунта агенту — в «Обзоре»
Дословно: «просто загрузка аккаунтов, а уже в обзоре прикреплять нужный аккаунт к агенту».
На «Обзоре» уже есть схема ролей и выбор модели в узле (A24). Добавить туда выбор **аккаунта**: какой обслуживает эту роль и в каком порядке резервирования.
Действия существуют — `assign_role`, `save_chain`, `reorder_chain`; второй реализации не заводить.
Ключевое требование, вытекающее из факта №1: **один аккаунт можно назначить сразу нескольким агентам**, вплоть до всех шести. Интерфейс не должен этому препятствовать и не должен считать это ошибкой.
## P0-4. Кнопка «Авто»
Дословно: «или при нажатии авто, сам распределяет аккаунты согласно маршрутизации».
Действие `auto_assign_all` существует (`action_handler.py:509`), но что оно делает и совпадает ли с ожиданием владельца — **проверьте исполнением и опишите в отчёте**. Отчёт без запуска не принимается.
Ожидаемое поведение:
- **Один провайдер.** Есть только Antigravity — он встаёт во все шесть ролей. Владелец дальше сам меняет модель у каждого агента. Это нормальный режим, а не вырожденный случай.
- **Несколько провайдеров.** Распределение идёт по назначению роли: «оркестратор у меня первый кодекс, а кодер антигравити» — у каждой роли есть предпочтительный провайдер, и авто-распределение ему следует, а не раскладывает аккаунты подряд.
- **Несколько аккаунтов одного провайдера** разводятся по разным ролям, чтобы не жечь квоту одного на всё сразу.
Порядок предпочтений по ролям возьмите из текущего `config/router_profiles.example.yaml` — он и выражает замысел владельца. Решите его менять — сначала спросите в отчёте, молча не меняйте.
## P0-5. Пороги квот: предупреждение или переключение
Дословно: «выставлять минимальные лимиты (например 10% или 5%) и при достижении этих лимитов или оповещение или автоматом переключает на резервный аккаунт».
Это то, ради чего хаб задумывался. Требуется:
1. **Настраиваемый порог** — общий и, если несложно, отдельный для аккаунта. Значения владельца: 10% и 5%. **В код их не зашивать**, это умолчание, а не константа.
2. **Выбор поведения** при достижении: только оповестить либо переключиться на следующий аккаунт в цепочке. Решает владелец, не код.
3. **Оповещение видно в интерфейсе** — в «Состоянии» и в журнале событий. Достаточно ли тоста — решите и обоснуйте.
4. **Переключение обратимо.** Квоты восстанавливаются по `reset_at`; отставленный по порогу аккаунт обязан вернуться в строй сам. Урок A23 уже оплачен: у `AUTH_REQUIRED` не было выхода, и шесть профилей выпали навсегда. **Повторять нельзя.**
Данные есть: `quota_collector` собирает проценты и `reset_at`, они видны в карточках.
Честность обязательна: порог срабатывает по **измеренной** квоте. Неизвестная квота («Н/Д») — **не** повод считать её нулевой и переключаться. Нет данных — так и сказать, поведение не менять.
## P0-6. Аудит вторым проходом
Для проверяющего. Список составлен из дефектов, уже проходивших мимо первого прохода.
1. **Выдуманные значения.** Находились захардкоженные коды устройства `GRK-7842`, `CDX-9104`, запасной коммит `fb23bff` в манифесте. Здесь особый риск в P0-5: соблазн подставить «разумный» процент при отсутствии данных.
2. **Тесты, закрепляющие дефект.** `test_headless_server_auth_matrix` дважды защищал заглушку: сначала требовал неверный адрес `x.ai/device`, затем слова «Headless» и «agy», хотя вход через веб уже работал. Новые тесты должны описывать желаемое поведение.
3. **Запустить изменённое.** Импорт ничего не доказывает. На днях вложенная функция оказалась не видна части точек вызова и давала `NameError` внутри обработки успеха — поймал `ruff`, а не тест.
4. **Кэш состояния.** Только что чинилось: учётные данные сохранялись, но `refresh(force_scan=False)` состояние не обновлял, и подключённый аккаунт не появлялся никогда. Любое изменение состава аккаунтов обязано быть видно **сразу**, без перезапуска.
5. **Побочные изменения** в файлах, которых задание не касалось, — объяснить каждое.
6. **Пропущенный пункт назвать пропущенным.**
---
## Ограничения
- Десктоп (`router/ui/**`) не трогать: он выводится из обращения.
- Правило честности без исключений. Нет данных — «Н/Д» и причина.
- Действия только через существующий `action_handler`; второй реализации `assign_role`, `save_chain`, `set_model` быть не должно.
- Меняете контракт — правьте `docs/web-api/CONTRACT.md` и скажите в отчёте.
- Конфигурацию владельца литералами не править.
- Тег `v0.1.1` не создавать.
## Критерии приёмки
1. Ветка в `origin`, `git status` чист.
2. Подключается **пятый** аккаунт одного провайдера; проверено исполнением, вывод в отчёте.
3. «Аккаунты» показывают только подключённые; при нуле подключённых — внятное пустое состояние, а не 24 пустые карточки.
4. Аккаунт назначается агенту из «Обзора»; один аккаунт назначается **всем шести** ролям; изменение доходит до `router_profiles.yaml` и переживает перезапуск.
5. «Авто» описано по факту запуска: что делает при одном провайдере, при двух, при нескольких аккаунтах одного провайдера.
6. Порог настраивается, значение не зашито; при достижении срабатывает выбранное поведение; при неизвестной квоте не срабатывает.
7. Аккаунт, отставленный по порогу, возвращается в строй после восстановления квоты **без ручного вмешательства**; проверено тестом.
8. Изменения состава аккаунтов видны в интерфейсе сразу, без перезапуска.
9. Ни одного выдуманного значения; проверено отдельно и описано в отчёте.
10. `ruff check .` чисто; релизный гейт не ухудшен.
11. **Скриншоты:** «Аккаунты» с одним аккаунтом, «Обзор» с назначением, настройка порога.
12. Отчёт: `START_HEAD`, `FINAL_HEAD`, `origin/main`, `git status`, `X passed / Y skipped / Z failed`. На `main` сейчас **432 passed, 2 skipped**.
## Главное
Владелец хочет управлять аккаунтами, а не слотами: загрузил аккаунты, распределил по агентам, следит за квотами и переставляет, когда они подходят к концу. Всё остальное в задании обслуживает это.
## Порядок сдачи
Передать точный `FINAL_COMMIT_SHA`. Сдано только после появления коммита в `origin`.