Тринадцать файлов существовали только на диске ПК владельца, в рабочей копии, отставшей от origin/main на 122 коммита. Их реализация и тесты давно влиты: tests/test_a42_provider_connect.py, test_a49_subagents_skills_memory.py, test_a51_hub_controls_hermes.py, test_a52_local_models_supervisor_dual.py, test_a55_account_connection.py, test_a56_context_compression.py и другие. Постановок, объясняющих, что эти тесты обязаны доказывать, в репозитории не было. Правило записано в agents/AGENTS.md: задание живёт в репозитории, а не в переписке и не в личных папках на диске. A48, A50 и A54 не переносятся: они уже есть на origin под другими именами, содержимое совпадает с точностью до перевода строки в конце файла. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
166 lines
15 KiB
Markdown
166 lines
15 KiB
Markdown
# Задание A51: подключённый аккаунт должен реально использоваться Hermes
|
||
|
||
## Дата поступления
|
||
2026-08-31
|
||
|
||
## База
|
||
|
||
`origin/main` (`17b368a`).
|
||
|
||
```
|
||
git fetch origin --prune
|
||
git checkout -b antigravity/a51-hub-controls-hermes origin/main
|
||
```
|
||
|
||
В `main` напрямую не пушить.
|
||
|
||
## Порядок исполнения
|
||
|
||
Два прохода: **Flash** реализует, **Pro** проводит аудит. Пункт **P0-6** написан для аудитора.
|
||
|
||
Зона: маршрутизация, назначение ролей, плагин Hermes, карточка аккаунта. С A49 (скиллы и память) и A50 (обнаружение и проверка) не пересекается по смыслу, но **трогает те же файлы, что A50** — `auto_assigner.py` и экран «Аккаунты». Выполнять **после A50**.
|
||
|
||
---
|
||
|
||
## Задача
|
||
|
||
Владелец: «в хабе я поставил аккаунт аги. Когда я захожу в Гермеса, какой аккаунт будет выбран? И если я поменяю в Гермесе аккаунт, поменяется он в хабе? Иначе толку от хаба, если в самом Гермесе это не работает».
|
||
|
||
Ответ, проверенный ревьюером: **сейчас не будет выбран ни один из его аккаунтов**.
|
||
|
||
---
|
||
|
||
## Что проверено исполнением
|
||
|
||
**Определение роли работает.** A35 встал: плагин перехватывает каждый `llm_execution` и определяет роль четырьмя уровнями — явная, по модели и провайдеру, по устойчивости сессии, по умолчанию. Это переделке не подлежит.
|
||
|
||
**Но цепочки указывают в пустоту.** Конфигурация владельца на рабочей машине:
|
||
|
||
```
|
||
default_role: не задан → используется manager
|
||
manager → ag-orch-fallback, codex-orch, opengo-3
|
||
developer-1 → ag-w1, codex-worker-1, opengo-3
|
||
researcher → opengo-1, ag-w3, ag-w4
|
||
|
||
профилей в конфигурации: 24
|
||
ролей: 13
|
||
```
|
||
|
||
Проверка вхождения **подключённых** аккаунтов владельца в цепочки:
|
||
|
||
```
|
||
ollama-1 НИ В ОДНОЙ
|
||
grok-1 НИ В ОДНОЙ
|
||
local-2 НИ В ОДНОЙ
|
||
local-3 НИ В ОДНОЙ
|
||
antigravity-1 НИ В ОДНОЙ
|
||
```
|
||
|
||
Все тринадцать ролей по-прежнему ссылаются на заготовки из старой конфигурации на 24 слота, а они не настроены. Значит при обращении Hermes цепочка `manager` перебирает три неавторизованных слота, отказывает, и плагин — правильно, по своему устройству — **пропускает вызов мимо хаба дальше в Hermes**:
|
||
|
||
```python
|
||
if isinstance(completion, dict) and completion.get("router_error"):
|
||
logger.warning("Router failover exhausted for role %r; passing the call downstream to Hermes")
|
||
```
|
||
|
||
Хаб при этом ведёт себя корректно: он не подменяет ответ. Но результат для владельца тот самый, которого он опасается — **хаб не участвует в работе вовсе**.
|
||
|
||
**Карточка аккаунта показывает роль, которой нет.** На экране `ollama-1` подписан «manager (primary)», хотя в цепочке `manager` его нет. Источник подписи — `auto_assigner.get_display_name_and_role`, а она читает **статическую таблицу** `DEFAULT_SLOT_ROLES`, а при промахе достраивает подпись из имени провайдера. В снапшот это попадает так:
|
||
|
||
```python
|
||
assigned_roles=role_assignments.get(pid, [log_role])
|
||
```
|
||
|
||
Есть аккаунт в живой цепочке — берётся живое значение; нет — подставляется **догадка**. Владелец видит «manager (primary)» и считает, что аккаунт назначен.
|
||
|
||
Тот же аккаунт на карточке списка подписан «worker», а в окне — «manager (primary)». Два разных источника в двух местах.
|
||
|
||
**Обратной синхронизации нет.** `_select_model` в `hermes_plugin.py` — ручная команда CLI, которая записывает в конфигурацию Hermes провайдера, модель и адрес. Ничего, что читало бы выбор владельца, сделанный **внутри** Hermes, и переносило бы его в хаб, в коде нет.
|
||
|
||
---
|
||
|
||
## P0-1. Подключённый аккаунт попадает в цепочку
|
||
|
||
1. **Подключение аккаунта ставит его в цепочку выбранной роли.** Роль владелец выбирает на третьем шаге мастера — сейчас этот выбор до цепочки не доходит.
|
||
2. **Если аккаунт никуда не назначен — так и писать.** «Не назначен» — нормальное состояние, но оно должно быть видно, а не подменяться догадкой.
|
||
3. **Кнопка «Авто»**: разложить подключённые аккаунты по ролям по способностям провайдера. Предложить расстановку и **показать до применения**, а не применять молча.
|
||
|
||
## P0-2. Карточка показывает то, что есть на самом деле
|
||
|
||
1. **Роль на карточке берётся только из живой цепочки.** Подстановка из `DEFAULT_SLOT_ROLES` в качестве роли — убрать: статическая таблица годится для человекочитаемого имени, но не для утверждения о назначении.
|
||
2. **Один источник для карточки и окна.** Сейчас список пишет «worker», окно — «manager (primary)».
|
||
3. **Показывать место в цепочке**: основной или запасной номер такой-то. «primary» без указания, в какой роли и на каком месте, ничего не значит.
|
||
|
||
## P0-3. Владелец видит, кто ответит на вызов
|
||
|
||
Главное, ради чего задание.
|
||
|
||
1. **На экране маршрутизации у каждой роли — «сейчас ответит: <аккаунт>»**, вычисленное по текущей цепочке и состоянию аккаунтов.
|
||
2. **Если не ответит никто** — сказать прямо: «цепочка пуста или все аккаунты недоступны, вызов уйдёт мимо хаба в Hermes». Это состояние сейчас и есть, и владелец о нём не знает.
|
||
3. **Роль по умолчанию видна и настраивается.** Сейчас `default_role` не задан, и молча используется `manager`. Показать это в настройках.
|
||
|
||
## P0-4. Видно, прошёл вызов через хаб или мимо
|
||
|
||
1. **Записывать по каждому вызову**: определилась ли роль, каким уровнем, какой профиль выбран, ушёл ли вызов мимо хаба и почему.
|
||
2. **Показывать в журнале событий** и счётчиком на «Обзоре»: сколько вызовов прошло через хаб, сколько мимо.
|
||
3. Это единственный способ ответить на вопрос владельца «работает ли хаб» измерением, а не рассуждением.
|
||
|
||
## P0-5. Обратная связь с Hermes
|
||
|
||
Владелец: «если я поменяю в Гермесе аккаунт, поменяется он в хабе?»
|
||
|
||
Сейчас — нет. Прежде чем делать, **выяснить и записать в отчёт**, что именно Hermes позволяет наблюдать: что хранится в его конфигурации, меняется ли она при выборе модели в интерфейсе, есть ли событие или файл, по которому это видно.
|
||
|
||
Дальше по результату:
|
||
|
||
1. **Если выбор Hermes читается** — показывать его в хабе и отмечать расхождение с цепочкой: «в Hermes выбран X, хаб направил бы на Y».
|
||
2. **Если не читается** — так и написать, а в интерфейсе объяснить владельцу, что хаб управляет маршрутом только когда Hermes не задаёт провайдера явно. Честное объяснение принимается.
|
||
3. **Ничего не записывать в конфигурацию Hermes автоматически.** `_select_model` остаётся ручной командой: молчаливая правка чужой конфигурации — это то, за что уже возвращались работы.
|
||
4. **Не выдумывать механизм**, которого в Hermes нет. Отсутствие способа — результат, он принимается.
|
||
|
||
## P0-6. Аудит вторым проходом
|
||
|
||
1. **Пройти путь целиком на живой машине**: подключить аккаунт, назначить роль, сделать запрос через Hermes и убедиться по журналу, что вызов пошёл через хаб и через **этот** аккаунт. Это единственная настоящая проверка задания.
|
||
2. **Проверить обратное**: убрать аккаунт из цепочки и убедиться, что вызов уходит мимо хаба и это видно в интерфейсе.
|
||
3. **Проверить, что роль на карточке исчезает**, когда аккаунт не назначен, — а не подменяется догадкой.
|
||
4. **Сверить карточку и окно**: подпись роли одинакова.
|
||
5. **Проверить конфигурацию владельца на копии**: старые цепочки на 24 заготовки не должны молча пропасть; предложить перенос, но не выполнять его без подтверждения.
|
||
6. **Побочные изменения** объяснить.
|
||
7. **Пропущенный пункт назвать пропущенным.**
|
||
|
||
---
|
||
|
||
## Ограничения
|
||
|
||
- Конфигурацию владельца молча не переписывать: перенос цепочек — только с подтверждением.
|
||
- В конфигурацию Hermes автоматически не писать.
|
||
- Учётные данные и `~/.hermes/agy_profiles/` не трогать.
|
||
- Определение роли из A35 не переделывать.
|
||
- Вёрстку A48 не ломать.
|
||
- Версию `0.1.1` не поднимать.
|
||
- Правило честности без исключений.
|
||
|
||
## Критерии приёмки
|
||
|
||
1. Ветка в `origin`, `git status` чист.
|
||
2. Подключение аккаунта с выбором роли кладёт его в цепочку этой роли; проверено.
|
||
3. Неназначенный аккаунт показан как неназначенный; догадка из статической таблицы как роль не используется.
|
||
4. Карточка и окно аккаунта показывают одну и ту же роль и место в цепочке.
|
||
5. На маршрутизации видно, какой аккаунт ответит для каждой роли; пустая цепочка названа прямо.
|
||
6. Роль по умолчанию видна и настраивается.
|
||
7. По журналу видно, прошёл вызов через хаб или мимо и почему; есть счётчик.
|
||
8. Пройден живой путь: подключение, назначение, запрос через Hermes, подтверждение по журналу.
|
||
9. Выяснено и записано, что Hermes позволяет наблюдать о своём выборе; сделано либо честно объявлено невозможным.
|
||
10. Конфигурация владельца не изменена без подтверждения.
|
||
11. `ruff check .` чисто; релизный гейт 10/10; тестов не меньше **517**.
|
||
12. Память проекта в AI-Memory обновлена.
|
||
13. Отчёт: `START_HEAD`, `FINAL_HEAD`, `origin/main`, `git status`, `X passed / Y skipped / Z failed`.
|
||
|
||
## Главное
|
||
|
||
Владелец подключил аккаунты, увидел на карточках «manager (primary)» и решил, что настроил маршрутизацию. На деле ни один его аккаунт не входит ни в одну цепочку: все тринадцать ролей ссылаются на заготовки старой конфигурации. При обращении Hermes цепочка отказывает, и вызов уходит мимо хаба.
|
||
|
||
Хаб ведёт себя корректно и не подменяет ответ. Но владелец об этом не знает и считает, что управляет маршрутизацией, а управляет пустотой. Задание должно сделать так, чтобы назначение действительно назначало, а расхождение было видно на экране, а не выяснялось разбором конфигурации.
|
||
|
||
## Порядок сдачи
|
||
Передать точный `FINAL_COMMIT_SHA`.
|