hermes-hub/agents/inbox/2026-08-24-A23-recovery-and-model-validation.md
Hermes Team 9c6a4e8f6d docs(task): A23 — самовосстановление профилей и честная проверка моделей
Написано под схему владельца: Flash реализует, Pro проводит аудит.
Пункт P0-4 — чек-лист для второго прохода, составлен из дефектов,
которые уже проходили мимо первого.

P0-1: mark_auth_required ставит состояние без срока истечения, а
маршрутизация пропускает нездоровый профиль — успеха не случится,
отметка не снимется никогда. Подтверждено: после починки авторизации в
A22 все шесть профилей Antigravity остались помечены, ревьюер снимал
отметки вручную.

P0-2: do_set_model пропускает проверку целиком при пустом кэше моделей.
Проверено — выдуманная модель записалась в конфигурацию владельца.
Отсутствие данных трактуется как разрешение.

P0-3: ручного обновления списка моделей нет с A18.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-24 01:35:52 +07:00

158 lines
13 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.

# Задание A23: самовосстановление профилей и честная проверка моделей
## Дата поступления
2026-08-24
## База
Проверочный HEAD на момент выдачи: **`45fd01a`**.
## Ветка
`antigravity/recovery-and-validation`
## Порядок исполнения
Задание выполняется в два прохода, как прошлый раз:
1. **Flash** реализует.
2. **Pro** проводит аудит и правит найденное.
Схема себя оправдала: в A22 второй проход поймал ложную инструкцию в веб-клиенте. Пункт **P0-4** написан специально для аудитора — он же приёмка.
---
## Порядок работы с git
```
cd <каталог репозитория>; git fetch origin --prune; git status
git checkout main; git pull --ff-only origin main
git checkout -b antigravity/recovery-and-validation
git commit -m "..." <- сначала коммит
git push -u origin antigravity/recovery-and-validation
```
В `main` напрямую не пушить: работа A20 ушла туда минуя ревью. В конце — push и проверка `git log --oneline -1 origin/antigravity/recovery-and-validation`, `git status` чистый.
---
## Что принято по A22
Задача решена, все три доказательства получены проверкой ревьюера:
```
1. agy models через профиль -> 14 моделей, включая gemini-3.1-pro-high
2. adapter.invoke(ag-w1) -> модель ответила «ОК»
3. route_request -> переключение codex-worker-1 -> ag-w1, ответ получен
```
Реализация аккуратная: видимая консоль на Windows через `CREATE_NEW_CONSOLE`, терминалы на Linux, `HOMEDRIVE` выставляется корректно. Пересохранение профиля не трогает глобальный `~/.gemini` — проверено.
Исправлено ревьюером при слиянии: инструкция в веб-клиенте вела на **несуществующий** `launcher/main.py`; тест проверял дословную формулировку и падал при её правке.
## Отменённое утверждение — прочитать обязательно
`agents/inbox/2026-08-24-CORRECTION-gemini-model-names.md`.
Ревьюер четырежды написал, что `gemini-3.7-flash` «у провайдера не существует». **Это неверно.** Модель настоящая, уровень усилия у неё — отдельный параметр. Дефект был в коде: `_model_supported_efforts` вызывала обнаружение без профиля, карта усилий оставалась пустой, подстановка уровня по умолчанию не срабатывала. Исправлено, `gemini-3.7-flash` работает без указания усилия.
**Конфигурацию владельца по этому поводу не править.**
---
## P0-1. Отказ авторизации — состояние без выхода
Проверено исполнением, дефект подтверждён.
`health_tracker.mark_auth_required` (строка 376) ставит `overall_state = AUTH_REQUIRED` и **не задаёт ни срока истечения, ни `reset_at`**:
```python
def mark_auth_required(self, profile_id, reason=None):
record.overall_state = AUTH_REQUIRED
record.last_error = reason
self._save_state()
```
Сравните с квотой: у неё `reset_at` есть, и состояние само рассасывается.
Дальше замыкается круг: маршрутизация **пропускает** нездоровый профиль (`skipped_unhealthy`), значит успешного вызова по нему не случится, значит `mark_success` не вызовется, значит состояние не снимется. **Никогда.**
Это не теория. После того как A22 починил авторизацию, все шесть профилей Antigravity остались помечены и пропускались — ревьюер вручную вызывал `clear_cooldown`, иначе третье доказательство не прошло бы. Владелец такой команды не знает и знать не должен.
**Требуется путь наружу.** Варианты на выбор, обосновать в отчёте:
- срок истечения у `AUTH_REQUIRED`, как у квоты;
- периодическая перепроверка помеченных профилей — редкая, чтобы не жечь квоту;
- снятие отметки при событии, которое достоверно означает починку: успешный вход через мастер, обновление учётных данных профиля.
Последнее выглядит самым честным: авторизацию починили — отметка снимается сразу, а не по таймеру.
**Тест обязателен:** профиль, помеченный `AUTH_REQUIRED`, после починки учётных данных снова участвует в маршрутизации **без ручного вмешательства**.
Сейчас на машине владельца: 2 «Работает», 6 «Не проверялся», 11 «Аккаунт не добавлен», 3 «Отключён».
## P0-2. Проверка модели молча отключается
`do_set_model` валидирует модель по обнаруженному списку — логика написана верно:
```python
if discovered is not None:
if model not in discovered and model not in canonical and model not in canonical_short:
return False, f"Модель '{model}' отсутствует в списке..."
```
Но при пустом кэше `discovered` равен `None`, проверка **пропускается целиком**, и проходит что угодно. Ревьюер убедился: `do_set_model('ag-w1','такой-модели-нет')` вернул успех и записал это в конфигурацию владельца. Запись убрана вручную.
Это ровно тот класс дефекта, с которым проект борется с первого аудита: **отсутствие данных трактуется как разрешение**.
Требуется:
1. При пустом кэше — **не молчать**. Либо отказать с внятной причиной, либо сохранить с явной пометкой «модель не подтверждена» и показать это в интерфейсе. Молчаливое согласие недопустимо.
2. **Прогревать кэш** перед проверкой, если его нет. Кэш на диске уже реализован (`models_cache.json`), обнаружение работает — не хватает только вызова в нужный момент.
3. Учесть при сравнении, что в обнаруженном списке идентификаторы склеенные (`gemini-3.7-flash-high`), а в конфигурации может стоять базовое имя (`gemini-3.7-flash`). **Базовое имя — валидно.** Не отвергать его.
**Тесты:** несуществующая модель отклоняется при наполненном кэше; при пустом кэше поведение осознанное и проверяемое; базовое имя без суффикса усилия принимается.
## P0-3. Ручное обновление списка моделей
Действия обновления моделей нет ни среди действий, ни в интерфейсе — пункт остаётся невыполненным с A18.
Добавить действие обновления и кнопку рядом с выбором модели. Обнаружение ходит в сеть и подпроцесс: выполнять в фоне, интерфейс не блокировать, показывать ход.
Замерено: `agy models` отвечает за десятки секунд, а иногда висит дольше двух минут. Таймаут обязателен, **прежний кэш при таймауте не затирать**.
## P0-4. Аудит вторым проходом
Это пункт для проверяющего. Схема Flash → Pro уже поймала один дефект в A22; ниже то, на что смотреть в первую очередь.
1. **Запустить то, что изменено.** В A15 вынесли действия и уничтожили класс приложения: модуль импортировался, тесты проходили, а `launch_hub()` упал бы с `NameError`. Импорт ничего не доказывает — Python примет и недостижимый код.
2. **Проверить тексты, которые видит владелец.** В A22 инструкция вела на несуществующий файл. Каждый путь и каждая команда в сообщениях интерфейса должны существовать.
3. **Проверить утверждения отчёта, а не поверить им.** По A8 отчёт назвал сделанными четыре вещи, из которых ни одна не работала.
4. **Проверить, что тесты проверяют суть, а не формулировку.** Два теста уже падали от переписанного текста при верном поведении.
5. **Пропущенный пункт назвать пропущенным.** Дважды главные пункты задания оставались нетронутыми, и выяснялось это только при проверке файлов.
---
## Ограничения
- Ваши файлы: `health_tracker.py`, `router_engine.py`, `action_handler.py`, `model_discovery*`, `unified_health.py`, `router/ui/**`, `router/web/**`, соответствующие тесты.
- Не откатывать: правку усилий из `45fd01a`, родной вход `agy` из A22, прогрев квот в веб-сервере.
- Никаких статусов и значений без основания. Нет данных — сказать об этом, а не пропустить проверку.
- Конфигурацию владельца литералами не править.
- Тег `v0.1.1` не создавать.
## Критерии приёмки
1. Ветка в `origin`, в `main` напрямую не пушилось, `git status` чист.
2. Профиль с `AUTH_REQUIRED` возвращается в маршрутизацию после починки учётных данных **без ручного вмешательства**; проверено тестом.
3. Выбранный способ выхода из состояния обоснован в отчёте.
4. `do_set_model` не пропускает непроверенную модель молча; поведение при пустом кэше осознанное; базовое имя без суффикса усилия принимается. Три теста.
5. Есть ручное обновление списка моделей; интерфейс не блокируется; таймаут не затирает кэш.
6. Второй проход выполнен, в отчёте перечислено, что проверялось по пунктам P0-4 и что найдено.
7. `ruff check .` чисто; релизный гейт не ухудшен.
8. Отчёт: `START_HEAD`, `FINAL_HEAD`, `origin/main`, `git status`, `X passed / Y skipped / Z failed`. На `main` сейчас 359 passed, 2 skipped.
## Главное
Antigravity наконец работает. Осталось, чтобы он не выпадал навсегда после единственного сбоя авторизации и чтобы выбор модели не соглашался на всё подряд, когда сравнить не с чем.
## Порядок сдачи
Передать точный `FINAL_COMMIT_SHA`. Сдано только после появления коммита в `origin`.