169 lines
16 KiB
Markdown
169 lines
16 KiB
Markdown
# Задание 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-4bis. Установщик должен различать первую установку и переустановку
|
||
|
||
Требование владельца: если программа уже установлена, мастер обязан предлагать **переустановку**, а не повторять сценарий первой установки.
|
||
|
||
Сейчас `SetupEngine.IsInstalled` вычисляется (`HermesHubSetup.cs:86`), но **в интерфейсе не используется ни разу**. Мастер всегда показывает одни и те же экраны — «Добро пожаловать в установку» → «Параметры установки» → «Установить», — независимо от того, стоит Hub на машине или нет.
|
||
|
||
Требуется: при обнаружении установленной копии первый экран показывает её состояние и предлагает действия.
|
||
|
||
```
|
||
Hermes Hub уже установлен
|
||
|
||
Установленная версия : 0.1.0 (19.08.2026)
|
||
Версия в дистрибутиве: 0.1.1
|
||
|
||
[ Переустановить ] [ Удалить ] [ Отмена ]
|
||
```
|
||
|
||
**Переустановка** означает точное зеркало: файлы программы и плагина приводятся в соответствие дистрибутиву, а всё, чего в новой версии нет, — удаляется. Это тот же механизм, что и в P0-4, но вызванный явной кнопкой.
|
||
|
||
**Что удаляется:** устаревшие модули, файлы переименованных и перенесённых пакетов, `__pycache__`, скомпилированные остатки прошлых версий.
|
||
|
||
**Что обязано сохраниться:** `router_profiles.yaml`, каталоги профилей с авторизацией (`agy_profiles`, `codex_profiles`, `opengo_profiles` и аналогичные), `hub_settings.json`, журналы, состояние роутера и телеметрии. Пользовательские данные лежат отдельно от программных файлов — переустановка их не касается.
|
||
|
||
Перед удалением показать, что именно будет удалено, хотя бы количеством файлов. Молча стирать нельзя.
|
||
|
||
**Тихий режим:** добавить флаг `/reinstall` с тем же поведением и кодами возврата, что и у остальных режимов. Существующий `/repair` либо привести к этой же семантике, либо явно развести: сейчас он объявлен, но отличие от обычной установки не документировано.
|
||
|
||
**Тесты:**
|
||
1. В песочнице: развернуть версию A, удалить файл из источника, запустить переустановку версии B — файла в цели нет, остальные соответствуют источнику.
|
||
2. Пользовательские данные переживают переустановку: создать профиль с `auth.json`, `router_profiles.yaml` с правкой и `hub_settings.json`, переустановить, убедиться, что всё на месте и не изменилось.
|
||
3. Обнаружение установленной копии: при наличии `HermesHub.exe` мастер показывает экран переустановки, при отсутствии — обычный сценарий.
|
||
|
||
## 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. При наличии установленной копии мастер предлагает переустановку и удаление, а не сценарий первой установки; версии установленной и новой видны на экране.
|
||
2. Переустановка удаляет устаревшие файлы и сохраняет пользовательские данные; оба условия проверены тестами в песочнице.
|
||
3. Hub запускается на машине без `customtkinter`: либо доустанавливает, либо показывает окно с понятной ошибкой. Проверено на изолированном venv.
|
||
4. Любой сбой до создания окна попадает в `startup.log` с трейсбеком.
|
||
5. Для всех пяти провайдеров мастера `find_free_slot` возвращает существующий профиль или `None`.
|
||
6. Тест профиля с просроченной авторизацией не открывает браузер.
|
||
7. Повторное развёртывание удаляет исчезнувшие файлы.
|
||
8. Самопроверка работает без запущенного Hermes и печатает связный вердикт без секретов.
|
||
9. Прогон **в обоих окружениях**; обе команды и оба результата в отчёте.
|
||
10. `ruff check .` чисто; release gate PASSED на финальном коммите.
|
||
11. Отчёт: `START_HEAD`, `FINAL_HEAD`, `origin/main`, `git status`, точный `X passed / Y skipped / Z failed`.
|
||
|
||
## Порядок сдачи
|
||
Передать точный `FINAL_COMMIT_SHA`.
|