docs(task): A8 and B7 — defects found during the first live run

The owner used the product and the router proved itself: the log shows a real
failover chain codex-orch -> ag-orch-fallback -> opengo-3 driven by actual quota
errors. Everything around that core failed.

Hub stopped launching because customtkinter vanished from the Hermes venv, most
likely wiped by that app's repair-install; pythonw swallows the traceback, so it
looks like the window never opens. The wizard offers five providers while the
router config defines profiles for three, so find_free_slot returns a
non-existent claude-orch. The test action still reaches the agy subprocess, which
opens a browser on expired credentials. The routing "configure" button only
raises a toast about drag-and-drop.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Hermes Team 2026-08-22 20:13:01 +07:00
parent 8cddc9fee2
commit 20078f5ff6
2 changed files with 240 additions and 0 deletions

View file

@ -0,0 +1,135 @@
# Задание A8 (Antigravity): запуск, развёртывание, самопроверка
## Дата поступления
2026-08-22
## База
Проверочный HEAD: **`8cddc9f`**, `origin/main` = `8cddc9f`. `git fetch`, зафиксировать `BASE_SHA`.
## Ветка
`antigravity/deployment-doctor`
---
## Что принято по A7
Проверено исполнением: лизы объединены (`engine.leases is LeaseManager.get()` → `True`, снапшот показывает `{'codex-orch': 1}`), CPU прогревается (первое измерение 25.0 вместо нуля), сеть стала скоростью `net_speed_mbps`. Прогон: headless 195, с UI-зависимостями 249, ruff чисто, гейт PASSED.
---
## Контекст: владелец впервые эксплуатировал Hub
Вчера и сегодня продукт запускали вживую. Результат: **роутер работает, оболочка — нет**. В журнале зафиксирован настоящий каскад отказоустойчивости:
```
20:07:10 Переключение роли 'orchestrator': сбой 'codex-orch' — Insufficient quota
20:07:10 Успешное переключение: резервный профиль 'ag-orch-fallback'
20:07:10 Переключение: сбой 'ag-orch-fallback' — Individual quota reached
20:07:10 Успешное переключение: резервный профиль 'opengo-3'
```
Это первое доказательство, что ядро делает то, ради чего создавалось. Всё остальное, о чём сообщил владелец, — дефекты вокруг ядра.
## P0-1. Hub перестал запускаться: зависимость живёт в чужом окружении
Симптом: «программа запускается и сразу закрывается». Причина воспроизведена:
```
ModuleNotFoundError: No module named 'customtkinter'
```
Лаунчер запускает `pythonw.exe` — без консоли, поэтому трейсбек уходит в никуда и окно просто не появляется. Ещё в 09:41 Hub стартовал нормально; пакет исчез между 09:41 и вечером. Наиболее вероятная причина — кнопка «Repair install» в диалоге ошибки Hermes: она пересоздаёт venv агента и стирает всё доустановленное.
Я вернул пакет вручную, Hub снова импортируется. Но проблема архитектурная: **Hub держит свои зависимости в venv чужого приложения, которое их периодически сносит.**
Требуется:
1. **Самолечение при запуске.** Перед созданием окна проверять импорт `customtkinter`, `PIL`, `psutil`, `yaml`. Если чего-то нет — доустановить в venv Hermes и повторить, либо показать понятное окно с одной кнопкой «Установить зависимости».
2. **Ошибка запуска должна быть видимой.** Сейчас любой сбой до создания окна = тишина. Писать трейсбек в `logs/startup.log` **до** импорта UI и, при падении, показывать нативное окно с текстом ошибки и путём к логу. Файл уже есть, но пишется слишком поздно.
3. Оценить переход на собственный venv Hub рядом с `%LOCALAPPDATA%\Programs\HermesHub\`, чтобы обслуживание Hermes не ломало Hub. Если решение — остаться в venv Hermes, записать это как осознанный выбор с обоснованием.
## P0-2. Мастер предлагает провайдеров, для которых нет профилей
Симптом владельца: «завершить не нажимается, пишет что всё исчерпано, но нет».
Причина: мастер предлагает пять провайдеров, а в конфигурации роутера профили есть только для трёх.
```
antigravity 10 профилей
openai-codex 3 профиля
opencode-go 3 профиля
claude НЕТ НИ ОДНОГО
grok НЕТ НИ ОДНОГО
```
`AutoAssigner.find_free_slot` перебирает `claude-orch`, `claude-worker-1`… не находит их в конфиге, пропускает все и возвращает `candidates[0]` — то есть **несуществующий** `claude-orch`. Дальше мастер сохраняет авторизацию в слот, которого нет, назначение роли отвечает «профиль не найден», завершение не проходит.
Требуется: профили для `claude` и `grok` во встроенной конфигурации и в шаблоне `router_profiles.example.yaml`, по образцу существующих, с ролями в цепочках. И `find_free_slot` не должен возвращать идентификатор, отсутствующий в конфиге, — при отсутствии свободных слотов возвращать `None` с внятной причиной.
**Тест:** для каждого провайдера, который предлагает мастер, `find_free_slot` возвращает либо существующий профиль, либо `None`.
## P0-3. Кнопка «Тест» открывает окно авторизации
Симптом: «при тесте открывается опять окно авторизации и ничего».
В журнале: `Ошибка теста ag-orch-fallback (gemini-3.7-flash): Antigravity error: agy error: authentication failed or timed out`.
Проверка учётных данных в `do_test_profile` выполняется, но затем вызывается адаптер, а он запускает `agy` — и **этот подпроцесс сам открывает браузер**, когда токен просрочен. Требование «тест никогда не запускает OAuth» стоит в проекте с первого аудита и нарушено на уровне подпроцесса.
Требуется: запускать `agy` в неинтерактивном режиме, чтобы при невалидных учётных данных он возвращал ошибку, а не открывал окно. Если у CLI нет такого флага — проверять валидность токена до вызова и не доходить до подпроцесса. Результатом теста в этом случае должно быть «Авторизация истекла, требуется повторный вход», а не молчаливое окно браузера.
**Тест:** просроченные учётные данные дают ошибку авторизации без попытки интерактивного входа.
## P0-4. Установка должна быть зеркалом, а не наслоением
`HermesHubSetup.cs:279` делает `CopyDirectoryRecursive` — только копирует, никогда не удаляет. Развёрнуто у владельца 51 файл от 19 августа против 70 в репозитории, и среди них четыре модуля, удалённых нами как мёртвый код:
```
router/capability/
router/skills/skill_registry.py
router/supervisor/lifecycle_supervisor.py
router/gui_server.py
```
Сами по себе инертны, но однажды уже ввели в заблуждение: тесты подхватывали `runtime.py` из развёрнутой копии.
Требуется точное зеркало источника: удалять файлы, которых нет в источнике, исключать `__pycache__`. **Тест:** развернуть, удалить файл из источника, развернуть снова, убедиться, что в цели его нет.
## P0-5. Команда самопроверки
Расширить `print_diagnostics_cli` до проверки, отвечающей на вопрос «работает ли Hub» **без запуска десктопа Hermes**:
- зависимости в venv с указанием, чего не хватает;
- свежесть развёрнутого плагина против версии приложения (для этого при установке писать манифест: версия, дата, коммит);
- валидность `router_profiles.yaml`, число профилей и ролей;
- по каждому профилю: провайдер, идентичность, авторизация, квота, источник данных;
- реальный тестовый вызов по одному профилю на провайдера;
- итог одной строкой: готов / частично / не готов, с причинами.
Секреты в выводе маскируются.
## P1-6. Остаток по YAML
Внутренние комментарии `router_profiles.yaml` теряются (7 → 2). Либо полный round-trip, либо статус «частично» с перечнем теряемого — в контракте и отчёте.
---
## Ограничения
- Граница прежняя: зона Codex (`router/ui/**`, `hermes_hub_app.py`, `tests/test_ui_*.py`) — не трогать. Исключение по P0-1: проверка зависимостей до создания окна лежит в точке входа; согласовать минимальную правку, остальное — данными.
- Тег `v0.1.1` не создавать.
## Критерии приёмки
1. Hub запускается на машине без `customtkinter`: либо доустанавливает, либо показывает окно с понятной ошибкой. Проверено на изолированном venv.
2. Любой сбой до создания окна попадает в `startup.log` с трейсбеком.
3. Для всех пяти провайдеров мастера `find_free_slot` возвращает существующий профиль или `None`.
4. Тест профиля с просроченной авторизацией не открывает браузер.
5. Повторное развёртывание удаляет исчезнувшие файлы.
6. Самопроверка работает без запущенного Hermes и печатает связный вердикт без секретов.
7. Прогон **в обоих окружениях**; обе команды и оба результата в отчёте.
8. `ruff check .` чисто; release gate PASSED на финальном коммите.
9. Отчёт: `START_HEAD`, `FINAL_HEAD`, `origin/main`, `git status`, точный `X passed / Y skipped / Z failed`.
## Порядок сдачи
Передать точный `FINAL_COMMIT_SHA`.

View file

@ -0,0 +1,105 @@
# Задание B7 (Codex): дефекты, найденные при живой эксплуатации
## Дата поступления
2026-08-22
## База
Проверочный HEAD: **`8cddc9f`**, `origin/main` = `8cddc9f`. `git fetch`, зафиксировать `BASE_SHA`.
## Ветка
`codex/usability-fixes`
## Отношение к B6
Задание **B6 (граф маршрутизации) остаётся в силе**, но это — приоритетнее. Владелец впервые прошёл сценарий вживую, и половина действий не сработала. Сначала чинится то, что он не смог сделать, потом граф.
---
## Что сообщил владелец, дословно
1. «завершить не нажимается. пишет что все исчерпано, но нет»
2. «при тесте открывается опять окно авторизации и ничего»
3. «появляется код, куда его вставлять, не понятно»
4. «назначить роль не получается, ничего не видно»
5. «в маршрутизации при нажатии на кнопку настроить ничего не происходит»
Каждое проверено по коду. Ниже — что относится к вам; пункты 1 (частично) и 2 уходят в backend отдельным заданием A8.
## P0-1. «Настроить» в маршрутизации не делает ничего
`hermes_hub_app.py:632`:
```python
elif action == "edit_route":
self._show_toast("Редактор цепочки использует кнопки и селекторы; drag-and-drop отключён.")
```
Кнопка показывает сообщение про отключённый drag-and-drop — и всё. С точки зрения пользователя нажатие не делает ничего, а текст объясняет то, чего он не спрашивал.
Требуется настоящий редактор цепочки для роли: список профилей в порядке приоритета, изменение порядка, добавление и удаление профиля из цепочки, сохранение через `AutoAssigner`. Кнопочный и селекторный, без drag-and-drop — это оговорено и допустимо. Но он должен существовать.
## P0-2. Результат действия не виден
«назначить роль не получается, ничего не видно».
Проверил: модальное окно назначения роли **открывается** и содержит семь вариантов — здесь дефекта нет. Проблема в обратной связи: результат уходит в `_show_toast`, то есть в строку состояния внизу окна, где его легко не заметить. Если профиль не найден (а при выборе Claude или Grok он сейчас действительно не найден — см. A8), пользователь видит ровно ничего.
Требуется:
- результат действия показывать заметно: в самой модалке до закрытия либо всплывающим уведомлением рядом с местом действия;
- **при ошибке модалку не закрывать** — сейчас `modal.destroy()` вызывается до показа результата, и человек остаётся без контекста;
- то же для остальных действий карточки: «Тест», «Основной», «Оркестратор».
## P0-3. Код авторизации: непонятно, что с ним делать
«появляется код, куда его вставлять, не понятно».
Сейчас в мастере для Codex и Grok показывается поле со ссылкой, отдельная метка с кодом и статус «Ожидание подтверждения кода XXX в браузере…». Ни одной фразы о том, что нужно сделать.
Требуется явная пронумерованная последовательность прямо в шаге:
```
1. Откройте ссылку — [кнопка «Открыть в браузере»] [копировать]
2. Введите на странице код: ABCD-1234 [копировать]
3. Подтвердите доступ — окно закроется само
```
Код — крупно, моноширинным, с кнопкой копирования. Статус ожидания — ниже, отдельной строкой. Пользователь не должен догадываться о порядке действий.
## P0-4. Мастер: честное поведение при отсутствии свободного слота
Backend вернёт `None`, когда свободных слотов действительно нет (A8 это чинит). Сейчас мастер подставляет `f"{provider[:3]}-spare-1"` — придуманный идентификатор, который может не существовать:
```python
AutoAssigner.find_free_slot(self.selected_provider) or f"{self.selected_provider[:3]}-spare-1"
```
Требуется: если слот не найден — не выдумывать, а показать понятное объяснение («все слоты этого провайдера заняты, освободите один или удалите неиспользуемый аккаунт») и не давать пройти дальше. Кнопка «Завершить» должна быть либо активной и работающей, либо отключённой с подсказкой почему — но не «нажимается и ничего не происходит».
## P1-5. Первый запуск должен вести пользователя
Остаётся из B6: на пустой конфигурации «Обзор» показывает «Критическое состояние» и пять `Н/Д` без единой подсказки. Первый экран обязан объяснять следующий шаг и вести к нему.
---
## Ограничения
- Граница: ваша зона — `src/antigravity_provider/router/ui/**`, `hermes_hub_app.py`, `tests/test_ui_*.py`.
- Не выдумывать идентификаторы, значения и метрики. Нет данных — «Н/Д» либо блок отсутствует.
- Три темы сохранить.
- Мастер не ломать: шесть рабочих потоков подключения и трёхэлементная распаковка `start_profile_oauth`.
- Тег `v0.1.1` не создавать.
## Критерии приёмки
1. Ни один файл чужой зоны не изменён.
2. «Настроить» открывает работающий редактор цепочки; изменение сохраняется и видно после перезапуска.
3. Ошибка любого действия видна пользователю в месте действия; модалка при ошибке остаётся открытой.
4. Шаг с кодом устройства содержит пронумерованную инструкцию и кнопки копирования для ссылки и кода.
5. При отсутствии свободного слота мастер объясняет причину и не подставляет выдуманный идентификатор.
6. Пустое состояние «Обзора» ведёт к подключению аккаунта.
7. Тесты на каждый пункт: редактор цепочки сохраняет порядок; ошибка действия отображается; мастер без свободных слотов не завершается молча.
8. Прогон **в обоих окружениях** — без UI-зависимостей и с `customtkinter`/`pillow`/`psutil`; обе команды и оба результата в отчёте.
9. `ruff check .` чисто; release gate не ухудшен.
10. **Скриншоты живого сценария**: подключение аккаунта, назначение роли, редактор цепочки — с реальными данными.
## Порядок сдачи
Передать точный `FINAL_COMMIT_SHA`.