A17 (Pro): «Работает» — ветка else в определении здоровья, она означает «мы не знаем о проблемах», а подана как утверждение. Отказы попадают в статус только после боевого сбоя, поэтому непроверенный профиль автоматически зелёный. Плюс «Проверить подключение» не вызывает модель вовсе — Grok её проходит и не работает. A18 (Flash): действия смены модели не существует ни среди семнадцати, ни в клиенте; десктоп это умеет, но логика заперта в методе интерфейса. И выбирать не из чего: кэш моделей пуст по всем провайдерам, потому что agy models нестабильна — в одном прогоне 40 секунд, в следующем висит больше двух минут. A19: два установщика. Windows — ярлык, открывающий веб окном приложения через --app без адресной строки (проверено на машине владельца: Edge и Chrome есть, окно открывается). Linux — скрипт установки, .desktop и удаление с сохранением данных, плюс честная подсказка про проброс порта при пустом DISPLAY. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
137 lines
13 KiB
Markdown
137 lines
13 KiB
Markdown
# Задание A17 (Antigravity Pro): честный статус аккаунта и настоящая проверка
|
||
|
||
## Дата поступления
|
||
2026-08-23
|
||
|
||
## База
|
||
Проверочный HEAD на момент выдачи: **`fb23bff`**.
|
||
|
||
## Ветка
|
||
`antigravity/honest-status`
|
||
|
||
---
|
||
|
||
## Порядок работы с git
|
||
|
||
```
|
||
cd <каталог репозитория>; git fetch origin --prune; git status
|
||
git checkout main; git pull --ff-only origin main
|
||
git checkout -b antigravity/honest-status
|
||
```
|
||
|
||
**Сначала коммит, потом push.** В прошлый раз работа A15 была выполнена целиком, но осталась незакоммиченной в рабочем каталоге — git в вашем окружении был недоступен, и ветка в `origin` оказалась пустой. Её нашли случайно.
|
||
|
||
Если git снова недоступен — **скажите об этом первой строкой отчёта**, а не в предупреждении под ним. Это меняет весь порядок приёмки.
|
||
|
||
В конце:
|
||
|
||
```
|
||
git status <- дерево чистое
|
||
git log --oneline -1 origin/antigravity/honest-status <- ваш коммит
|
||
```
|
||
|
||
---
|
||
|
||
## Что принято по A15
|
||
|
||
Веб-API и вынесение действий в общий `ActionExecutor` — правильная архитектура, и она работает. Порт путей на Linux почти закрыт: осталось одно место, `hermes_hub_app.py:37`.
|
||
|
||
Но в сданном виде **не работало ни то, ни другое**, и это важнее похвалы:
|
||
|
||
1. **Десктоп был уничтожен.** При выносе действий пропало объявление `class HermesHubApp` вместе с 13 методами каркаса — `__init__`, `_build_layout`, `_create_view`, `_show_view`, `_refresh_data`. Оставшиеся 14 методов оказались вложены **внутрь функции `_load_saved_theme` после её `return`** — синтаксически валидный недостижимый код. Поэтому модуль импортировался, и дефект выглядел безобидным, а `launch_hub()` упал бы с `NameError`.
|
||
|
||
2. **Веб-API падал с 500 на обоих значимых эндпоинтах**: `get_auth_token` и `run_server` читали `config.hub`, которого у `RouterConfig` нет. Работал только `/api/health` — у него нет проверки авторизации, из-за чего сервер и выглядел поднявшимся.
|
||
|
||
3. **`do_save_settings` при переносе потеряла** атомарную запись через `os.replace`, `ensure_ascii=False` и вызов `set_refresh_interval` — интервал обновления квот из настроек перестал применяться.
|
||
|
||
Всё восстановлено ревьюером. Урок один и он общий для проекта: **крупное перемещение кода проверяется запуском того, что перемещали.** Импорт модуля ничего не доказывает — Python примет и недостижимый код.
|
||
|
||
---
|
||
|
||
## Главное: «Работает» — вымышленный статус
|
||
|
||
Владелец сообщил про два аккаунта: «стоит опенкод аккаунт, который не подключён… аккаунт не работает» и «и грок не работает». Оба показаны зелёным **«Работает»**.
|
||
|
||
По одному из них причина найдена и уже исправлена: адаптер OpenCode не читал сохранённый ключ (`fb23bff`). Но осталась причина, общая для обоих и более глубокая.
|
||
|
||
`unified_health.py:429` — `STATUS_HEALTHY` с подписью «Работает» назначается в **ветке `else`**, когда ни одно условие отказа не совпало:
|
||
|
||
```
|
||
1. не enabled -> Отключён
|
||
2. нет учётных данных -> Аккаунт не добавлен / Требуется вход / Холодный резерв
|
||
3. cooldown или квота -> Квота исчерпана
|
||
4. rate limit -> Лимит запросов
|
||
5. precord.overall_state -> Ошибка
|
||
6. иначе -> «Работает» <- сюда попадает всё непроверенное
|
||
```
|
||
|
||
Состояния отказа берутся из `precord` — записей health tracker, которые появляются **только после настоящего сбоя в бою**. Профиль, который ни разу не вызывали, автоматически получает зелёное «Работает».
|
||
|
||
**То есть надпись означает «мы не знаем о проблемах», а подана как утверждение, что аккаунт работает.** Это тот же класс дефекта, из-за которого в первом аудите проекта были удалены выдуманные проценты квот: отсутствие данных выдаётся за положительный результат.
|
||
|
||
### Что требуется
|
||
|
||
1. **Различать «проверено и работает» и «не проверялось».** Профиль без подтверждения не должен выглядеть так же, как подтверждённо рабочий. Формулировку выберите сами, но она обязана быть честной: «Не проверялся» — правда, «Работает» — нет.
|
||
|
||
2. **Хранить результат и время последней успешной проверки** рядом с профилем и отдавать их в снапшоте. Интерфейсу нужно показать «проверено 12:05», а не только цвет.
|
||
|
||
3. Состояние отказа по-прежнему приходит из боевых сбоев — это правильно и ломать не нужно.
|
||
|
||
**Тест:** профиль со свежесохранёнными учётными данными и без единой проверки не получает статус, утверждающий работоспособность.
|
||
|
||
## P0-2. «Проверить подключение» ничего не проверяет
|
||
|
||
`do_test_profile` проверяет наличие учётных данных и доступность локального runtime — и **никогда не вызывает модель**. Поэтому Grok эту проверку проходит и всё равно не работает.
|
||
|
||
Так сложилось не случайно: требование «тест не должен запускать OAuth и открывать браузер» стоит в проекте с первого аудита, и ради него вызов модели убрали целиком. Требование верное, но реализация выплеснула вместе с ним смысл проверки.
|
||
|
||
**Требуется настоящая проверка**, не нарушающая прежнего запрета:
|
||
|
||
- минимальный реальный вызов к провайдеру — самый дешёвый из возможных, с жёстким таймаутом;
|
||
- **интерактивный вход не запускается ни при каких условиях**: просроченные учётные данные дают ошибку «Авторизация истекла», а не окно браузера. Для Antigravity это уже обеспечено флагами `BROWSER=none` и `CI=1` в окружении подпроцесса и проверкой срока токена до вызова;
|
||
- результат сохраняется как последняя проверка (P0-1) с временем;
|
||
- по каждому провайдеру в отчёте: что именно вызывается и сколько это стоит владельцу. Если у провайдера нет дешёвого способа — **сказать об этом прямо**, а не имитировать проверку.
|
||
|
||
Осторожно с ценой: у владельца шесть аккаунтов Antigravity, три Codex, три OpenCode. Проверка всех подряд не должна съедать квоту. Массовую проверку делать по явной команде, а не автоматически при каждом обновлении.
|
||
|
||
**Тест:** просроченные учётные данные дают ошибку авторизации без попытки интерактивного входа; успешная проверка фиксируется с временем.
|
||
|
||
## P1-3. Остаток порта на Linux
|
||
|
||
`hermes_hub_app.py:37` по-прежнему читает `LOCALAPPDATA` напрямую:
|
||
|
||
```python
|
||
_LOCAL = Path(os.environ.get("LOCALAPPDATA", ""))
|
||
```
|
||
|
||
На Linux это даст пустой путь. Провести через `paths.get_hermes_home()`, как остальные семь мест.
|
||
|
||
В `agy_subprocess.py` два оставшихся упоминания трогать не нужно: строка 41 — комментарий, строка 414 — список переменных окружения, пробрасываемых подпроцессу на Windows, и он там уместен.
|
||
|
||
---
|
||
|
||
## Ограничения
|
||
|
||
- Параллельно идут **A18** (смена модели) и **A19** (установщики). Ваши файлы: `unified_health.py`, `health_tracker.py`, `action_handler.py`, `adapters/**`, `state_store.py`, `hermes_hub_app.py`, `docs/UI_STATE_CONTRACT.md`, `docs/web-api/CONTRACT.md`. **Не ваши:** `router/web/**`, `router/ui/**`, `model_discovery*`, `installer/**`, `launcher/**`.
|
||
- Меняете снапшот — правьте `docs/web-api/CONTRACT.md` и скажите об этом в отчёте: против него пишется клиент.
|
||
- Никаких статусов без основания. Нет проверки — так и написать.
|
||
- Тег `v0.1.1` не создавать.
|
||
|
||
## Критерии приёмки
|
||
|
||
1. Ветка в `origin`, `git status` чист. Если git был недоступен — сказано первой строкой отчёта.
|
||
2. Непроверенный профиль не показывается как работающий; проверено тестом.
|
||
3. Результат и время последней проверки хранятся и приходят в снапшоте; контракт обновлён.
|
||
4. «Проверить подключение» делает реальный вызов с таймаутом и **не запускает интерактивный вход ни при каких условиях**; проверено тестом на просроченных данных.
|
||
5. В отчёте по каждому провайдеру сказано, что вызывается при проверке и во что это обходится владельцу.
|
||
6. Массовая проверка не запускается автоматически при обновлении данных.
|
||
7. `hermes_hub_app.py:37` больше не читает `LOCALAPPDATA` напрямую.
|
||
8. Прогон в обоих окружениях; `ruff check .` чисто; релизный гейт не ухудшен (сейчас 7/7).
|
||
9. Отчёт: `START_HEAD`, `FINAL_HEAD`, `origin/main`, `git status`, точный `X passed / Y skipped / Z failed`. На `main` сейчас 331 passed, 2 skipped.
|
||
|
||
## Главное
|
||
|
||
Зелёная галочка на нерабочем аккаунте — худший вид лжи в этом продукте: она не просто бесполезна, она уводит от поиска настоящей причины. Владелец потратил на это два обращения. Лучше честное «не проверялся», чем уверенное «работает».
|
||
|
||
## Порядок сдачи
|
||
Передать точный `FINAL_COMMIT_SHA`. Сдано только после появления коммита в `origin`.
|